UITextView selectAll method not working as expected - ios

I'm creating an iOS 8 app with Xcode 6.0.1 for my iPhone 5 (which has iOS 8.0.2 on it). I want to make it so that when a user clicks on my UITextView, all the text gets selected so he can easily start typing and erase what was there (but I don't want the text to be automatically erased because the user may want to keep it or append to it). To do this, I have the following code:
- (void)textViewDidBeginEditing:(UITextView *)textView {
if ([textView hasText]) {
NSLog(#"selectedRange before: %d", textView.selectedRange.length);
[textView selectAll:self];
NSLog(#"selectedRange after: %d", textView.selectedRange.length);
}
}
When this method gets called, the console output is what I expect (i.e. the selectedRange length is the same as the number of characters in the textView's text). However, nothing shows up as selected in the UITextView and it doesn't act selected (i.e. no selection menu pops up).
I have seen multiple questions like this on the internet, but none of the provided solutions worked for me (and some of them wrote it off as a bug without providing any solution). Changing the sender id to something other than self (such as nil) did not help, and neither did it help to call [textView select:self] as one person suggested. I have also tried this code:
- (void)textViewDidBeginEditing:(UITextView *)textView {
if ([textView hasText]) {
UITextRange *range = [textView textRangeFromPosition:textView.beginningOfDocument toPosition:textView.endOfDocument];
[textView setSelectedTextRange:range];
}
}
But, it has the same problem.
Any suggestions?

This solution works too and does not require subclassing UITextView, just put this function on your delegate:
OBJECTIVE C -
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
dispatch_async(dispatch_get_main_queue(), ^{
[textView selectAll:nil];
});
return YES;
}
SWIFT 3 -
func textViewDidBeginEditing(_ textView: UITextView) {
DispatchQueue.main.async {
textView.selectAll(nil)
}
}

#brentvatne 's solution worked for me. Posting the Swift syntax so people can copy and paste in the future.
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
dispatch_async(dispatch_get_main_queue()) {
textView.selectAll(nil)
}
return true
}

The best solution I've found for this issue so far is to create a custom UITextView (i.e. create a new class that extends UITextView) and then implement the selectAll method like this:
- (void)selectAll:(id)sender {
[super selectAll:sender];
UITextRange *selectionRange = [self textRangeFromPosition:self.beginningOfDocument toPosition:self.endOfDocument];
[self performSelector:#selector(setSelectedTextRange:) withObject:selectionRange afterDelay:0.0];
}
Then when you use a text view, set its type to your custom text view type (in your code and in the storyboard). Now you can successfully call the selectAll method whenever you need to. I suppose this should work with UITextField too, but I haven't tried it yet.

Related

UITextField "Next" Adding Extra Newline to UITextView [duplicate]

I have a scroll view which contains a UITextField and a UITextView. The UITextField return key is "Next" and when this is pressed I call [myTextView becomeFirstResponder]; A random new line is inserted into my textview so I start on the second line. How do I avoid this?
Also important to note that this does not happen when I tap directly on the UITextView rather than tapping the next key.
Thanks in advance!
One solution to solve this is when you implement the textFieldShouldReturn: delegate method, be sure to return NO, and not YES. By returning NO, the newline isn't passed on to the text field.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// move to next field or whatever you need
[myTextView becomeFirstResponder];
return NO;
}
Okay, this behaviour is due to a bug in iOS when becoming firstresponder within same run loop by using next button. To over come this you should do this manually.
First resign first responder from a textFied, and then make textView as a first responder. like this. Implement this delegate method.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
[textView performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
return YES;
}
Similar idea to performSelector, but using asynchronous dispatch queue (Swift 4 version):
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
DispatchQueue.main.async {
self.myTextView.becomeFirstResponder()
}
return true
}

resignFirstResponder keyboard will NOT go away before segue

