How to make a number pad appear without a text box - swift

Hello I am trying to have a number pad appear after a timer is up. Then have my user type numbers on the pad and their input be saved to a variable in my code not a text box. I can't seem to find anything on popping up a number pad without using a text box. Any help is appreciated.

Ok I'm going to give you some code that will greatly help you. You need some sort of UITextView or UITextField to get the system keyboard. So essentially what we will do is have a textField without showing it, and then grab the info off it and store it into the variable.
//Dummy textField instance as a VC property.
let textField = UITextField()
//Add some setup to viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self //Don't forget to make vc conform to UITextFieldDelegateProtocol
textField.keyboardType = .phonePad
//http://stackoverflow.com/a/40640855/5153744 for setting up toolbar
let keyboardToolbar = UIToolbar()
keyboardToolbar.sizeToFit()
let flexBarButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneBarButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissKeyboard))
keyboardToolbar.items = [flexBarButton, doneBarButton]
textField.inputAccessoryView = keyboardToolbar
//You can't get the textField to become the first responder without adding it as a subview
//But don't worry because its frame is 0 so it won't show.
self.view.addSubview(textField)
}
//When done button is pressed this will get called and initate `textFieldDidEndEditing:`
func dismissKeyboard() {
view.endEditing(true)
}
//This is the whatever function you call when your timer is fired. Important thing is just line of code inside that our dummy code becomes first responder
func timerUp() {
textField.becomeFirstResponder()
}
//This is called when done is pressed and now you can grab value out of the textField and store it in any variable you want.
func textFieldDidEndEditing(_ textField: UITextField) {
textField.resignFirstResponder()
let intValue = Int(textField.text ?? "0") ?? 0
print(intValue)
}

I am using storyboard and this is what I did:
drag-drop a text field
On the storyboard, in the attributes inspector (having selected the text field), under "Drawing", select hidden
make an outlet for the text field in your view controller
make sure your view controller extends the UITextViewDelegate
make your current view controller the delegate
in the required location simply call <textfieldOutlet>.becomeFirstResponder()
Now that this is simply a textfield's data, u can always store the value and use it else where.

Related

Swift: programmatically locate a UITextFelds UILabel and containing UIView container

I have a form with a UIView wrapping around a UILabel and UITextField. When a user enters the field I would like to change the colour of the label and the border colour of the view container.
If I call a function on the firstResponder I will need to find the text field's corresponding label and view copntainer.
I thought to have a firstResonder function for each field and in each function send the corresponding outlets (textfield, label, view) to a function which handles the colour changes for the label and view border.
This is not terrible but I and sure this can be accomplished more efficiently.
Any pointers please.
edit:
I changed my requirement slightly to place the label inside the textfield and highlight the textfield border instead of the encapsulating view.
This is what I ended up with:
override func viewDidLoad() {
super.viewDidLoad()
firstNameLabel.connect(with: firstName)
}
extension UILabel {
#objc
func editingChanged(textField: UITextField) {
}
#objc
func editingDidBegin(textField: UITextField) {
self.textColor = UIColor.blue
textField.layer.borderColor = UIColor.blue.cgColor
}
#objc
func editingDidEnd(textField: UITextField) {
self.textColor = UIColor.green
textField.layer.borderColor = UIColor.green.cgColor
}
func connect(with textField:UITextField){
//textField.addTarget(self, action: #selector(UILabel.editingChanged(textField:)), for: .editingChanged)
textField.addTarget(self, action: #selector(UILabel.editingDidBegin(textField:)), for: .editingDidBegin)
textField.addTarget(self, action: #selector(UILabel.editingDidEnd(textField:)), for: .editingDidEnd)
textField.layer.borderColor = UIColor.gray.cgColor
textField.layer.borderWidth = 1;
textField.layer.cornerRadius=10
}
}
The usual thing is to give each member of each group a corresponding tag. Since viewWithTag drills down to find any view with the given tag, the problem is solved if you know how to convert the tag value of the view you have to the tag value of the view you want.
For example, text field 10, view 110, label 210; text field 11, view 111, label 211; and so on. Or whatever system suits your fancy.
Alternatively just walk the view hierarchy. The view is the text field's superview, and the label is the first subview of the view that is a label.

How can I reset navigation bar items when coming back from popup?

I have a navigation bar with two buttons as the right bar button items and a text field in the title view. If I tap the text field, a search screen pops up and I can enter texts into the text field. The texts in the text field would set the "resultText" variable in my code below. The button items, including filterItem and mapItem, are well connected with #IBOutlet.
I would like to hide the right bar button items when the text field is not empty. With the code shown below, it works fine initially when I enter texts into the text field. However, when I delete the texts in the text field and then returns from the pop-up, the app crashes because the button items are found nil. I do not understand why it is nil. Am I missing something here?
if !resultText.isEmpty {
navigationItem.rightBarButtonItem = nil
} else {
navigationItem.setRightBarButtonItems([filterItem, mapItem], animated: false)
}
You are adding and removing buttons from the navigation bar, it must be removing reference from view. Try adding it using code -
func addBarButtonItems() {
let filterItemBarButton = UIBarButtonItem(title: "filterItem", style: .plain, target: self, action: #selector(filterItemTapped))
let mapItemBarButton = UIBarButtonItem(title: "mapItem", style: .plain, target: self, action: #selector(mapItemTapped))
navigationItem.rightBarButtonItems = [filterItemTapped, mapItemTapped]
}
func removeBarButtonItems() {
navigationItem.rightBarButtonItems = nil
}
#objc private func filterItemTapped() {
//code
}
#objc private func mapItemTapped() {
//code
}
Call these methods correctly in textField delegate methods.

UILabel Not Updating to match UILabel.text

When I change the text attribute of my UILabel, it only partially updates in the app.
I've tried putting it in the viewDidLoad function and in a separate dedicated function to no avail. If I print the Label.text to the console after the update it is correct, but it doesn't show up properly on screen.
The class in question:
#IBOutlet weak var PromptLabel: UILabel!
var labelText = "";
override func viewDidLoad() {
super.viewDidLoad()
print("view loaded")
self.PromptLabel.text = "Tell Us... " + labelText;
// Do any additional setup after loading the view.
}
Called by:
#IBAction func UsingButtonPressed(_ sender: Any) {
(sender as AnyObject).setTitleColor(UIColor.red, for: .normal)
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil);
let secondController = storyBoard.instantiateViewController(withIdentifier: "Secondary View Controller") as! SecondaryViewController;
secondController.labelText = "What You're Using Today"
self.present(secondController, animated: true, completion: nil)
}
The label should display as "Tell Us... What You're Using Today" but it only shows "Tell U..." which makes no sense.
You need to make sure that the constraint for the UILabel's width is wide enough to accommodate the text. Otherwise, it is being cutoff and the ... you are seeing is because the label's line break is set to truncating tail, which means that the end of the string will be replaced with ... when the label is too narrow.
So this is an interface builder issue and not a Swift-specific issue. The label's text is being correctly updated, the UI is just not able to properly display it.
If you want the width constraint of the label to change dependent on the text, there are ways to calculate the width of the text and update the constraint's constant to accommodate that text's width.

