UITextField with "clear' button size on begin editing - swift

I have a UITextField with a clear button and I place it inside a vertical Stack View with leading alignment. When the editing starts, the field has the size without the clear button taking into account, so the text scrolls left. As soon as any character is added, the field corrects its size. I don't want to fix the field width, as I like this behaviour—when you always have the clear button at the end of the text.
The function below corrects the size, but the clear button appears only after the text field changes:
func textFieldDidBeginEditing(_ textField: UITextField) {
UIView.animate(withDuration: 0.4) {
textField.sizeToFit()
}
}
Any idea how to correct this? Thank you in advance.

func textFieldDidBeginEditing(_ textField: UITextField) {
textField.invalidateIntrinsicContentSize()
}

Related

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
}

NSTextField Focus

How to remove focus on NSTextField if I'm not focus in the textField?
I have a NSTextField and I set the Action: Sent On End Editing. After clicking the textField, the FOCUS will always in my NSTextField when I click other places on the view.
Furthermore, I use makeFirstResponder and resignFirstResponder but all in vain. Any idea?
#IBAction func endOfEditing(sender: NSTextField) {
view.window?.makeFirstResponder(nil)
view.window?.resignFirstResponder()
}
For illustration, the FOCUS will always be in textField no matter where I pressed. (Even when I pressed the button)
How can I remove the focus when I click outside the textField?
Just try this: change "view.window?" to "sender"
#IBAction func endOfEditing(sender: NSTextField) {
sender.makeFirstResponder(nil)
sender.resignFirstResponder()
}
Swift 4
For those still troubling with focus and unfocus: I don't know if it was the case back in 2016, but in Swift 4 my NSTextField loses focus except if I click in another "selectable" outlet.
For instance if I click on a NSButton right after NSTextField it won't lost focus. I suspect the NSButton catches the event, preventing the textfield to know it has to unfocus.
Here is a much radical solution which will definitely defocus an NSTextField
#IBAction func clickMyButton(_ sender: NSButton) { // or anywhere else
self.quantityTextField.isEnabled = false
self.quantityTextField.isEnabled = true
}
Not very sexy but it is reliable
Edit: Just saw it, it solves #Lee's issue
try add this function to your viewController
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// I assume your textField call myText
myText. resignFirstResponder()
}

How to automatically adjust the width of a centered UITextfield in Swift while editing?

I have a custom UITextfield that is x-centered to its superview via a constraint. It also has a height-constraint and a nice thin line right under the text that has the same width as the textfield itself (like I said: custom).
I now want to keep the UITextfield centered while editing its content, adjusting itself in width to its string content.
What right now happens is that it keeps its width while editing, cropping its overlapping content.
My code is kind of
// on textfields editing change event
#IBAction func nameFieldEditingChanged(sender: AnyObject) {
// set width of textfield to width of textstring
checkUserNameTextfieldWidth()
}
func checkUserNameTextfieldWidth(){
println("checkUserNameTextfieldWidth \(userName.text) / \(userName.frame.size.width) / \(view.bounds.width)")
// something has to happen here I guess...
}
Thnx!
You have to use UITextView instead of UITextField and apply its sizeThatFits method (see this response)

Prevent user from setting cursor position on UITextField

I have an UITextField constructed using the storyboard. I want to not allow the user to change the position of the cursor and keep it always at the end of the text into the text field.
I tried to change the position of the cursor at the touchdown event, but when selecting the text field and then change the position of the cursor by touching the text field again, the position is changed:
- (IBAction)amountBoxTouchDown:(id)sender {
UITextPosition *start = [amountBox positionFromPosition:[amountBox beginningOfDocument] offset:amountBox.text.length];
UITextPosition *end = [amountBox positionFromPosition:start
offset:0];
[amountBox setSelectedTextRange:[amountBox textRangeFromPosition:start toPosition:end]];
}
Does anyone know a solution? Thanks
Simply create a subclass of UITextField and override the closestPositionToPoint method:
- (UITextPosition *)closestPositionToPoint:(CGPoint)point{
UITextPosition *beginning = self.beginningOfDocument;
UITextPosition *end = [self positionFromPosition:beginning offset:self.text.length];
return end;
}
Now the user will be unable to move cursor, it will be always in the end of field.
SWIFT:
override func closestPosition(to point: CGPoint) -> UITextPosition? {
let beginning = self.beginningOfDocument
let end = self.position(from: beginning, offset: self.text?.count ?? 0)
return end
}
Disable any gesture recognizers on the text field after it has become first responder. This allows it to receive the initial tap, but prevents the user from interacting with the field while it is the first responder. This keeps the system behavior of keeping the cursor at the end of the text without allowing the user to override it.
In a UITextField subclass, add the following:
SWIFT 3.1:
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return !isFirstResponder
}
In your storyboard, change the class of your text field to your UITextField subclass.
If you are interested in always keeping your cursor at the end of the text, do this:
override func closestPosition(to point: CGPoint) -> UITextPosition? {
return self.endOfDocument
}
Think in layers and of controls as tools that you can combine to achieve functionality.
If you simply place a UIButton over top a UITextField and change the button type to Custom, you can prevent all touch events on the text field such as moving the cursor, selecting text, copying, cutting, and pasting.
By default, a custom button is transparent.
Create an action so that when the button is touched, the text field becomes the first responder.
Ok, what you have to do is have a UITextField that is hidden.
Add that hidden text field to the view, and call becomeFirstResponder on it. From your amountBoxTouchDown: method.
In the Textfield delegate, take the text the user typed in and add it to amountBox.text. Also turn off userInteractionEnabled for the visible amountBox textField.
This creates the effect you desire.
Have a look at for some sample code Link.
Extension of #kas-kad's solution. I created a subclass of UITextView, with this
var enableCursorMotion = true
override func closestPosition(to point: CGPoint) -> UITextPosition? {
if enableCursorMotion {
return super.closestPosition(to: point)
}
else {
let beginning = self.beginningOfDocument
let end = self.position(from: beginning, offset: (self.text?.count)!)
return end
}
}
Why not use the textFieldDidChangeSelection method from UITextFieldDelegate?
With the below implementation the cursor is always in the end.
And you don't have to create a subclass of your UITextField
func textFieldDidChangeSelection(_ textField: UITextField) {
let position = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: position, to: position)
}
A drawback of this is that you can't mark the text or even see the menu with select, paste etc options.