I am having a hell of a time fixing a bug, which currently is only occurring when I run my app on the simulator. Essentially, I have a UITextView that I am trying to send the message endEditing to. If I send the message while the user is editing the textview (forced or otherwise) I get back YES. If however, the user has not yet begun editing the textview and I send the message endEditing:YES, I get back NO. Should this even be possible? Shouldn't endEditing:YES always force the view to end editing?
Additional Details: I have tried setting the owning class to be the uitextviews delegate, but even then it doesn't look like the shouldEndEditing method even gets called.
UPDATE: It does not seem that this is normal behavior (that the method should return no if the text field is not currently the first responder).
I created a simple test:
AppDelegate.h
#import <UIKit/UIKit.h>
#interface DRAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (assign) IBOutlet UITextView *textView;
#property (assign) IBOutlet UIButton *endButton;
#property (assign) IBOutlet UILabel *results;
-(IBAction)tapEndEditingButton:(id)sender;
#end
and AppDelegate.m
-(void)tapEndEditingButton:(id)sender
{
BOOL didEndEditing = [self.window endEditing:YES];
NSString *result = (didEndEditing) ? #"YES" : #"NO";
_results.text = result;
}
Regardless of whether the textfield has focus, and regardless of whether I set the force parameter of endEditing to YES or NO, didEndEditing gets set to YES.
Check Apple Doc on UIView
endEditing:
Causes the view (or one of its embedded text fields) to resign the first responder status.
...
Return Value
YES if the view resigned the first responder status or NO if it did
not.
Discussion
This method looks at the current view and its subview hierarchy for
the text field that is currently the first responder. If it finds one,
it asks that text field to resign as first responder. If the force
parameter is set to YES, the text field is never even asked; it is
forced to resign.
So... When you said:
If however, the user has not yet begun editing the textview and I send the message endEditing:YES, I get back NO
It's perfectly fine because in your scenario, since there is no firstResponder to resign, calling -endEditing: will return NO and doesn't harm the performance (isn't a bug either, imho)
To answer the essence of the question:
[UIView endEditing:YES/NO]; will return NO if the specified UIView object was not the firstResponder.
Example:
-(void)testEndEditing
{
UIButton *btnTest = [UIButton buttonWithType:UIButtonTypeCustom];
[btnTest setFrame:CGRectMake(0, 130, 320, 44)];
[btnTest setBackgroundColor:[UIColor blueColor]];
[btnTest addTarget:self action:#selector(myEndEditing:)
forControlEvents:UIControlEventTouchUpInside];
[btnTest setTitle:#"End Editing" forState:UIControlStateNormal];
[self.view addSubview:btnTest];
UITextField *txtFTest = [[UITextField alloc] init];
[txtFTest setFrame:CGRectMake(0, 50, 320, 30)];
[txtFTest setBackgroundColor:[UIColor orangeColor]];
[txtFTest setText:#"textField"];
[self.view addSubview:txtFTest];
/*
globally declare "UITextView *txtVTest;"
*/
txtVTest = [[UITextView alloc] init];
[txtVTest setFrame:CGRectMake(0, 80, 320, 50)];
[txtVTest setBackgroundColor:[UIColor redColor]];
[txtVTest setText:#"textView"];
[self.view addSubview:txtVTest];
//make UITextField object the firstResponder
[txtFTest becomeFirstResponder];
}
-(void)myEndEditing:(UIButton *)sender
{
//test endEditing method on UITextView object
BOOL isWhat = [txtVTest endEditing:YES];
NSLog(#"%i",isWhat);
}
PS: If neither the textField nor the textView is the firstResponder then it returns YES (dunno why but i'll check)
Related
I'm finding a weird behaviour when I add a leftView (or righView) to a UITextField, then return NO to textFieldShouldEndEditing and dealocate the view controller, when trying to access the view controller again (a new instance, one would think), some times I get an EXC_BAD_ACCESS, and other times the text field is simply unresponsive.
I have no idea why this is happening, so I'm posting below the steps to reproduce.
Start with a very simple project with a Navigation Controller and 2 View Controllers, the first one has just a button to go to the second one, and the second one just have a UITextField.
The UIViewController of this second controller is the UITextField delegate:
#interface ViewController () <UITextFieldDelegate>
#property (nonatomic, weak) IBOutlet UITextField *textField;
#end
#implementation ViewController
- (void)dealloc {
NSLog(#"Dealocating View Controller");
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
textField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
textField.leftViewMode = UITextFieldViewModeAlways;
return NO;
}
#end
Now go to the second view controller, you can edit the text field with no problem, but cannot stop editing (as expected), but you CAN go back to the first view controller (at this point the second view controller is dealocated, as testified on the console).
But now if you again go into the second view controller (a new instance, I would expect, since the other one was deallocated), you'll find you can no longer interact with the text field.
Why is this happening? What am I doing wrong?
Notes
If I return YES to textFieldShouldEndEditing, everything works fine; but I want to keep the user on the text field until they enter a valid text.
If I don't add a leftView, everything works fine; but I want to give the user a visual feedback as to why the text field is not allowing them to leave.
Edit in response to Teja Nandamuri
I updated the view controller code to try and make sure the textField resigned first responder before the view controller get deallocated, but even with these changes, upon reentering the textField is unresponsive:
#interface ViewController () <UITextFieldDelegate>
#property (nonatomic, weak) IBOutlet UITextField *textField;
#end
#implementation ViewController
- (void)dealloc {
NSLog(#"Dealocating View Controller");
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
if (textField == self.textField && ![textField.text isEqualToString:#"bien"]) {
textField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
textField.leftViewMode = UITextFieldViewModeAlways;
return NO;
}
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ([textField isFirstResponder]) {
[textField resignFirstResponder];
}
}
#end
Please note that even you return NO, the UIKit will end the editing while leaving from the second controller.
Consider this case, you return NO to prevent the user moving to another control, this works as long as you stay in the same controller. If you move away from it, everything gets deallocated and when you comeback again, the textField will get instantiated again, and UIkit thinks the new textfield is a different one and will not allow the new textField to becomeFirstResponder, since the old one did not resign its responder. So you cannot edit the new textField.
Adding the leftView will not make a difference here.The only thing matter is the delegate method to return YES .Because you cannot control UIkit, and it expects you to return YES, so modify your delegate method:
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
if (your-condition) {
textField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
textField.leftViewMode = UITextFieldViewModeAlways;
return NO;
}
return YES;
}
This case works as long as you are in the second View controller. Anyway the problem still exists if you leave from the second controller, and the delegate method returns NO, you should stop user from going back to first view controller if your text field condition is not met.
Here's the setup:
SNController is an NSObject and it has inside:
UIView
UITextField
UISegmentedControl
UILabel
UIButton
The view is the same as the view inside the ViewController.
One can be visible at the time. When the UITextField is visible (alpha = 1.0)
all the others are invisible (alpha = 0.0).
(1) I perform an animation and I want the text field:
UITextField *textField;
to become the first responder while the animation happens:
[textField becomeFirstResponder];
[UIView animateWithDuration: ...];
Unfortunately it doesn't work.
I've logged the text field and it showed that it cannot become the first responder and I have no idea why...
I've tried another two methods but none of them worked so far:
(2) A complicated way to communicate with the ViewController from the SNController:
Inside the SNController.h/.m:
#property (nonatomic, copy) void(^eventCallBack)();
if (self.eventCallBack) {
self.eventCallBack();
}
Inside the ViewController.m:
__weak ViewController *self_ = self;
self.restoration.controller.eventCallBack = ^(){
[self_.restoration.controller.textField becomeFirstResponder];
};
(3) I also tried these methods from inside the SNController:
[self.textField performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
[self.textField performSelectorOnMainThread:#selector(becomeFirstResponder) withObject:nil waitUntilDone:YES];
It just won't give up, it doesn't become the first responder no matter what.
Any ideas?
I realised there was a mistake. There isn't a UITextField inside the SNController but a UIView subclass named SNTextField.
I implemented this method where the UITextField was initialised:
- (void)activateKeyboard
{
[self.textField becomeFirstResponder];
}
and it worked!
Let's say that I wish to approach a certain UIControl in a certain method that it triggers - I can send the method a pointer to the UIControl using "sender". but then, for some reason, I cannot approach sender's properties directly and have to use the setX:forState: methods. If I approach the properties directly, I get no error or warning; it simply does nothing...
Here's an example:
h. file...
#interface MYViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIButton *dButton; //connected to a UIButton in IB
-(IBAction)dButtonClick:(UIButton*)sender; //connected to the same UIButton in IB
#end
then...
m. file...
- (void)viewDidLoad
{
[super viewDidLoad];
self.dButton.titleLabel.textColor = [UIColor greenColor]; //this is working...
}
-(IBAction)dButtonClick:(UIButton*)sender
{
sender.titleLabel.textColor = [UIColor redColor]; //this is not working...
self.dButton.titleLabel.textColor = [UIColor redColor]; //this is also not working...
[sender setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; //only this is working.
//But why?!?!?
}
I tried to search for some info about the way these items work, but couldn't find anything that explains the logic behind it clear enough.
Any help would be... well... helpful...
This:
sender.titleLabel.textColor
And this:
[sender setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
Are different not just because of the dot notation but also because of the state. The button has different colours for each state so any colour set directly will be overridden by the colour for the state.
This line:
self.dButton.titleLabel.textColor = [UIColor redColor];
Could be the same issue or you may just not have connected the outlet.
I'd like to dismiss the keyboard with a text field using
- (BOOL)textFieldShouldReturn:(UITextField *)textField {, but I need to do the same with a text view, so I'll use - (BOOL)textViewShouldReturn:(UITextView *)textView {. Is it possible to put them togheter in the same AppDelegate?
Thank you.
Yes, you can have two delegates
#interface ViewController : UIViewController<UITextFieldDelegate,UITextViewDelegate>
Unfortunately
The protocol UITextViewDelegate does not have, something like this.
- (BOOL)textViewShouldReturn:(UITextView *)textView {
UITextViewDelegate Protocol,
EDIT 1 :
Button press event to hide the keyboard.
-(IBAction) yourButtonPressed:(id)sender;{
for(UIView *v in self.view.subviews){
if([v isKindOfClass:[UITextField class]] || [v isKindOfClass:[UITextView class]]){
if([v isFirstResponder]){
[v resignFirstResponder];
break;
}
}
}
}
I don't fully understand your question...
Basically you will do the following:
implement the UITextFieldDelegate and UITextViewDelegate in your
viewController
implement the methods of that delegates, you need/want
resignFirstResponder / endEditing of the textField / textView, where
ever you want
Just subscribe to the delegates from your viewController and make sure to set the delegate on those objects to your viewController.
.h
#interface YourViewController : UIViewController <UITextFieldDelegate, UITextViewDelegate>
.m
someTextField.delegate = self;
someTextView.delegate = self;
From the sound of it you just need to tie into the actions of the textField and textView. Create an IBAction and tie it to what you'd like. Then you can resignFirstResponder from that IBAction.
Use this action for both your textField and textView
- (IBAction)lowerTheText:(id)sender
{
[sender resignFirstResponder];
}
Yes, with a UITextView it's tricky. As I say in my book...
http://www.apeth.com/iOSBook/ch23.html#_uitextview
...on the iPad, the problem of dismissing the keyboard doesn't arise because the user can dismiss it with the button in the lower right corner of the keyboard. So this leaves only the iPhone. You will typically have an interface such that there is a Done button or similar. Look at how the Notes app solves this, for example.
The process itself is just the same: call endEditing: on the superview and whoever is first responder will cease being first responder and the keyboard will retire.
Yes, you can use textfield delegate and textview delegate in the same application.
TextField:
#interface ViewController : UIViewController<UITextFieldDelegate,UITextViewDelegate>
// This allocates the textfield and sets its frame (or) you can use interfaceBuild
UITextField *textField = [[UITextField alloc] initWithFrame:
CGRectMake(20, 50, 280, 30)];
textField.delegate=self;
// This method enables or disables the processing of return key
-(BOOL) textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder]; // this is event for hide keyboard.
return YES;
}
TextView:
// init
UITextView *textView = [[UITextView alloc] initWithFrame:
CGRectMake(20, 50, 280, 30)];
- (void)textViewDidChangeSelection:(UITextView *)textView
{
[textField resignFirstResponder]; // this is event for hide keyboard.
}
I am mocking up a quick demo of a project but am having a problem with a UITextField.
The behavior that we want is that when a user clicks on a button, there should be a custom view that appears with a UITextField and a UIButton in a custom view that overlays the main view.
I have a custom view called Searchview and the following in the Searchview.m. The problem is that when the textField is a property, it doesn't show but when it is a local variable, it does show. Can anybody help me with what is going on so that the UITextField shows? Is how I am doing this even the right idea (custom UIView or custom UIControl or a modal controller)? Finally, would setNeedsDisplay be appropriate here?
thx in advance
#interface Searchview()
#property (nonatomic, weak) UITextField *textField;
#end
- (void)drawRect:(CGRect)rect
{
// this doesn't work
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 120.0f, 25.0f)];
self.textField.returnKeyType = UIReturnKeyDone;
self.textField.placeholder = #"Writer";
self.textField.borderStyle=UITextBorderStyleBezel;
[self.textField addTarget:self
action:#selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEndOnExit];
[self addSubview: self.textField];
/* this works
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0f, 10.0f, 120, 25)];
textField.returnKeyType = UIReturnKeyDone;
textField.placeholder = #"Writer";
textField.borderStyle=UITextBorderStyleBezel;
[textField addTarget:self
action:#selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEndOnExit];
[self addSubview: textField];
*/
UIButton *mButton=[UIButton buttonWithType:UIButtonTypeRoundedRect];
mButton.frame=CGRectMake(200.0f,10.0f,100.0f,37.0f);
[mButton setTitle:#"search" forState:UIControlStateNormal];
[mButton addTarget:self action:#selector(showSearchController:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:mButton];
[self setNeedsDisplay];
}
As a property - not showing:
As a local variable - showing:
#property (nonatomic, strong) UITextField *textField;
change the weak to strong and change the self.textFiled to _textField to have a try
And make sure your textField property not be released
It's pretty simple when you think about it. ARC (approximately) converts the following code:
self.weakProp = [[Foo alloc] init];
to the equivalent of the following "manually reference-counted" code:
Foo * temp = [[Foo alloc] init];
self.weakProp = temp;
[temp release];
Nothing is retaining it, so it is released.
I can only think of two reasons to have assign/weak IBOutlets:
For an outlet in a VC, so it doesn't retain a subview when its view is set to nil (e.g. on a memory warning). This is less relevant in iOS 6.0 since views are not automatically released on a memory warning (so if you do it, you can release them all explicitly).
For a view where the outlet points to a superview (and would cause a retain cycle). This is quite rare.
In general, I prefer strong IBOutlets: They might keep objects alive for a little longer than necessary, but they are safer than assign and more efficient than weak. Just watch out for retain cycles!