Generally, to grab the text of a UITextField every time it is changed, you just drag an IBOutlet from the Storyboard file, and select the "editing has changed" option.
However, I want to grab the text of a UITextField in a UIAlertController, so the text field is created programmatically, not via storyboard.
How do I programmatically create a function that runs every time a textfield (in the Alert window) has changed?
What I need:
func textHasChanged(textField: TextField) {
print(textField.text)
}
If input is "cat"
Needed Output needs to be:
"c"
"ca"
"cat"
How would I achieve this?
Note: I've tried doing the .addtarget method:
textField.addTarget(self, action: Selector(self.textFieldDidChange(_:)), for: UIControlEvents.editingChanged)
with this function in the same file:
func textFieldDidChange(txtField: UITextField)
But I keep getting this error:
Value of type "name of my viewcontroller" has no member "textFieldDidChange"
I've looked up this question but most give me outdated answers. Thanks!
Try this:
#IBAction func displayAlert(_ sender : AnyObject) {
let alertController = UIAlertController(title: "Enter some text", message: "Please enter some text in the field below", preferredStyle: .alert)
alertController.addTextField()
alertController.addAction(UIAlertAction(title: "OK", style: .default) { action in
print("User pressed OK")
})
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
print("User pressed Cancel")
})
if let textField = alertController.textFields?.first {
textField.addTarget(self, action: #selector(ViewController.textHasChanged), for: .editingChanged)
}
self.present(alertController, animated: true)
}
Related
The code below allows the user to do a 2 finger swipe down on an imageView and thus presenting a popover/actionSheet. That process works fine. Normally it is possible to tap outside the popover/actionSheet to close it.
The problem is that once the popover/actionSheet is presented, it doesn't allow tapping the background to close the popover/actionSheet. You actually need to tap inside the popover/actionSheet to close it.
There are other places in the app that present a popover/actionSheet but these are presented using a simple button tap.
Here's the really weird scenario. If I do the 2 finger swipe on the imageView and open the popover/actionSheet, the inability to tap the backGround is broken on all the other popover/actionSheet in the app too. If I bypass the 2 finger swipe on the imageView all of the other popover/actionSheet work as normal.
I've stripped out all the code other than what's needed to present the popover/actionSheet. And I created a new project with on VC and one imageView so as to eliminate any possible conflict with cocoa pod, etc.
What is wrong with this code?
class ViewController: UIViewController
{
#IBOutlet weak var imageView_Outlet: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
imageView_Outlet.isUserInteractionEnabled = true
let swipeGuesture = UISwipeGestureRecognizer(target: self, action: #selector(imageViewSwiped(recognizer:)))
swipeGuesture.numberOfTouchesRequired = 2
swipeGuesture.direction = .down
imageView_Outlet.addGestureRecognizer(swipeGuesture)
}
#objc func imageViewSwiped(recognizer: UISwipeGestureRecognizer)
{
let theAlert = UIAlertController(title: "Welcome Image", message: "Only one image can be saved as your welcome screen. The current image will automatically be replaced." , preferredStyle: .actionSheet)
let chooseImage = UIAlertAction(title: "Choose a New Image", style: .default, handler: { (okAction) in
})
let deleteBtn = UIAlertAction(title: "Delete the Current Image", style: .destructive, handler: { (deleteAction) in
})
let cancelBtn = UIAlertAction(title: "Cancel", style: .cancel) { (cancelAction) in
}
theAlert.addAction(cancelBtn)
theAlert.addAction(chooseImage)
theAlert.addAction(deleteBtn)
let popOver = theAlert.popoverPresentationController
popOver?.sourceView = self.imageView_Outlet
popOver?.sourceRect = self.imageView_Outlet.bounds
popOver?.permittedArrowDirections = .any
present(theAlert, animated: true)
}
}
How to show keyboard when UIAlertController is present with Custom View and UITextField? I mean I want to keyboard automatically show without user touch the UITextField in alert view.
My code like below to make a Alert.
func callAlertConfirmation() {
let vc = UIViewController()
vc.preferredContentSize = CGSize(width: 250, height: 70)
let textBookmark = UITextField(frame: CGRect(x: 10, y: 0, width: 230, height: 40))
textBookmark.placeholder = "Typing folder name"
textBookmark.font = UIFont.systemFont(ofSize: 15)
textBookmark.textColor = UIColor.black
textBookmark.borderStyle = UITextField.BorderStyle.roundedRect
textBookmark.autocorrectionType = UITextAutocorrectionType.no
textBookmark.keyboardType = UIKeyboardType.alphabet
textBookmark.returnKeyType = UIReturnKeyType.done
textBookmark.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center
textBookmark.textAlignment = NSTextAlignment.left
textBookmark.clearButtonMode = .whileEditing
vc.view.addSubview(textBookmark)
let alert = UIAlertController(title: "Create New Folder", message: nil, preferredStyle: .alert)
alert.setValue(vc, forKey: "contentViewController")
let actOKButton = UIAlertAction(title: "OK", style: .default) { (_) -> Void in
// action When User Okay
}
alert.addAction(actOKButton)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.preferredAction = actOKButton
present(alert, animated: true)
}
And when I call
callAlertConfirmation
I get the result like this picture:
But I want to picture like below when I call
callAlertConfirmation
But when I use
alert.addTextField
I get keyboard automatically show when alert present.
Thanks in advance.
What
As #C4747N already said, you need to call
.becomeFirstResponder()
When
You want to call this method as you present the alert:
present(alert, animated: true) {
textBookmark.becomeFirstResponder()
}
The way you want to "read" this is like:
Present the alert and once you're done execute the completion body (make the alert's textField first responder)
I may be wrong but whenever I want a UITextView or UITextField to show a keyboard immediately i do:
textBookmark.becomeFirstResponder()
I want to display an ActionSheet on both, iPhone and iPad devices. While my code works properly on iPhone's, it doesn't on iPad's. The reason for this is that I need to specify a location where to display the popover. As a result, I tried to used the code proposed in this answer. The problem that I currently have is, that I do not know how to pass the argument sender to the method.
For example, I have a UITableViewRowAction which when clicked should display the ActionSheet:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let rowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Action", handler:{action, indexpath in
print("Action for element #\(indexPath.row).");
self.displayActionSheet()
});
return [rowAction];
}
Which argument is sender in my case? I am not able to pass rowAction to displayActionSheet(), because the variable is used within its own initial value. I also tried to pass self.displayDeleteLicencePlateActionSheet(self.items[indexPath.row]), but same result – I always end up in the else clause of the guard expression:
guard let button = sender as? UIView else {
print("sender empty")
return
}
I want to display also an ActionSheet when clicking on an UIBarButtonItem:
let myButton = UIBarButtonItem(title: "FooBar", style: .Plain, target: self, action: #selector(SecondViewController.displaySecondActionSheet(_:)))
But same result. How can this be done?
Please refer following code to fix your issue.
TARGET_OBJECT will be your sender where from you want to show an alert.
func showAlert() {
let alert = UIAlertController(title: "Title", message: "Message text", preferredStyle: .alert)
let actionOK = UIAlertAction(title: "OK", style: .default) { (alertAction) in
}
alert.addAction(actionOK)
if let popoverController = alert.popoverPresentationController {
popoverController.sourceView = self.TARGET_OBJECT // TARGET_OBJECT will be your sender to show an alert from.
popoverController.sourceRect = CGRect(x: self.TARGET_OBJECT.frame.size.width/2, y: self.TARGET_OBJECT.frame.size.height/2, width: 0, height: 0)
}
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
}
Please check attached image it will appear as you want.
I've spent hours trying to figure out how to create/then get a custom inputView to work.
I have a grid of TextInputs (think scrabble board) that when pressed should load a custom inputView to insert text.
I've created a .xib file containing the UI elements for the custom inputView. I was able to create a CustomInputViewController and have the inputView appear but never able to get the actual TextInput to update it's value/text.
Apple documentation has seemed light on how to get this to work and the many tutorials I've have seen have been using Obj-C (which I have been unable to convert over due to small things that seem unable to now be done in swift).
What is the overarching architecture and necessary pieces of code that should be implemented to create a customInputView for multiple textInputs (delegate chain, controller, .xibs, views etc)?
Set up a nib file with the appropriate inputView layout and items. In my case I set each button to an action on File Owner of inputViewButtonPressed.
Set up a storyboard (or nib if you prefer) for a view controller.
Then using the following code, you should get what you're looking for:
class ViewController: UIViewController, UITextFieldDelegate {
var myInputView : UIView!
var activeTextField : UITextField?
override func viewDidLoad() {
super.viewDidLoad()
// load input view from nib
if let objects = NSBundle.mainBundle().loadNibNamed("InputView", owner: self, options: nil) {
myInputView = objects[0] as UIView
}
// Set up all the text fields with us as the delegate and
// using our input view
for view in self.view.subviews {
if let textField = view as? UITextField {
textField.inputView = myInputView
textField.delegate = self
}
}
}
func textFieldDidBeginEditing(textField: UITextField) {
activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
activeTextField = nil
}
#IBAction func inputViewButtonPressed(button:UIButton) {
// Update the text field with the text from the button pressed
activeTextField?.text = button.titleLabel?.text
// Close the text field
activeTextField?.resignFirstResponder()
}
}
Alternatively, if you're wanting to be more keyboard-like, you can use this function as your action (it uses the new let syntax from Swift 1.2), break it up if you need 1.1 compatibility:
#IBAction func insertButtonText(button:UIButton) {
if let textField = activeTextField, title = button.titleLabel?.text, range = textField.selectedTextRange {
// Update the text field with the text from the button pressed
textField.replaceRange(range, withText: title)
}
}
This uses the UITextInput protocol to update the text field as appropriate. Handling delete is a little more complicated, but still not too bad:
#IBAction func deleteText(button:UIButton) {
if let textField = activeTextField, range = textField.selectedTextRange {
if range.empty {
// If the selection is empty, delete the character behind the cursor
let start = textField.positionFromPosition(range.start, inDirection: .Left, offset: 1)
let deleteRange = textField.textRangeFromPosition(start, toPosition: range.end)
textField.replaceRange(deleteRange, withText: "")
}
else {
// If the selection isn't empty, delete the chars in the selection
textField.replaceRange(range, withText: "")
}
}
}
You shouldn't go through all that hassle. There's a new class in iOS 8 called: UIAlertController where you can add TextFields for the user to input data. You can style it as an Alert or an ActionSheet.
Example:
let alertAnswer = UIAlertController(title: "Input your scrabble Answer", message: nil, preferredStyle: .Alert) // or .ActionSheet
Now that you have the controller, add fields to it:
alertAnswer.addTextFieldWithConfigurationHandler { (get) -> Void in
getAnswer.placeholder = "Answer"
getAnswer.keyboardType = .Default
getAnswer.clearsOnBeginEditing = true
getAnswer.borderStyle = .RoundedRect
} // add as many fields as you want with custom tweaks
Add action buttons:
let submitAnswer = UIAlertAction(title: "Submit", style: .Default, handler: nil)
let cancel = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertAnswer.addAction(submitAnswer)
alertAnswer.addAction(cancel)
Present the controller whenever you want with:
self.presentViewController(alertAnswer, animated: true, completion: nil)
As you see, you have various handlers to pass custom code at any step.
As example, this is how it would look:
My objective is to create a new row in the table in the master view titled with the input from the UIAlert stored in namePrompt. How would I achieve this?
Currently, when I try to make a new row (using the + button in the top-right hand corner of the iPad simulator, running iOS 8) it crashes the app. I'm not really sure what I have done wrong, and any help at all is appreciated.
Code:
func insertNewObject(sender: AnyObject) {
//prompt for kid name
var namePrompt = UIAlertController(title: "Add Child", message: "Please enter child name", preferredStyle: UIAlertControllerStyle.Alert)
namePrompt.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
namePrompt.addAction(UIAlertAction(title: "Add Child", style: UIAlertActionStyle.Default, handler: nil))
namePrompt.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
textField.placeholder = "Johnny"
})
self.presentViewController(namePrompt, animated: true, completion: nil)
let childName: AnyObject = namePrompt.textFields![0]
//add new object w/ above name
objects.insertObject(childName, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
I think that the problem is arising from the let childName: Any Object line, and/or the objects.insertObject line.
--EDIT--
When I build/run the project, no errors are given to me in the code, but when I try to add a new row, as soon as I press the + button, I get 0x1fbaff4: nopw %cs:(%eax,%eax) from Thread 1 . This is related to the line let object = objects[indexPath.row] as NSDate (in below snippet) line (the default for the objects.insertObject line above was an NSDAte). I changed the NSDate to NSString and the same thing happened.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let object = objects[indexPath.row] as NSString
cell.textLabel?.text = object.description
return cell
}
You seem to be assuming that the textFields of the UIAlertController will be filled in immediately after the alert is presented. That doesn't give the user any time to actually fill out the fields.
Put the let childName = namePrompt.textFields![0] line (and following) in one of your button actions instead and wait for the user to tap that button before reading the textField.
--EDIT--
Here is your code with my suggested modification. I made as few changes to your code as possible. Note that there is an if statement to guard against empty childNames, but it won't guard against someone entering a bunch of spaces. You should probably make that part more robust.
func insertNewObject(sender: AnyObject) {
//prompt for kid name
var namePrompt = UIAlertController(title: "Add Child", message: "Please enter child name", preferredStyle: UIAlertControllerStyle.Alert)
namePrompt.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
namePrompt.addAction(UIAlertAction(title: "Add Child", style: UIAlertActionStyle.Default, handler: { (alertAction) in
let childName = (namePrompt.textFields![0] as UITextField).text as String
if childName != "" {
self.objects.insert(childName, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
}))
namePrompt.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
textField.placeholder = "Johnny"
})
self.presentViewController(namePrompt, animated: true, completion: nil)
}