I am stumped and I hope someone can help.
I am calling the resign first responder method for all five of my text fields prior to a segue. The segue occurs, if the keyboard was visible prior to the segue, the keyboard remains no matter what I do. This did not happen in IOS6. It is only happening in IOS7.
Thank you so much in advance for your assistance.
Here is the scenario:
The user touches one text field at time to enter data. The keyboard has no problems changing from first responder from one field to the next and can be resigned from the DONE button without issues. The problem comes when the user touches a field that will be populated from the picker view. If the keyboard was visible from one of the previous text fields, it won't go away.
I have this code attempting to resignFirstResponder on the editingDidBegin action of two of the fields. I am using these two fields to hold numbers but I am filling them from a picker on the next view.
- (IBAction)txtRatioOrHullTypeTouched:(id)sender
{
// Hide the keyboard before the segue to the picker occurs.
[self.txtPitch resignFirstResponder];
[self.txtRPM resignFirstResponder];
[self.txtSlipOrSpeed resignFirstResponder];
[self.txtRatio resignFirstResponder];
[self.txtHullType resignFirstResponder];
segueToPicker = YES; // Raise flag indicating that this segue is to the picker.
[self performSegueWithIdentifier:#"toPicker" sender:sender];
}
I also put this same code in the viewWillDisappear as shown here:
- (void)viewWillDisappear:(BOOL)animated // Unchanged
{
// Hide the keyboard before the segue to the picker occurs.
[self.txtPitch resignFirstResponder];
[self.txtRPM resignFirstResponder];
[self.txtSlipOrSpeed resignFirstResponder];
[self.txtRatio resignFirstResponder];
[self.txtHullType resignFirstResponder];
[super viewWillDisappear:animated];
}
Both of these methods are on the initial view, ViewController.m file.
I ended up here removing the text field causing the problem and replacing them with buttons. No scenario I tried (dozens) got this code to work as expected in IOS7, even though it all worked flawlessly in IOS6.
I tried all of the above and it worked as long as i dismissed the controller with a button. The function that was called when pressing the button could call the TextField's resignFirstResponder() function and all was well.
However, when an edge swipe was performed to dismiss the controller the keyboard kept popping up the next time I showed it. In my code I reuse the same controller between views. This might not be wise but, it's snappy!
After trying everything the internet had written (well not really, but pretty close) about this I found that i could implement the TextField's textViewShouldBeginEditing() and return false between the ViewControllers ViewDidDisappear and ViewDidAppear. It's ha hack, but it did the trick when nothing else worked.
I hope this helps you guys!
Swift code:
In my ViewController
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myTextField.allowEdit = true
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
myTextField.allowEdit = false
}
In my TextField class
class MyTextField: UIView, UITextFieldDelegate {
var allowEdit = true
func textFieldShouldBeginEditing(textView: UITextView) -> Bool {
return allowEdit
}
}
You can call endEditing: on the view controller with the text fields. Your viewWillDisappear: method will look like this:
- (void)viewWillDisappear:(BOOL)animated
{
[self.view endEditing:YES];
[super viewWillDisappear:animated];
}
Contributing my 2 cents worth. dismissing keyboard correctly on iOS 9.2, a minimalist sample, FYI.
...
#property (assign) BOOL isTransitioning;
...
-(void)viewWillAppear:(BOOL) animated {
self.isTransitioning = YES;
}
-(void)viewWillDisappear:(BOOL) animated {
self.isTransitioning = YES;
}
-(void)viewDidAppear:(BOOL) animated {
self.isTransitioning = NO;
}
-(void)viewDidDisappear:(BOOL) animated {
self.isTransitioning = NO;
}
-(BOOL) textViewShouldBeginEditing:(UITextView*) tv {
if (self.isTransitioning) {
return NO;
}
return YES;
}
I think due to the way you are leaving the view through a picker, without going through an exit, you need to include the following in your viewController:
- (BOOL) disablesAutomaticKeyboardDismissal
{
return NO;
}
Swift, 2017
override var disablesAutomaticKeyboardDismissal: Bool {
get { return false }
set { }
}
So it seems now that the text field that controls the keyboard will not allow resignation. I used the canResignFirstResponder query on that field and the result (boolean) was FALSE. I also noticed that i get a flashing cursor in the field even after the resignFirstResponder is called. – Larry J Oct 25 '13 at 23:32
I know this is old, but I had a similar issue and wanted to share what worked for me in case it might help anyone else:
After reading the above comment I found that moving [self.view endEditing:YES] from where I had it in textFieldDidBeginEditing to textFieldSHOULDBeginEditing did the trick for me. Now the keyboard is dismissing properly before my segue.
Taking Zaheer's comment into Swift this works very well for me.
view.endEditing(true)
This is a problem i have frequently. My best method to cope is creating a clear button under the keyboard and having that call a dismiss helper. Control the clear button by toggling its isHidden property. Tapping outside the keyboard will hit that clear button and call the dismiss helper. What it won't do is trigger your segue, the user will need to tap again to navigate out but that keyboard will be gone.
in viewDidLoad():
var clearButton: UIButton!
self.clearButton = UIButton(frame: self.view.frame)
self.clearButton.backgroundColor = .clear
self.clearButton.addTarget(self, action: #selector(self.dismissHelper(_:)), for: .touchUpInside)
self.view.addSubview(self.clearButton)
self.clearButton.isHidden = true
Then add the dismiss helper:
func dismissHelper(_ sender: UIButton?) {
self.clearButton.isHidden = true
view.endEditing(true)
}
func displayClearButton(){
print("display clear button, hidden = false")
self.clearButton.isHidden = false
}
then on your textfield add the target
self.textField.addTarget(self, action: #selector(self.displayClearButton), for: .editingDidBegin)

How to disable keyboard appearing when hitting on a text field , iOS?

I have a text field , and i need when the user presses it to show a custom picker.
The picker is shown fine , but the problem is that the keyboard appears on the bottom and i dont want that.
This is an iPad project which i am trying to convert from my iphone one. On the iPhone , this works well and the keyboard is always hidden.
What could i be missing/forgetting to do here ?
EDIT
For future reference what actually happened here , was that in fact both times (iphone & ipad) the keyboard was not hidden. I just thought that it was hidden in the iphone because my picker , which was popping from the bottom was hiding the keyboard as it was on top of it. But on ipad this wasnt the case.
Anyway i fixed it , using the delegate method suggested below.
Caution , i accepted this answer cause it was the one answering specifically what i wanted. The rest of the answers are correct and my considered better for other implementations.
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
// Here You can do additional code or task instead of writing with keyboard
return NO;
}
this delegate method will get called first when you hit to textfield and if you write NO as a boolean value means you dont want to begin editing so it will not present Keyboard.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if(textfield == yourtextField)
{
[textfield resignFirstResponder];
// Show you custom picker here....
return NO;
}
}
and you need to implement the uitextfielddelegate in the controller.
and give assign the delegate to yourtextField.
Use textfield delegate.
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
return NO;
}
It looks like all of these answers take one approach, to simply deny the keyboard before it comes up. This prevents first responder status, which has many advantages.
One simple approach that allows you to maintain first responder status is to create an empty view and assign that to the inputView property on your input field. If you are using iOS 9 (or later?) you will also have to get rid of the inputAssistantItem objects as well.
UITextField *field = [[UITextField alloc] init];
field.inputView = self.emptyKeyboard.view;
UITextInputAssistantItem *aItem = [field inputAssistantItem];
aItem.leadingBarButtonGroups = #[];
aItem.trailingBarButtonGroups = #[];
Then if you want to control the field from an alternate view controller, you can do so by adding targets:
[field addTarget:self.numberPad action:#selector(editingBegan:) forControlEvents:UIControlEventEditingDidBegin];
[field addTarget:self.numberPad action:#selector(editingEnded:) forControlEvents:UIControlEventEditingDidEnd];
[field addTarget:self.numberPad action:#selector(fieldChanged:) forControlEvents:UIControlEventEditingChanged];
It is also possible to do this a lot more cleanly by subclassing UITextField.
Use the textField Delegate,
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
textField=nil;
return NO;
}
swift 3.0 version
First set the delegate for the text field
self.textfield.delegate = self
Then in an extension
extension ViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return false
}
}
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
// Here you can do for Specific text Field by
if (textField==(the text field you don't want to show keyboard)) {
NSLog(#"don't show keyboard");
return NO;
}
else {
return YES;
}
}
Swift 3/4
Add:- UITextFieldDelegate in your class.
Add:- self.textField.delegate = self In ViewDidLoad
last one just add this func -
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return false
}

