resignFirstResponder causing EXC_BAD_ACCESS - iphone

I've got a UITextField on UITableViewCell, and a button on another cell.
I click on UITextField (keyboard appears).
UITextField has the following method called:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
NSLog(#"yes, it's being called");
owner.activeTextField = textField;
return YES;
};
Where owner.activeTextField is a (retain, nonatomic) property.
The problem
When the keyboard is visible I scroll the cell out of the view.
I then click a button that is on a different cell. The button calls:
[owner.activeTextField resignFirstResponder]
And that causes EXC_BAD_ACCESS.
Any idea? The cell is most definitely in the memory. My guess is that once it disappears it is removed from the view and one of it's properties (parent view?) becomes nil and that causes the said error..
Am I right?
TL;DR; How can I remove the keyboard (resign first responder) when UITextField is removed from the view?

Sometimes the problem can be another level deep... Check and make sure that the next object in the responder chain (the one that's subsequently receiving the becomeFirstResponder message) isn't garbage. Just a thought.

Have you checked owner.activeTextField to see if it has been deallocated / set to nil? Not sure if that would call a EXC_BAD_ACCESS but worth a try.
Also do you have any calls to NSNotificationCenter? I was struggling with something similar today which was causing an EXC_BAD_ACCESS on becomeFirstResponder, which was due to me calling [[NSNotificationCenter defaultCenter] removeObserver:keyboardObserver]; on the incorrect delegate.

A bit old, but since I just had the same problem with a legacy manual reference counting app, I'll give it a try. Note: this problem shouldn't happen with ARC anymore (and if it does, my solution most definitely doesn't fit there...).
What seems to happen is that:
the textfield is saved into the cell and is (possibly over)retained
when you scroll the cell with the textfield, the cell get's recycled (which is good), but the textfield is not (which is why the textfield is still in memory when the bug happens)
at that point, dismissing the keyboard correctly resign the first responder for the textfield, but as the call travels down the view hierarchy, it hits the cell, which is not in memory anymore.
a simple (and imo elegant) solution for the problem would be to
fix the over retention of uitextfield if any
subclass UITextField to resign first responder status before being deallocated
a single method,like this:
- (void) dealloc {
[self resignFirstResponder];
[super dealloc];
}
would be required, which will have the side-effect benefit of removing the keyboard as soon as the cell gets out of view.
Another solution (which is the one I chose, for various reasons) would be to manually retain and recycle the cell with the textfield until the table is deallocated.
i'm sure you already solved your problem, but I hope this will help someone else...

Related

tableView:didSelectRowAtIndexPath:]: message sent to deallocated instance 0xebba1b0

I understand the reason why message sent to deallocated instance 0xebba1b0 is called, it is because I am sending a message to an object which is no longer in memory.
So here's my scenario. I have a ZoomedViewController which has a UITableView in it. The UITableView has a custom UITableViewCell, which has an attributed label as a subview. When a link is pressed on the attributed label (which in turns triggers didSelectRowAtIndexPath) it delegates to my MainViewController and calls the method closeZoomedImageVC in MainViewController:
-(void) closeZoomedImageVC
{
[self.zoomedImageContainer_ removeFromParentViewController];
[self.zoomedImageContainer_.view removeFromSuperview];
}
the issue is that when that didSelectRowAtIndexPath is triggered, then zoomedImageContainer_ is already gone. How do I solve this then?
To illustrate the point better, basically when I do:
[self performSelector:#selector(closeZoomedImageVC) withObject:nil afterDelay:1.0];
this doesn't cause the crash anymore, but this is not a solution as it is hacky. What this does is it lets didSelectRowAtIndexPath to be executed first before it is deallocated.
Store a reference to your UITableView in ZoomedViewController:
#property (nonatomic, strong) IBOutlet UITableView *tableView;
Make sure to connect the outlet in Interface Builder. Now, when your zoomedImageContainer_.view is removed, it won't dealloc the UITableView until you release that reference as well.
You also need to store a strong reference to your ZoomedViewController in MainViewController, and only set that to nil after you have saved the selected row back in MainViewController.
When tapping on a cell in a table will invoke an animation to highlight selected cell, and it needs a short duration complete, so the best way is what you are doing, perform selector after a delay, I think 0.5 second is enough.
I found the solution to my self is to just set the allowSelection = NO in the tableView property. This will let the attributedLabel inside the UITableViewCell to have interaction but will disable didSelectRowAtIndexPath being called

Why calling [self becomeFirstResponder] caused so many problems?

For the past few extremely frustrating days of my life, I've been trying to figure out what's wrong with me code. In a certain page, if I put UITextViews or UITextFields or a MFMailComposer or a MessageComposer or anything with fields that require editing, the fields just wouldn't respond to touches. I couldn't edit anything when I ran the app. I couldn't edit text views or email fields or anything. I tried everything, but nothing worked. It turns out that on the main page (MainVC) that leads to the page where fields don't respond (GiftVC), in the viewDidAppear method (in the MainVC), I say: [self becomeFirstResponder];.
Now I'm not really sure why I put that there, but it turns out that commenting that line out fixes everything and makes all the fields and textviews and email composers and everything work just fine again.
I also have this in the MainVC page:
-(BOOL)canBecomeFirstResponder {
return YES;
}
and commenting that out fixes the problem as well.
The weird part is that even with the [self becomeFirstResponder] line, everything worked just fine in the new iOS 5 (simulator and device), but in iOS 4 (simulator and device), it wouldn't work at all with that line. Now that I've removed it, it works fine in both cases.
If you have the following in your UIViewController subclass
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.view.window) {
[self becomeFirstResponder];
}
}
then you probably intended to allow that subclass to handle motion events (shaking) or something similar. So that's probably why it's there.
If you weren't able to edit UITextFields then this subclass was probably becoming the first responder and not forwarding the event to the actual UITextField. When a UIViewController subclass calls overrides canBecomeFirstResponder to return YES and makes them self the first responder (ie [self becomeFirstResponder], if you want don't want that custom class to handle the touch events for the UITextField, then you should override the nextResponder method.
An example from my own product -- Essentially I have a UIViewController subclass that does two things: 1) it handles shake events and 2) it displays another view modally when some button is tapped. On the modal view there are some UITextFields. To allow my UIViewController subclass to forward the touch events to my modal view, I added the following:
- (UIResponder *)nextResponder
{
if (!self.view.window) {
// If the modal view is being displayed, forward events to it.
return self.modalViewController;
} else {
// Allow the superclass to handle event.
return [super nextResponder];
}
}
This will work on iOS 4 and 5, with either sdk.
Now, in your case you obviously didn't remember adding the code to become first responder in the first place, so you don't need the above hooks. However, it's good to know for the future.
Back to your actual question -- once you updated your SDK to 5, why wouldn't things work on iOS 4, but they would work on iOS 5? iOS 5 is doing some of the event forwarding for you which is why it works there. It should have never worked on iOS 4 in the beginning. Apple fixed some bugs that allowed it to work on 4, which is why it no longer works on 4.
I know the question had already accepted an accepted answer; I just wanted to clear up any confusion out there.
Check if MainVC has a method called canResignFirstResponder that returns NO (at least sometimes). If so, then once it becomes first responder, it won't let anything else become first responder, until it returns YES from that method. (All the UITextViews, etc. have to become first responder to be edited.)
Actually just look everywhere in all your code for canResignFirstResponder, in case it's in a superclass or something.
Otherwise the only thing that would stop the text fields and views from being editable would probably be if they got set userInteractionEnabled = NO, but since it hinges on the becomeFirstResponder statement, it is more likely to do with canResignFirstResponder.
In iOS 4, a subclass must override canBecomeFirstResponder in order to be able to become first responder. Maybe this is different for iOS 5 or it's a bug.
Try this,
Make sure you have added the uiTextViewDelegate and
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView{
NSLog(#"textViewShouldBeginEditing:");
return YES;
}

iPhone SDK: resignFirstResponder not working

For some reason, resignFirstResponder is not working. I am not sure why? I have tried to call it from textFieldDidEndEditing and nothing happens. A NIB is being used and each's delegate is pointing to files owner.
What needs to be done to get the keyboard to dismiss?
Thanks.
Don't use -textFieldDidEndEditing. That's called after the text field resigns firstResponder status, which is what you're trying to use it as a hook to make happen. Cart before horse, chicken-and-egg kind of problem.
Instead use -textFieldShouldReturn to get triggered when the return key is pressed (and remember to return YES; from that.) Also float a clear custom button behind the elements of the view and handle a "background tap" that goes through all the text fields on your view and resigns first responder on the lot of them.
actually you should return NO so that the text field does not begin editing at all. If it does, the firstresponder gets set and the keyboard pops up again.
Make sure your setting your delegates for the textfield.
myTextField.delegTe = self;
And you are using in your header:
<UITextFieldDelegate>
EDIT:
Try:
if(textField == myTextField){
[textField resignFirstResponder];
}

How can I avoid a crash when VoiceOver encounters UIPickerView as subview of UITableViewCell contentView?

In my app I have a UIPickerView as a subview of a table cell's contentView. I disable scrolling in the table view, and the arrangement works fine under normal circumstances. However, I've found that when VoiceOver (or the Accessibility Inspector) is turned on, the app crashes as soon as the picker is due to get focus.
The error is:
-[UITableViewCellAccessibilityElement numberOfComponents]: unrecognized selector sent to instance 0xc15dc70
What seems to be happening is that VoiceOver is sending messages presumably intended for the UIPickerView (or its own UIAccessibilityElement?) to the UITableViewAccessibilityElement instead.
When I patch UIAccessibilityElement with the following category...
#implementation UIAccessibilityElement (GMPatches)
- (NSInteger)numberOfComponents {
return 0;
}
#end
... I eliminate the crash -- but now, unsurprisingly, VoiceOver cannot change the UIPickerView value.
And if I change it to the true value in this context...
#implementation UIAccessibilityElement (GMPatches)
- (NSInteger)numberOfComponents {
return 1;
}
#end
... then VoiceOver sends the next misaddressed message, bringing everything down again:
-[UITableViewCellAccessibilityElement selectedRowInComponent:]: unrecognized selector sent to instance 0x1e97b0
I'm feeling fairly sure this is an iOS bug.
I've tried setting isAccessibilityElement = NO on the picker, the table cell and the table cell's content view, none of which helps.
I guess I might be able to expand the UIAccessibilityElement category above to forward various messages to its parent cell's child UIPickerView. But this feels like unpleasantly brittle hackery.
Any better ideas how I might work around this?
I had the same crash when I just set tableView.tableHeaderView = myUITextField and turn VoiceOver on in the system setting app. Problem has been solved now when I wrap myUITextField in a UIView and then assign it to tableView.tableHeaderView.
I believe this would be a bug in iOS 9 or later, for UITextField is also a sub class of UIView.
The issue you are facing has to be solved differently. You are trying to add the UIPicker to the UITableCell, but this is not possible. It looks like it is not possible to determine if you actually scrolled / picked the table cell or the UIPicker.
To overcome this issue, push another view to the table that includes the UIPicker.

EXC BAD ACCESS while using UISearchBar

I think, I need another good advice. Up to now my solution seemed to run well, but now...
OK, the facts:
I have an ordinary UIViewController
On top a UINavigationBar, behind
that a
UISearchBar, hidden initially
At bottom a UIToolBar
My main view controller supports
UISearchBarDelegate
A switch on UIToolBar toggles the visibility of the UISearchBar
if (show) {
[searchBar setShowsCancelButton:TRUE animated:TRUE];
[navigationBar setHidden:TRUE];
[searchBar becomeFirstResponder];
}
else {
[navigationBar setHidden:FALSE];
[searchBar setShowsCancelButton:FALSE animated:TRUE];
}
searchIsVisible = !searchIsVisible;
There is some animation around, but I have dropped this for example. It works well, I can enter a search string and access it using "searchBarSearchButtonClicked:". I'm also able to react on "searchBarCancelButtonClicked:"
I either didn't notice it before or it happens right now - I'm occasionally catching an EXC BAD ACCESS without further notice. I'm pretty sure, the "[searchBar becomeFirstResponder];" statement is the reason for that, because I can provoke it with tapping into the search line too.
Unfortunately I'm unable to figure out, what the reason is. Do I have to provide another delegate method, as the two I have right now?
Any pointer welcome.
My guess is that searchBar is being deallocated and you don't know it. Probably because your view controller is being deallocated and/or your view hierarchy. Try breaking in your deallocs and seeing whether when they occur makes sense.