Swift: Can't get UITextField to dismiss keyboard

I have two textFields and a Done button in my VC but I'm having some problems with the ending of editing.
My textFieldDidEndEditing method is called when I tap on one textField after being inside the other one, or when I tap outside the textField (because I added a tap recognizer to the parent view) but not when I tap the Done button.
And, most important (especially when I run on an actual device), the keyboard won't disappear under any of these circumstances, even though my textFieldDidEndEditing method calls resignFirstResponder().
Why isn't the keyboard dismissing? Also, is there a way to have textFieldDidEndEditing get called when I tap outside the field just automatically (without having it come from the tap recognizer)? It just seems like this should be how it works, but if I'm wrong, I'm wrong.
Here's some pertinent parts of my code.
1.Trying to dismiss the keyboard. The first part of this method works, and the value is stored (when the method is called at all, that is). At no point does the cursor disappear from the textField, nor does the keyboard get dismissed.
func textFieldDidEndEditing(_ textField: UITextField) {
if let playerName = textField.text, let playerNum = nameFields.index(of: textField) {
playerNames[playerNum] = playerName
}
resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textFieldDidEndEditing(textField)
return true
}
Also, here's a curious thing: when I set a breakpoint in textFieldDidEndEditing and debug, enter a value in one field and hit Done, it segues to the next scene, and then stops at textFieldDidEndEditing, which at this point has no effect (the values may be stored but they aren't reflected in the new scene).
2.Trying to add the tap recognizer to the done button. I don't have an outlet to the done button in my code, just out of laziness, so that's probably the best solution. But, I'm still interested in why this doesn't work. This is identical to the code that defines the tap recognizer that's working in the parent view.
func dismiss(_ sender:UITapGestureRecognizer) {
nameFields.forEach { textFieldDidEndEditing($0) }
}
override func viewDidAppear(_ animated: Bool) {
for view in view.subviews where view is UIButton {
let dismissTextField = UITapGestureRecognizer(target: self, action: #selector(dismiss(_:)))
dismissTextField.numberOfTapsRequired = 1
view.addGestureRecognizer(dismissTextField)
}
}
You need to call resignFirstResponder inside textFieldShouldReturn method instead of calling textFieldDidEndEditing.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
Also in your TapGesture method simply call endEditing(_:) with your view instead of looping through the array of textFields.
func dismiss(_ sender:UITapGestureRecognizer) {
self.view.endEditing(true)
}
Swift 5: This solution below is much easier actually.
Simply subclass your ViewController with the text fields to UITextFieldDelegate like this:
class CreateGroupViewController: UIViewController, UITextFieldDelegate {
then if you named your UITextFields: nameTextField & occupationTextField then add the following delegations to your viewDidLoad() method:
self.nameTextField.delegate = self
self.occupationTextField.delegate = self
Then simply add the generic function to your file:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
If you do it this way, then for every textField reference outlet you create, all of them will hide the keyboard after the user hits the return button no matter which textField he's typing. For each textField you add to your view, add another self.nextTextField.delegate = self line to your viewDidLoad.
Remember to test this with your iPhone/iDevice plugged into your developer computer bc XCode's simulator doesn't bring up the keyboard (bc you're normally testing using a full keyboard). Or if you've set up your testing hardware (iPhone) via WiFi you can do it that way also. Otherwise, your users will be "testing" this on TestFlight.

Keyboard issue on my app

I just made a simple app where you type things in four boxes and can randomly select one. After typing in the boxes I need the keyboard to go away so people can see what the result is, but the on-screen keyboard just stays. Is this something I need to change in the files of the app?
You have to use resignFirstResponder() property of the text field.
If you want to hide the keyboard when a tap occurred outside the text field,
you can use an UITapGestureRecognizer.
override func viewDidLoad() {
super.viewDidLoad()
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped(_:)))
view.addGestureRecognizer(tapRecognizer)
}
func backgroundTapped(sender: UITapGestureRecognizer) {
view.endEditing(true)
}