Tap on UITextField's clear button hides keyboard instead of clearing text

In iPhone, I have a view which has a UITextField. When I tap on the clear button of UITextField's the keyboard dismissed instead of clearing the text in the UITextField. On an iPad it is working correctly. What can I do to fix this?
Just clear the field, resignFirstResponder (if you want to hide keyboard) and return NO/false
Note: set Attributes inspector property of UITextField
Clear Button -> Appears while editing
so it will display the clear button while editing in the text field.
// Objective-C
-(BOOL)textFieldShouldClear:(UITextField *)textField
{
textField.text = #"";
[textField resignFirstResponder];
return NO;
}
// Swift
func textFieldShouldClear(textField: UITextField) -> Bool {
textField.text = ""
textField.resignFirstResponder()
return false
}
Try this code after you attach delegate of uitextifield
-(BOOL)textFieldShouldClear:(UITextField *)textField
{
return true;
}
First, check all the code blocks that related to your UITextField (especially the code yourTextField.hidden = YES;)
Put break points and analyze every UITextField delegates that you implemented.
(textFieldDidEndEditing,textFieldShouldEndEditing,textFieldShouldReturn.etc.)
OR
Implement the textFieldShouldClear delegate and write the code here to visible and clear your UITextField
To do this, you have to set the clearButtonMode as below,
yourTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
yourTextField.delegate = self;
//For active keyboard again
[yourTextField becomeFirstResponder];
Then implement the textFieldShouldClear delegate
YourClass.h
#interface className : UIViewController <UITextFieldDelegate>
YourClass.m
-(BOOL)textFieldShouldClear:(UITextField *)textField {
yourTextField.hidden = NO;
yourTextField.text = #"";
return YES;
}
Just make sure U've given these two
editingTextField.delegate = self;
editingTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
TextFieldShouldClear is needed only if you need to do some customizations :-)
Are you doing some thing in this method?
Maybe you are are calling resignFirstResponder in this delegate method, thats why the keyboard is getting dismissed.
Please go through the delegate methods, and check what u r doing exactly.
This issue happened also if you have
yourTextField.clearButtonMode = UITextFieldViewModeNever;
Check this line and delete it or change view mode..

