TextField user interaction disabled, but display an error view when touched - swift

I have a UITextField and in my ViewController's code it is set depending on it's value to
textField.isUserInteractionEnabled = false
or
textField.isUserInteractionEnabled = true
Now, when user interaction is disabled, I would like it still to react to touches and show an error message (e.g. unhide another view), which tells the user that editing this text field is not possible.
How can I achieve this in the most lean way? This solution here (https://stackoverflow.com/a/9117285) suggests to not disable user interaction, but reject content changes - which is what I don't want (the keyboard should not show up - it won't show up when user interaction is disabled, but I can't react to touches either).

You either need to add a view a bove the textfield when it's disabled with a gesture to show the appropriate message , or do this
NotificationCenter.default.addObserver(self, selector: #selector(keyShow), name:UIResponder.keyboardWillShowNotification, object: nil)
}
#objc func keyShow ( _ not:NSNotification) {
if shouldHideKeyB {
self.view.endEditing(true)
// show disabled message
}
}
where shouldHideKeyB is the current state of the textfield

Instead of using isUserInteractionEnabled you could implement your own isDisabled Bool and UITextFieldDelegate and implement func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool. When isDisabled is true show the error view and return false, otherwise return true. In the didSet of isDisabled you can hide the error view.
Returning false from this method should stop the keyboard from popping up and will still allow you to interact with the view.

Related

How to give the focus to the text field?

Very simple setting: I have a ViewController scene with a TextField on it. The ViewController is the text field's delegate.
I would like to have the following behavior: When the user enters the text field (i.e., taps on it), I would like to display a modal alert box with an OK button. After the users presses OK, the TextField should get the focus (i.e., cursor blinking inside it).
I can't get this working. I react on the user tapping in the text field by textFieldShouldBeginEditing(). This works in the sense that I can display the message box there. But after the user (in this case it's me ;o)) taps the OK button, the text field doesn't have the focus, and when I tap it again, the message box appears again.
How can I get rid of this?
Do you really want the modal dialog to show every time the text field is clicked? Bear in mind that putting the activation in textFieldShouldBeginEditing() will mean that re-activating the field after dismissing the dialog will re-show the dialog.
Maybe you just need to show the dialog once? In which case a simple boolean flag that is set on first showing will fix the issue. I.e. at view controller scope:
var hasShownWarningDialog = false
and then implement instead (after comments):
func textFieldShouldBeginEditing(textField: UITextField) -> Bool
{
if !hasShownWarningDialog
{
hasShownWarningDialog = true
// Create dialog here
self.present(alert, animated: true, completion: {self.textField.becomeFirstResponder()})
return false
}
else
{
return true
}
}

How to create toggle button that only toggles once

I want to create a button that, when pressed, toggles to a different format and stays that way, not allowing the user to toggle it back. For example, I am trying to create a button that says "Special 1" then when it is clicked by the user, it toggles to say "USED". When the word "USED" pops up on the UIButton, it stays that way and the user cannot change it back. Please help me out I can't figure this out. I am also open to other ideas to execute this other then a UIButton, for example maybe a UITableView??
What you can do is create a boolean (true or false) variable in your ViewController, and whenever the button is tapped you set this variable so that the tap function associated with the button can be manipulated.
1.) Make an action connection from your ViewController to your code this can be done by clicking the dual view button (the two circles in the top right corner) then holding control and drag your button into your code. :
For more information on connecting views to your code: https://codewithchris.com/9-hooking-it-all-up-swift-iboutlet-properties/
Skip to 6 min for the actual connection.
2.) Now we can just make our boolean variable, and make a basic if statement:
var didClick:Bool = false
#IBAction func touchButton(_ sender: UIButton) {
if !didClick { // If didClick is false
didClick = true // Set it to true
sender.setTitle("USED", for: .normal)
}
}
Note: The sender should be set to UIButton NOT Any
Should look like this:
_ sender: UIButton
If you are using a storyboard you can write this inside the IBAction function:
#IBAction func buttonName(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
// Do whatever you need to do after...
}
sender's type could be UIButton or Any depending on how you made the connection from the storyboard.

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.

Dismiss Keyboard not working with PickerView in the way

(So far) I have two view controllers which both have at least one textfield.
When the user taps into a textfield the keyboard pops up.
I have all of the code in place to move content up and then back down again when this happens, (everything is inside of a scroll view which I am led to believe is best practice)
I also have the code in place to dismiss the keyboard when the user taps outside of the textfield.
On the first view controller it works great, but on the 2nd I have a UIPickerView that takes up a good amount of space underneath the textfield. So what happens is when the user goes to tap the most obvious amount of space he/she is actually tapping the scroll view and nothing happens. But if the user taps in a very small area that is empty and not the scroll view the keyboard dismisses.
How can I dismiss the keyboard with the UIPickerView in the way?
Here is an image of what my situation looks like
Here is some of the code
func textFieldShouldClear(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
func dismissKeyboard() {
view.endEditing(true)
}
You can disable userInteractionEnabled on the picker when the keyboard is shown (or textField become first responder) and enable it back when the keyboard is dismissed (or textField resign first responder).
You have to resignFirstResponder .
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
textField.resignFirstResponder()
return false
}

Select next NSTextField with Tab key in Swift

Is there a way to change the responder or select another textfield by pressing tab on the keyboard, in Swift?
Notes:
It's for a fill in the blank type application.
My VC creates a list of Words [Word], and each of those words has its own WordView - word.wordView. The WordView is what is displayed. WordView is a child of NSTextField.
I tried to override keydown but it doesn't allow me to type anything in the text view.
You have to connect your textField nextKeyView to the next textField through the IB or programmatically:
textField1.nextKeyView = textField2
Assuming you want to go from textView1 to textView2. First set the delegate:
self.textView1.delegate = self
Then implement the delegate method:
func textView(textView: NSTextView, doCommandBySelector commandSelector: Selector) -> Bool {
if commandSelector == "insertTab:" && textView == self.textView1 {
self.window.makeFirstResponder(self.textView2)
return true
}
return false
}
If you want some control over how your field tabs or moves with arrow keys between fields in Swift, you can add this to your delegate along with some move meaningful code to do the actual moving like move next by finding the control on the superview visibly displayed below or just to the right of the control and can accept focus.
public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
switch commandSelector {
case #selector(NSResponder.insertTab(_:)), #selector(NSResponder.moveDown(_:)):
// Move to the next field
Swift.print("Move next")
return true
case #selector(NSResponder.moveUp(_:)):
// Move to the previous field
Swift.print("Move previous")
return true
default:
return false
}
return false // I didn't do anything
}
I had the same or a similar problem, in that I wanted to use an NSTextView field, to allow multiple lines of text to be entered, but it was the sort of field where entering a tab character would make no sense. I found an easy fix for this: NSTextView has an instance property of isFieldEditor, which is set to false by default; simply set this to true, and tabs will now skip to the next field.