Return key pressed on UITextView - ios

I have a UITextView which I am using as text entry in a chat application. When the user presses return then I want to do some action e.g. save the chat message.
I haven't been able to find a solution that allows me to do this (lots for TextFields but not for TextView).
Here is the solution I am trying at the moment, which seems to be the most obvious I can find, but it isnt working, in debug I see that the method isn't touched:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:#"\n"]) {
NSLog(#"Return pressed");
} else {
NSLog(#"Other pressed");
}
return YES;
}
In my chat .h file:
#interface ChatTableViewController : UITableViewController <UITextViewDelegate>
and viewdidload .m file
- (void)viewDidLoad {
[super viewDidLoad];
enterText.delegate = self;
// more
}
Incidentally if there is a better field to use as input in a chat program than UITextView please let me know.
Many thanks

... but it isn't working, in debug I see that the method isn't touched
Your problem obviously is that the delegate method is not called. Fix the delegate and use the code you already have: it's good.

Try this way. No need to go with delegates.
Add this event responder at where you initialize the textView.
[theTextView addTarget:self
action:#selector(targetMethodToPerformCustomOperation)
forControlEvents:UIControlEventEditingDidEndOnExit];

Related

Keyboard is not hiding when click on second textField [duplicate]

This question already has an answer here:
Issue related to textfield and datepicker
(1 answer)
Closed 6 years ago.
I have two textFields. On click, the first one is showing a qwerty keyboard and the second one is showing picker.
My issue is when I'm clicking on my first textField, the keyboard is showing properly. Now, After I type anything, I directly want to click on the second textField which is the showing picker and the keyboard should disappear, but the keyboard is still there.
I want to hide the keyboard as soon as I click on the second textField.
There are two ways to achieve this -
First: Use the UITextFieldDelegate.
For that-
List the UITextFieldDelegate in your view Controller's protocol list like,
#interface ViewController : UIViewController <UITextFieldDelegate>
Then make your ViewController conform to the Textfield's delegate to implement the methods like textFieldDidBeginEditing, and textFieldDidEndEditing:
So, go to the viewDidLoad method and conform to the UITextField's protocol like-
- (void)viewDidLoad
{
[super viewDidLoad];
self.firstTextField.delegate = self; //assuming your first textfield's
//name is firstTextField
//Also give your first textfield a tag to identify later
self.firstTextField.tag = 1;
}
Now, you are set to implement the delegate methods. But, to achieve your target first, you need to take a UITextField instance to know when you are typing in the firstTextField. So, declare a property of UITextField type. Do that in the interface file like-
#property(nonatomic, strong) UITextField *currentTextField;
Now in the textFieldDidBeginEditing delegate method, assign the firstTextField instance to the currentTextField when you start typing in it like-
- (void)textFieldDidBeginEditing:(UITextField *)textField{
if(textField.tag == 1){
self.currentTextField = textField;
}
}
After that in the textFieldDidEndEditing method, check if it is the current textfield from which you are coming out and dismiss the keyboard like-
-(void)textFieldDidEndEditing:(UITextField *)textField{
if(textField.tag == 1){
[self.currentTextField resignFirstResponder];
}
return YES;
}
Second: You can use UIResponder. As ViewControllers inherit from UIResponder, you just override the method- touchesBegan:withEvent method, something like-
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.view endEditing:YES];// this will do the trick
}
In this case, when you click out side the textField, the keyboard should automatically disappear.
use UITextFieldDelegate set delegate to self in ViewDidLoad and then put this code
func textFieldDidEndEditing(textField: UITextField)
{
yourtextfieldname.resignFirstResponder()
return true
}
Step 1 :- Code for ViewController.h :-
//add UITextFieldDelegate just after UIViewController
#interface ViewController : UIViewController<UITextFieldDelegate>
Step 2 :- Code For ViewController.m :-
//inside viewdidLoad write following code, where txtInput is outlet for input text field
_txtInput.delegate = self;
// Last Thing write following code just above #end
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
//that's it use this very simple way

UITextView - Max line breaks

