How to determine the last activ UITextField in Xcode - swift

I am new to swift and i am playing around with a little converter app. I Have two TextFields and one "convert" button.
I want to overwrite the non acticv Textfield with the converted value from the last activ TextField.
I know it would be easier with more Textfields and more buttons but i would like to solve it this way.
Can anybody give me an advise?
var NumberC = Int(InputCelcius.text!)!
var NumberF = Int(InputFahrenheit.text!)!
if InputCelcius.isFirstResponder {
if (InputCelcius.text?.isEmpty)! {
NumberC=0
print("NumberC is empty")
}
NumberC = NumberC * 9/5 + 32
InputFahrenheit.text = " \(NumberC)"
}
else if InputFahrenheit.isFirstResponder {
if (InputFahrenheit.text?.isEmpty)! {
NumberF=0
print("NumberF is empty")
}
NumberF = (NumberF - 32) * 5/9
InputCelcius.text = " \(NumberF)"
}

I’d suggest using the UITextFieldDelegate function textFieldDidEndEditing. Make sure you subclass your viewController as a UITextFieldDelegate.
class ViewController: UIViewController, UITextFieldDelegate {
Set each textField's delegate to self in viewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
InputFahrenheit.delegate = self
InputCelsius.delegate = self
}
With this method you wouldn't even need a UIButton, but you could use the button to force the textField to end editing like this.
#objc func buttonAction (sender: UIButton) {
self.view.endEditing(true)
}
Which will call this delegate method if the user was editing a textField.
func textFieldDidEndEditing (textField: UITextField) {
if textField.isEmpty {return} //this prevents nil errors and user would have to type in "0" if wishing to convert zero.
if textField == InputCelsius {
let conversion = Float(textField.text!) * 9/5 + 32
InputFahrenheit.text = String(format: “%.f”, conversion) //“%.1f” will show one decimal place and so forth
} else if textField == InputFahrenheit {
let conversion = ( Float(textField.text!) - 32 ) * 5/9
InputCelsius.text = String(format: “%.f”, conversion)
}
}

I‘m not quite sure about that but I think if you press the button, the button itself will become first responder, so your code won‘t work.
The proper way to do that, would be to set IBAction functions that get called when you finish editing one of your textfields. Just connect the Textfields inside your storyboard to the code like you did it with your button.
Then you can set up two functions, one for each of the textfields, so you don‘t have to worry about firstresponder.

Related

I can't change the text of a UILabel in Swift

I am trying to change the text of a UILabel in my code, but the text won't change.
I tried to use the well-known command for changing the text, "NameOfLabel.text = 'Hello", but that did not work. So I tried to put it in a start function so you would click a UIButton and it would change the text, didn't work either.
#IBOutlet var nameOfRobot: UILabel!
#IBAction func startButton(_ sender: Any){
let nameNumber = Int.random(in: 1...3)
if nameNumber == 1 {
self.nameOfRobot.text = "Ben"
}
if nameNumber == 2 {
self.nameOfRobot.text = "Oliver"
}
if nameNumber == 3 {
self.nameOfRobot.text = "Colton"
}
}
I want it to choose a number between 1 and three and have it change the UILabel to that name. When I start the app though, it works, but it doesn't change the text of the label.
Looks like you forgot to connect the action to the button press:
You can tell this is done correctly by looking at the full circle indicator in the editor:
EDIT: Setting the correct class to the viewController in the storyboard:

view move up in particular textfield delegate