How do I dismiss the iOS keyboard?

I have a UITextfield that i'd like to dismiss the keyboard for. I can't seem to make the keyboard go away no matter what code i use.
If you have multiple text fields and don't know which one is first responder (or you simply don't have access to the text fields from wherever you are writing this code) you can call endEditing: on the parent view containing the text fields.
In a view controller's method, it would look like this:
[self.view endEditing:YES];
The parameter forces the text field to resign first responder status. If you were using a delegate to perform validation and wanted to stop everything until the text field's contents were valid, you could also code it like this:
BOOL didEndEditing = [self.view endEditing:NO];
if (didEndEditing) {
// on to the next thing...
} else {
// text field must have said to first responder status: "never wanna give you up, never wanna let you down"
}
The endEditing: method is much better than telling individual text fields to resignFirstResponder, but for some reason I never even found out about it until recently.
[myTextField resignFirstResponder]
Here, second paragraph in the Showing and Hiding the Keyboard section.
I've discovered a case where endEditing and resignFirstResponder fail. This has worked for me in those cases.
ObjC
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
[self setEditing:NO];
Swift
UIApplication.shared.sendAction(#selector(resignFirstResponder), to: nil, from: nil, for: nil)
There are cases where no text field is the first responder but the keyboard is on screen.
In these cases, the above methods fail to dismiss the keyboard.
One example of how to get there:
push the ABPersonViewController on screen programmatically; open any contact;
touch the "note" field (which becomes first responder and fires up the keyboard);
swipe left on any other field to make the "delete" button appear;
by this point you have no first responder among the text fields (just check programmatically) but the keyboard is still there. Calling [view endEditing:YES] does nothing.
In this case you also need to ask the view controller to exit the editing mode:
[viewController setEditing:NO animated:YES];
I suggest you add and action on your header file:
-(IBAction)removeKeyboard;
And in the implementation, write something like this:
-(IBAction)removeKeyboard
{
[self.textfield resignFirstResponder];
}
In the NIB file, connect from the UITextFiled to the File's Owner on the option DidEndOnExit. That way, when you press return, the keyboard will disappear.
Hope it helps!
In your view controller YourViewController.h file, make sure you implement UITextFieldDelegate protocol :
#interface YourViewController : <UITextFieldDelegate>
#end
Then, in YourViewController.m file, implement the following instance method:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.yourTextField1 resignFirstResponder];
[self.yourTextField2 resignFirstResponder];
...
[self.yourTextFieldn resignFirstResponder];
}
To resign any text field in the app
UIApplication.shared.keyWindow?.endEditing(true)
This approach is clean and guarantied to work because the keyWindow is, by definition, the root view of all possible views displaying a keyboard (source):
The key window receives keyboard and other non-touch related events. Only one window at a time may be the key window.
This will resign one particular text field
// Swift
TextField.resignFirstResponder()
// Objective C
[TextField resignFirstResponder];
To resign any text field use below code
// Swift
self.view!.endEditing(true)
// Objective C
[self.view endEditing:YES];
as a last resort 💩
let dummyTextView = UITextView(frame: .zero)
view.addSubview(dummyTextView)
dummyTextView.becomeFirstResponder()
dummyTextView.resignFirstResponder()
dummyTextView.removeFromSuperview()
If you don't know which textField is the first responder you can find it. I use this function:
UIView *resignFirstResponder(UIView *theView)
{
if([theView isFirstResponder])
{
[theView resignFirstResponder];
return theView;
}
for(UIView *subview in theView.subviews)
{
UIView *result = resignFirstResponder(subview);
if(result) return result;
}
return nil;
}
Then in your code call:
UIView *resigned = resignFirstResponder([UIScreen mainScreen]);
You just replace yourTextFieldName with, you guessed it! your textfield. This will close the keyboard.
[yourTextFieldName resignFirstResponder];
-(void)methodName
{
[textFieldName resignFirstResponder];
}
call this method (methodName) with didEndOnExit
For Swift 3
You can hide the keyboard like this:
textField.resignFirstResponder()
If you want to hide the keyboard when the user press the "intro" button, you have to implement the following UITextFieldDelegate method:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
// your code
[textField reloadInputViews];
}
3 Simple & Swift steps
Add UITextFieldDelegate to your class as below:
class RegisterVC: UIViewController, UITextFieldDelegate {
//class implementation
}
in class implementation, add the delegate function textFieldShouldEndEditing::
internal func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return true
}
and as a last step, set your UITextField(s) delegate(s) to self, in somewhere appropriate. For example, inside the viewDidLoad function:
override func viewDidLoad(){
super.viewDidLoad()
myTextField1.delegate = self
myTextField2.delegate = self
..
..
}
Now, whenever user hits the return key, keyboard will dismiss.
I prepared an example snippet too, you can check it from here.
Set up the "Did End On Exit" event in Xcode (right click on your text field).
Realize this method:
-(IBAction) closeKeyboard:(id) sender {
[_txtField resignFirstResponder];
}

Resources