When using a UITextView to gather user input in Objective-C, how can I limit the user from trying to do more than one line break at a time?
So, this would be fine:
This is my text.
Here is some more text.
But this would not be fine:
This is my text.
Here is some more text way down here.
In your ViewController.h add UITextViewDelegate:
YourViewController : UIViewController <UITextViewDelegate>
and in the ViewController.m implement the method textView:shouldChangeTextInRange:replacementText: this way:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:#"\n"]) {
NSMutableString *futureString = [textView.text mutableCopy];
[futureString insertString:text atIndex:range.location];
NSRange rangeOflineBreaks = [futureString rangeOfString:#"\n\n\n"];
if (rangeOflineBreaks.location != NSNotFound) {
return NO;
}
}
return YES;
}
This will be executed every time before the user wants to add some text to the textView and wont let him add another line break if it notices that he wants to add a line break and it finds, after trying adding it (that's why the futureString name) a triple line break mode. In that case he wont let the user add another line break.
Try it out, it should work :)
PS: Don't forget to set your textView delegate the viewController in the viewDidLoad (yourTextView.delegate = self)

Dismiss the keyboard with MULTIPLE UITextFields?

Is it possible to dismiss the keyboard when you have MULTIPLE UITextFields ? If so how ?
As a side note, do I have to dismiss the keyboard for Each and Every field or can it be done globally ? Oh and it would be super cool if I don't have to touch the DONE button, I'd ideally like a solution that where the user touches anything BUT the field in question and the keyboard automagically disappears...
Oh and if you'd be so kind step by step instructions.
I should have added that I have a method already to resign the keyboard....
However, it only runs when my form is submitted! (see method below)
My question is how to the keyboard to hide/dismiss without having to jump thru so many damned hoops! You'd figure after 6 years, a mature operating system would have a way to GLOBALLY hide the keyboard....NOT!
Ok, enough whining....
- (void)hideKeyboard {
[self.dancePlace resignFirstResponder];
[self.danceGate resignFirstResponder];
[self.danceTerminal resignFirstResponder];
[self.danceText resignFirstResponder];
[self.danceDate resignFirstResponder];
[self.danceStyle resignFirstResponder];
[self.danceTimeOut resignFirstResponder];
}
And this is called when my button is submitted....
- (IBAction)addListingPressed:(id)sender {
// NSLog(#"BUTTON PRESSED");
[self hideKeyboard];
[self valuesAdded];
}
My question, assuming anyone can answer this...and I suspect not, is there a way to globally hide the keyboard if the following conditions are MET: 1.) the user taps OUT of any one of the existing fields, 2.) presses anywhere else on the screen. 3.) Is no more than a line or two in the existing viewcontroller.m file. 4.) I don't have to add a confusing button on the viewcontroller. (any time I have to add outlets, the damned thing is crashing on me...and then nastiness happens, and really...remember I am JUST a beginner, and its very confusing to read that I have to place this here and that there...oy. Simple folks, simple. I'm not looking for elegant solution, just so that it works.
I have a super class that all my view controllers inherit from. In that class I have this code.
MySuperViewController.h
#import <UIKit/UIKit.h>
#interface MySuperViewController : UIViewController
#property(strong, nonatomic) UITapGestureRecognizer *backgroundTapGestureRecognizer;
#end
MySuperViewController.m
- (void)viewDidLoad{
//add a tap gesture recognizer to capture all tap events
//this will include tap events when a user clicks off of a textfield
self.backgroundTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onBackgroundTap:)];
self.backgroundTapGestureRecognizer.numberOfTapsRequired = 1;
self.backgroundTapGestureRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:self.backgroundTapGestureRecognizer];
}
- (void)onBackgroundTap:(id)sender{
//when the tap gesture recognizer gets an event, it calls endEditing on the view controller's view
//this should dismiss the keyboard
[[self view] endEditing:YES];
}
I have the UITapGestureRecognizer as a public property, so I can override it if I need to.
subclass
MyViewController.h
#import <UIKit/UIKit.h>
#import "MySuperViewController.h"
#interface MyViewController : MySuperViewController<UIGestureRecognizerDelegate>
#end
MyViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//You don't always want the keyboard to be dismissed, so you tie into the gesture recognizer's delegate method
//By doing this, you can stop the endEditing call from being made
[self.backgroundTapGestureRecognizer setDelegate:self];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
//touch.view is the view that recieved the touch
//if this view is another textfield or maybe a button, you can return NO and the endEditing call won't be made
if (touch.view == self.myViewThatShouldNotBeBlocked) {
return NO;
}
//if you want the gesture recognizer to accept the event, return yest
return YES;
}
I uploaded an example project to github.
https://github.com/JeffRegan/KeyboardBeGone
RDVKeyboardAvoiding is a scroll view with a tap gesture recognizer, designed for multiple textViews/textFields. It keeps track of the active view and removes a lot of boilerplate code.
tap anywhere outside the textField .. it will hide it..
[self.view endEditing:YES];
There are couple of other ways to do it.
[myEditField resignFirstResponder];
[myEditField endEditing];
[parentView endEditing];
If you dont wont to do so many things and simply want to dismiss keyboard than give iboutlet to each of your text filed to following method..
-(IBAction)hidekeyboard:(id)sender
{
[sender resignFirstResponder];
}
Yes, you only have to dismiss it for the one that is currently being edited.
In order to know which one is being edited, you can check the -(BOOL)isFirstResponder property, which will return YES if it is the first responder (the one being edited) or NO if it is not. Once you know which one is the first responder you can call -(void)resignFirstResponder on that one to get rid of the keyboard.
For example, if you have a method called -(void)aMethod that you want to dismiss the current view controller and you have an array of textViews called textArray, you could do a little loop such as:
-(void)aMethod {
for (UITextField *text in self.textArray) {
if ([text isFirstResponder]) [text resignFirstResponder];
return;
}
}
This way, you can have a variable number of textFields and it will still work.
If you only have one or two textFields and you do not want to create an Array object, you could do (assuming the fields are named text1 and text2:
-(void)aMethod {
if ([text1 isFirstResponder]) [text1 resignFirstResponder];
else if([text2 isFirstResponder]) [text2 resignFirstResponder];
}
Also, to make things easier for the future you could create a category method for UIView (which is what I do) to get the current first responder if it exists as a subview of that view:
#implementation UIView (GetFirstResponder)
- (UIView *)getFirstResponder {
if ([self isFirstResponder]) return self;
else {
for (UIView *subview in self.subviews) {
UIView *firstResponder = [subview getFirstResponder];
if (firstResponder) return firstResponder;
}
}
return nil;
}
You can put this method on the top of any file that you want to call it from, or create a separate file for it and import it.
Once you have this method, you can call:
- (void)aMethod {
UIView *view = [self.view getFirstResponder];
if (view) [view resignFirstResponder];
}
[superview endEditing:YES]; // superview can be the view controller's view property.

Is it possible to make UITextView inactive (editable = NO) without hiding the keyboard?

I use a UITextView in a subclass of UIViewController which I've named FacebookPostViewController. So the purpose of the text view is to allow the user to review the content of a prepared post, possibly make changes to it, and then decide if the text from the text view should be posted to Facebook, or not.
I've set the returnKeyType property of the UITextView to UIReturnKeySend, because I want to use it to actually post to Facebook. Canceling is possible via a back button in the NavBar.
So the FacebookPostViewController is delegate of the UITextView, and in the textView:shouldChangeTextInRange:replacementText: delegate method, I check for a newline insertion, and then call my sendTextToFacebook method:
- (void)sendTextToFacebook;
{
// prepare sending
NSMutableDictionary* sendParameters = [NSMutableDictionary dictionaryWithObjectsAndKeys:self.textView.text, #"message", nil];
// request sending to own feed
[[FacebookController sharedFacebookController].facebook requestWithGraphPath:#"me/feed" andParams:sendParameters andHttpMethod:#"POST" andDelegate:self];
}
As you can see, I specify self as the FBRequestDelegate, so I also implement
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error;
- (void)request:(FBRequest *)request didLoad:(id)result;
where I give the user feedback if posting was successfull or not.
However, I've figured that it takes some time until the Facebook server replies, so I want to have the complete UI inactive until one of the two delegate methods is called. Therefore, I've implemented the following method:
- (void)disableUserInterface;
{
self.textView.editable = NO;
self.loginButton.enabled = NO;
self.logoutButton.enabled = NO;
[self.navigationItem setHidesBackButton:YES animated:YES];
[self.spinner startAnimating];
self.spinner.hidden = NO;
}
My problem is that calling self.textView.editable = NO will hide the keyboard, which looks stupid. If I remove that line, the keyboard won't disappear, but the user can change the textView content while the text is sent to Facebook. However, as posting is already initiated. these changes won't actually appear in Facebook.
I thought I could simply implement another UITextViewDelegate method:
- (BOOL)textViewShouldEndEditing:(UITextView *)textView
{
return NO;
}
The UI result is then exactly how I'd like to have it. However, this will lead to a EXC_BAD_ACCESS crash later when I've popped the FacebookPostViewController and then push another view to the UINavigationController. Ther error then is
*** -[UITextView isKindOfClass:]: message sent to deallocated instance 0x20c650
So, has anybody an idea how I can solve my problem to make the UITextView uneditable, but prevent the keyboard from disappearing?!?
Okay, thanks to the comment from David H above, I've figured out a solution. But here are the most important insights:
When I pop my FacebookPostViewController from the UINavigationController, the UITextView is asked automatically by the system to resign being the first responder.
You should never let textViewShouldEndEditing: always return NO, because this will always prevent the keyboard to hide, which will then lead to the crash described above, as soon as the UITextView is deallocated.
The solution is to prevent the keyboard from disappearing only in case we are still waiting for a response from the Facebook server. Here are a couple of lines of codes that I've added, which do exactly this:
#interface FacebookPostViewController ()
// ...
#property (nonatomic, assign, getter = isWaitingForFBResponse) BOOL waitingForFBResponse;
// ...
#end
#implementation FacebookPostViewController
// ...
#synthesize waitingForFBResponse = _waitingForFBResponse;
- (id)init;
{
self = [super init];
if (self) {
// set default values
self.waitingForFBResponse = NO;
// ...
}
return self;
}
#pragma mark -
#pragma mark UITextViewDelegate methods
- (BOOL)textViewShouldEndEditing:(UITextView *)textView
{
if ([self isWaitingForFBResponse]) {
return NO;
} else {
return YES;
}
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
// Did user press 'return' (Send) key?
if([text isEqualToString:#"\n"]) {
// enter waiting state
self.waitingForFBResponse = YES;
// disable the user interface
[self disableUserInterface];
FacebookController *fbController = [FacebookController sharedFacebookController];
if ([fbController.facebook isSessionValid] == YES) {
// Post to Facebook!
[self sendTextToFacebook];
} else {
// we need to login first
[self loginToFacebook];
// remember that user wants to post to Facebook
self.sendTextAfterFBLogin = YES;
}
// Don't allow textView to insert a LF into the text property
return NO;
}
// allow all other edits
return YES;
}
#pragma mark -
#pragma mark FBRequestDelegate methods
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error;
{
// request failed, so display error status overlay.
[[FeedbackController sharedFeedbackController] displayStatusOverlayWithString:NSLocalizedString(#"StatusMessage_Failure", nil)];
// leave waiting state
self.waitingForFBResponse = NO;
// enable UI again
[self enableUserInterface];
}
- (void)request:(FBRequest *)request didLoad:(id)result;
{
// request succeeded, give user appropriate feedback.
[[FeedbackController sharedFeedbackController] displayStatusOverlayWithString:NSLocalizedString(#"StatusMessage_Sent", nil)];
// leave waiting state
self.waitingForFBResponse = NO;
// pop the view, as the user accomplished his goal.
[self.navigationController popViewControllerAnimated:YES];
}
#end

Objective-C, how to Generally resignFirstResponder?

(my boss says) that I have to implement a "Done" button on a navBar so that the various items in the view (that contain an edit box) will dismiss their keyboard (if they were in focus).
It seems that I must iterate through all items and then call resignFirstResponder on each on the off-chance that one of them is in focus? This seems a bit messy (and hard to maintain if e.g. someone else adds more items in future) - is there a better way to do it?
I have found it!
Thanks to this
I discovered that all I need do is this:-
-(void) done {
[[self.tableView superview] endEditing:YES];
}
// also [self.view endEditing:YES]; works fine
[remark]
Also I learn how to do the equivalent of an "eventFilter" to stop UITableViewController from swallowing background touch events by intercepting them before they get there - from the same, wonderful post on that thread - see "DismissableUITableView".
[end of remark]
You don't have to iterate through the controls since only one can be first responder at the moment.
This will reset the responder to the Window itself:
[[self window] makeFirstResponder:nil]
One solution is to use a currentTextField Object,
In .h file have an instance variable as
UITextField *currentTextField;
Now in .m file.
Note : Dont forget to set the delegates of all the textField to this class
- (void)textViewDidBeginEditing:(UITextView *)textView
{
currentTextField = textField;
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
currentTextField = nil;
}
Now in your button action method
-(IBAction)buttonTap
{
if([currentTextField isFirstResponder])
[currentTextField resignFirstResponder];
}
This avoids iterating through all the text field.
I think best way to handle it by searching all subviews of main view with recursive function, check example below
- (BOOL)findAndResignFirstResponder {
if (self.isFirstResponder) {
[self resignFirstResponder];
return YES;
}
for (UIView *subView in self.subviews) {
if ([subView findAndResignFirstResponder]) {
return YES;
}
}
return NO;
}
and also you can put this method to your utility class and can use from tap gesture. All you have to do is simply adding to gesture to view.
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(hideEverything)];
[self.tableView addGestureRecognizer:gestureRecognizer];
and than you can call hideEverything method;
- (void) hideKeyboard {
[self.view findAndResignFirstResponder];
...
...
}

Resources