I have to move the UIView in only last UITextField in Swift 3.0 on mentioned below delegate method using tag,
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField.tag == 4){
//UIView Up
}
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if (textField.tag == 4){
//UIView Down
}
return true
}
I tried many codes but none of them are working like notification,..etc.
You need to add Observers into the NotificationCenter for listening to both when Keyboard goes up and down (i'll assume your textfield outlet is lastTextField for this example to work but this obviously have to be adapted to whatever name you've had provide for it)
IBOutlet weak var passwordTextField: UITextField!
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
(Code above can be added in viewDidLoad())
Then you add methods to be executed when those notifications arrive, like this:
func keyboardWillShow(_ notification:Notification) {
if view.frame.origin.y >= 0 && lastTextField.isFirstResponder {
view.frame.origin.y -= getKeyboardHeight(notification)
}
}
func keyboardWillHide(_ notification:Notification) {
if view.frame.origin.y < 0 {
view.frame.origin.y += getKeyboardHeight(notification)
}
}
Validations within those methods prevent double execution like moving up/down twice when moving between textfields without resigning first responder which is common in cases like your (i assume your doing this for a form hence the clarification you only need it for the fourth textfield). Notice i'm only doing validation in for the specified textfield (with its outlet lastTextField) in the keyboardWillShow method, this in case you move thor another textfield while the keyboard is shown and resign responder from it in which case, even though it isn't the original place where you started, the view will return to its original place when the keyboard is hidden.
You'll also need a method for getting keyboard's height, this one can help with that:
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
Let me know how it goes but i just tested this same code on my app and it works so you should be fine.
PS: pay close attention to the storyboard (if you're using it) and that delegate for textfields are set up properly.
The problem you are trying to remedy is rather complicated, because it requires you to:
Find the textField which is firstResponder
Calculate where that TextField is relative to it's superViews
Determine the distance for the animation, so that the containing
superview doesnt jump out of the window, or jumps too
much/repeatedly
Animate the proper superView.
As you can see.. it's quite the algorithm. But luckily, I can help. However, this only works for a hierarchy which has the following layout:
superView (view in the case of UIViewController) > (N)containerSubviews > textFields
where N is an integer
or the following:
superView (view in the case of UIViewController) > textFields
The idea is to animate superView, based on which textField is firstResponser, and to calculate if it's position inside of the SCREEN implies that it either partially/totally obstructed by the Keyboard or that it is not positioned the way you want for editing. The advantage to this, over simply moving up the superView when the keyboard is shown in an arbitrary manner, is that your textField might not be positioned properly (ie; obstructed by the statusbar), and in the case where your textfields are in a ScrollView/TableView or CollectionView, you can simply scroll the texfield into the place you want instead. This allows you to compute that desired location.
First you need a method which will parse through a given superView, and look for which of it's subViews isFirstResponder:
func findActiveTextField(subviews : [UIView], textField : inout UITextField?) {
for view in subviews {
if let tf = view as? UITextField {
guard !tf.isFirstResponder else {
textField = tf; break
return
}
} else if !subviews.isEmpty {
findActiveTextField(subviews: view.subviews, textField: &textField)
}
}
}
Second, to aleviate the notification method, also make a method to manage the actual animation:
func moveFromDisplace(view: UIView, keyboardheight: CGFloat, comp: #escaping (()->())) {
//You check to see if the view passed is a textField.
if let texty = view as? UITextField {
//Ideally, you set some variables to animate with.
//Next step, you determine which textField you're using.
if texty == YourTextFieldA {
UIView.animate(withDuration: 0.5, animations: {
self./*the proper superView*/.center.y = //The value needed
})
comp()
return
}
if texty == YourTextFieldB {
// Now, since you know which textField is FirstResponder, you can calculate for each textField, if they will be cropped out by the keyboard or not, and to animate the main view up accordingly, or not if the textField is visible at the time the keyboard is called.
UIView.animate(withDuration: 0.5, animations: {
self./*the proper superView*/.center.y = //The Value needed
})
comp()
return
}
}
}
Finally, the method which is tied to the notification for the keyboardWillShow key; in this case, i have a UIViewController, with an optional view called profileFlow containing a bunch of UITextFields
func searchDisplace(notification: NSNotification) {
guard let userInfo:NSDictionary = notification.userInfo as NSDictionary else { return }
guard let keyboardFrame:NSValue = userInfo.value(forKey: UIKeyboardFrameEndUserInfoKey) as? NSValue else { return }
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
let keybheight = keyboardHeight
var texty : UITextField? //Here is the potential textfield
var search : UISearchBar? //In my case, i also look out for searchBars.. So ignore this.
guard let logProfile = profileFlow else { return }
findActiveTextField(subviews: [logProfile], textField: &texty)
//Check if the parsing method found anything...
guard let text = texty else {
//Found something.. so determine if it should be animated..
moveFromDisplace(view: searchy, keybheight: keybheight, comp: {
value in
search = nil
})
return
}
//Didn't find anything..
}
Finally, you tie in this whole logic to the notification:
NotificationCenter.default.addObserver(self, selector: #selector(searchDisplace(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
I can't provide more content to the code, since it all depends on your view hierarchy, and how you want things to animate. So it's up to you to figure that out.
On a side note, usually, if you have so many textfields that to lay them out properly means they overstep the length of the screen.. it's probable that you could simplify your layout. A way to make this algorithm better would be to make sure you have all your textfields in one containing view, which again can become heavy for when, say, you use AutoLayout constraints. Odds are if you're in this situation, you can probably afford to add a flow of several views etc.
There is also the fact that i've never really needed to use this for iPhone views, more for iPad views, and even then for large forms only (e-commerce). So perhaps if you're not in that category, it might be worth reviewing your layout.
Another approach to this, is to use my approach, but to instead check for specific textFields right in the findActiveTextField() method if you only have a handful of textfields, and to animate things within findActiveTextField() as well if you know all of the possible positions they can be in.
Either way, i use inout parameters in this case, something worth looking into if you ask me.

How to make a number pad appear without a text box

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.

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.

Swift save text to an instance/global variable when UIButton is pressed

So I have a UItextfield and a UIButton I was wondering if there is a way to save the text after the button is pressed in an instance variable so that I could use it in my other class.
The thing I am trying to do is to have a user input some text in the textfield on screen 1 and then when the user taps the button I want to take him to screen 2 however in the class of that screen 2 I want to know what text user entered in order to display data accordingly.
Right now I just have an action function for the button and I access text inside it and save it to an instance variable but when I call it in my other class it is empty because I initialized it as empty. So can somebody please help me and tell me how to go about this.
Thanks!
Here is the code for first screen
var searchRecipe = ""
#IBAction func SearchButton(sender: UIButton) {
if let recipe = RecipeSearchBar.text {
searchRecipe = recipe
}
}
Here is the code for second screen. I have connected the button in first screen this screen so when user taps the button he gets here.
var recipeName:[String] = []
var imageURL:[String] = []
var timeInSeconds:[Float] = []
func apiCall()
{
//search recipe API call
var searchRecipe = ""
searchRecipe = RecipesViewController().searchRecipe
print (searchRecipe) //prints nothing
endpoint = "http://api.yummly.com/v1/api/recipes?_app_id=apiId&_app_key=apiKey&q=\(searchRecipe)" //this is where I want to use the searchRecipe text from class 1
}
Well personally I would do what follows (Based on my interpretation of your issue):
Please note that I take for granted you are implementing a view-controller-based navigation and you know what IBOutlets, IBActions & Segues are
In the first view controller create an IBOutlet property of
UITextField named RecipeSearchBar and connect it to the relative text
field.
In the second view controller create a variable value of type
string;
Change the storyboard segue identifier to "toSecondViewController"
Then in the first view controller create an IBAction to be called
when the UIButton is pressed.
In your case:
#IBAction func SearchButton(sender: UIButton) {
if let recipe = RecipeSearchBar.text {
searchRecipe = recipe
}
//If you are navigating into a new view view controller
//here you need to call self.presentViewController(...)
}
Inside this action, you are going to call self.presentViewController
to display your second view controller, but you have to do one last
very important step: pass the value of the UITextField to the instance
that will hold the second view controller
To achieve this, override prepareForSegue in your first view controller
and share the UITextField value. This method is triggered once you called self.presentViewController for further implementation.
//Did not tested the code but should just be ok
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "toSecondViewController")
{
//Create the instance
let secondViewController = segue.destinationViewController
//Set the value
secondViewController.value = searchRecipe;
}
}
You are now good to go.
Hope this helped, if so mark the question for others.
Bye