I have a like button in an action sheet:
likeAction = UIAlertAction(title: "Like this post", style: UIAlertActionStyle.Default, handler: { (action) -> Void in
println("like pressed")
})
Once pressed I'd like to update the title to "Unlike this post"
Title is readonly and there isn't a SetTitle() function on UIAlertActions.
Ideas much appreciated.
This works for me.
self.alertAction.setValue("New Title", forKeyPath: "title")
Hope this helps.
This was answered by Lyndsey Scott in a comment on the original post.
Set a bool variable to indicate whether the post is "liked" then set the alert title conditionally.
Related
I can add textField to my alert as shown below using alert.addTextField()
let alert = UIAlertController(title: "Title", message: "Subtitle", preferredStyle: UIAlertController.Style.alert)
alert.addTextField()
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: { _ in
print(alert.textFields?[0].text ?? "")
}))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
However, if I have a custom textField, like CurrencyField as shared in https://stackoverflow.com/a/29783546/3286489, how could I add that to my alert (instead of just having a generic TextField)?
The Holy Path
The Apple-given API doesn't allow for you to use a custom subclass of UITextField. It does, however, allow you to customize the UITextField:
alert.addTextField { textField in
// customize text field
}
You'll have to try and port the functionality of your CurrencyView over, having only access to the APIs available by default on UITextField.
The Unholy Path
⚠️ Disclaimer: This is a Bad Idea. Using vendor APIs in ways they weren't intended to makes your code and application less stable.
Now that we've got that out of the way: you could also add a view directly to the default UITextField. You'd have to disable interaction/editing on the original UITextField, though, and make sure it only goes to your custom text field.
If you really wanted to go to the dark side, you could swizzle UITextField and force your CurrencyView to be initialized, but, once again, this is a Bad Idea.
Dear Stackoverflowers,
Can any experts on Swift/UIKit see what I'm doing wrong in the following screenshot?
Xcode can't seem to find the convenience initializer for a UIAlertAction based on a title, style and handler, and I don't know of any other way to initialize a UIAlertAction. In practice, the handler won't be nil.
Thank you in advance,
Jamie
You are trying to pass Int to style param which is against swift rule. Try this out:
let saveAction = UIAlertAction(title: "Save", style: UIAlertActionStyle.Default, handler: nil)
I am using Swift 2. This question relates to iOS9.
In brief:-
If the file rename button is clicked and the file name is still invalid then do I need to present the alert again or is there a smarter way of handling this?
In full:-
Saving a file imported from iCloud, I am presenting a UIAlertController called alertController if a file with the same name (.lastPathComponent) already exists in /Documents.
The UIAlertController has two actions titled Cancel and Rename and a .addTextFieldWithConfigurationHandler. If the file name already exists then the user is prompted to either cancel or rename.
This question relates to validating the new name and re-presenting (or not dismissing) the UIAlertController until the file name is valid:
If the user clicks the rename button and the file name is still the same, or is the same as another file which already exists, then I want the UIAlertController to be shown again. Or better still it would not be dismissed until the file name is valid.
The way I have done this (the only way I have figured out) is by adding a func named presentAlertController which presents the UIAlertController. This func is called from the rename handler when the button is clicked if the file name already exists (or has not been changed). (The same as simply presenting the UIAlertController again from the action).
My question:-
My code does what I want but can anyone suggest a neater and less clumsy way of achieving this outcome - without having to present the UIAlertController again?
Here is the relevant code. Please note that this whole section is within the completion handler of another function - hence the need for various references to self and why the UIAlertController code is within a dispatch_async(dispatch_get_main_queue()) (has to be called from the main queue).
//...
if checkFileExists(saveURL) { // saveURL: NSURL
dispatch_async(dispatch_get_main_queue()) {
let message = "File named `\(saveURL.lastPathComponent!)` already exists. Please import using a new name or else cancel."
let alertController = UIAlertController(title: "", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addTextFieldWithConfigurationHandler { textField -> Void in
textField.text = saveURL.lastPathComponent! // it presents a text field containing the file name which needs to be changed
}
func presentAlertController() {
self.presentViewController(alertController, animated: true) {}
}
alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
alertController.addAction(UIAlertAction(title: "Rename", style: .Default) { action -> Void in
saveURL = saveURL.URLByDeletingLastPathComponent!.URLByAppendingPathComponent((alertController.textFields?.first!.text)!)
if checkFileExists(saveURL) {
presentAlertController() // currently it is calling a function to present the UIAlertController again if the file still exists when the button is clicked
} else {
saveXML(saveURL, dataObject: self.myThing)
self.fileTableView.reloadData()
}
})
presentAlertController() // this will be the first time that this called
}
}
//...
So you can add a target to your text field that will be called when the text field is edited, and in that function you can check if the user has typed a valid name. The problem you'll have with that is you'll need access to alertController so you can disable the "Rename" button. You can accomplish that by making a property at the top of your view controller like so:
var alertController: UIAlertController!
then revise the code your posted like so:
//...
if checkFileExists(saveURL) { // saveURL: NSURL
dispatch_async(dispatch_get_main_queue()) {
let message = "File named `\(saveURL.lastPathComponent!)` already exists. Please import using a new name or else cancel."
//just assigning here, not redeclaring (get rid of "let")
alertController = UIAlertController(title: "", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addTextFieldWithConfigurationHandler { textField -> Void in
textField.text = saveURL.lastPathComponent! // it presents a text field containing the file name which needs to be changed
//Add the target here, calls on checkString
textField.addTarget(self, action: "checkString:", forControlEvents: UIControlEvents.EditingChanged)
}
func presentAlertController() {
self.presentViewController(alertController, animated: true) {}
}
alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
alertController.addAction(UIAlertAction(title: "Rename", style: .Default) { action -> Void in
saveURL = saveURL.URLByDeletingLastPathComponent!.URLByAppendingPathComponent((alertController.textFields?.first!.text)!)
if checkFileExists(saveURL) {
presentAlertController() // currently it is calling a function to present the UIAlertController again if the file still exists when the button is clicked
} else {
saveXML(saveURL, dataObject: self.myThing)
self.fileTableView.reloadData()
}
})
//Disable the "Rename" button to start, remove this line if you don't want that to happen
(alertController.actions as! [UIAlertAction])[1].enabled = false
presentAlertController() // this will be the first time that this called
}
}
//...
Then you'll have to make a checkString function (obviously you can rename this however you like as long as you also change the selector on the line where you add the target to the text field). Here's a little bit of code to give you an idea, but you'll have to write your own stuff here.
func checkString(sender: UITextField) {
//Pretty safe assumption you don't want an empty string as a name
if sender.text == "" {
(alertController.actions as! [UIAlertAction])[1].enabled = false
}
//As soon as the user types something valid, the "Rename" button gets enabled
else {
(alertController.actions as! [UIAlertAction])[1].enabled = true
}
}
This code has been tested, but not very rigorously, so comment if you have problems or if it works.
I have an alertController with a button in it. Here's how I created the button:
var myButton : UIAlertAction!
// Later...
self.myButton = UIAlertAction(title: "myButton", style: .Default, handler: {action -> Void in
self.myButton.title = "✔︎\(myButton)"
})
After the button get's selected, I want to add a "✔︎" at the beginning of the title. (As you can see in the last line of code above.) When I try doing that, I get the following error:
Cannot assign to the result of this expression
What can I do to fix that? If myButtons title is read-only, then is there a valid workaround to it?
The error is not about the "✔︎". It's because of myButton. It means nothing to assign a UIAlertAction to String. That's how you get the error.
I was under the impression that if the normal action is a destructive action and the other is a cancel action in their UIAlertController that the destructive one should be on the left and the cancel should be on the right.
If the normal action is not destructive, then the normal action should be on the right and the cancel should be on the left.
That said, I have the following:
var confirmLeaveAlert = UIAlertController(title: "Leave", message: "Are you sure you want to leave?", preferredStyle: .Alert)
let leaveAction = UIAlertAction(title: "Leave", style: .Destructive, handler: {
(alert: UIAlertAction!) in
//Handle leave
}
)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
confirmLeaveAlert.addAction(leaveAction)
confirmLeaveAlert.addAction(cancelAction)
self.presentViewController(confirmLeaveAlert, animated: true, completion: nil)
I was under the impression that if I add the leaveAction first, then the cancelAction that the leaveAction would be the button on the left. This was not the case. I tried adding the buttons in the opposite order as well and it also resulted in the buttons being added in the same order.
Am I wrong? Is there no way to achieve this?
My solution to this was to use the .Default style instead of .Cancel for the cancelAction.
Since iOS9 there is a preferredAction property on UIAlertController. It places action on right side. From docs:
When you specify a preferred action, the alert controller highlights the text of that action to give it emphasis. (If the alert also contains a cancel button, the preferred action receives the highlighting instead of the cancel button.)
The action object you assign to this property must have already been added to the alert controller’s list of actions. Assigning an object to this property before adding it with the addAction: method is a programmer error.