2

I am having a problem that I can't seem to figure out.

What I have setup:

  1. A custom UITableViewCell that has multiple textfields in it. Similar to how Apple has it setup when adding a new address for someone in contacts.
  2. The table with the custom cell is being presented as a modal view. This table view also has other cells that are generic (i.e. not the custom cell stated in 1. ).
  3. The presented modal view has a toolbar at the top, which contains a done button. When done is pressed the modal view sends a call back to its delegate and dismisses.

The problem that I am having: When the user selects a UITextField and starts to edit the UIKeyboard is presented (obviously right?). If the keyboard is open, and the user selects "done" on the toolbar (as stated in 3. ), the app crashes because the parent view is trying to dismiss the modal view (modal view containing the keyboard) while the keyboard is open.

High level it seems like it should work like this:

User clicks "done" in toolbar -> check if UIKeyboard is open -> if open, close else dismiss modal view

But it hasn't been that easy.

I have been pulling my hair out trying to find a viable work around for this and nothings seems to work.

Any suggestions would be very appreciated.

Edit

Here is the crash log:

2011-12-22 16:03:09.021 Renshaw[2308:5f43] bool _WebTryThreadLock(bool), 0xad7a9e0: Tried     to obtain the web lock from a thread other than the main thread or the web thread. This may be     a result of calling to UIKit from a secondary thread. Crashing now...
1   _ZL17_WebTryThreadLockb
2   WebThreadLock
3   -[UITextRangeImpl isEmpty]
4   -[UITextRange(UITextSelectionAdditions) _isCaret]
5   -[UITextSelectionView setCaretBlinks:]
6   -[UIKeyboardImpl setCaretBlinks:]
7   -[UIKeyboardImpl setDelegate:force:]
8   -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
9   -[UIResponder _finishResignFirstResponder]
10  -[UIResponder resignFirstResponder]
11  -[UITextField resignFirstResponder]
12  -[UIView(UITextField) endEditing:]
13  -[UIWindowController _prepareKeyboardForTransition:fromView:]
14  -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:]
15  -[UIViewController _dismissViewControllerWithTransition:from:completion:]
16  -[UIViewController dismissViewControllerWithTransition:completion:]
17  -[UIViewController dismissViewControllerWithTransition:completion:]
18  -[ListingsViewController postSearch:allObjects:constrainedList:]
19  -[FilterViewController searchObjects]
20  -[NSObject performSelector:withObject:]
21  -[MBProgressHUD launchExecution]
22  -[NSThread main]
23  __NSThread__main__
24  _pthread_start
25  thread_start
[Switching to process 10499 thread 0x2903]
[Switching to process 10499 thread 0x2903]
random
  • 8,568
  • 12
  • 50
  • 85

5 Answers5

5

The clue is in the stack trace:

Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...

You're calling a UIKit method from a secondary thread. You must be doing something on not-the-main-thread such as calling resignFirstResponder. See here for more information on that:

Update:

It turns out the solution was to use something along the lines of:

dispatch_async(dispatch_get_main_queue(), ^{ <code_here> });

to wrap the call to dismiss the view so that the UIKit methods were being run on the main thread.

Community
  • 1
  • 1
mattjgalloway
  • 34,792
  • 12
  • 100
  • 110
  • I believe that I found the problem but I am still not sure why it IS a problem. I am using `[HUD showWhileExecuting:@selector(searchObjects) onTarget:self withObject:nil animated:YES];` to run through a searching algorithm. At the end it calls the delegate to dismiss the view. If I just use `[self searchObjects]` and the UIKeyboard is open it fixes my original issue. If I use `[HUD showWhileExecuting:@selector(searchObjects) onTarget:self withObject:nil animated:YES];` it causes a crash. Why would that be? – random Dec 22 '11 at 22:43
  • What thread does the delegate callback from `HUD` come on? If that's where you're dismissing the view then I bet that's being called on a background thread. Wrap your call to dismiss the view in `dispatch_async(dispatch_get_main_queue(), ^{ });` or use `performSelectorOnMainThread:withObject:waitUntilDone:`. – mattjgalloway Dec 22 '11 at 22:46
  • `dispatch_async(dispatch_get_main_queue(), ^{ });` += 1 – Jacksonkr Feb 08 '12 at 23:09
1

Put this above your @implementation

static UITextField *currentlySelectedTextField = nil;

And override those methods

- (void)viewWillDisappear {
  [super viewWillDisappear];
  [currentlySelectedTextField resignFirstResponder];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField {
  currentlySelectedTextField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
  currentlySelectedTextField = nil;
}

of course this assumes that you are registering yourself as a delegate for each of the UITextFields that are being used inside your custom cells

Eugene
  • 10,006
  • 4
  • 37
  • 55
  • The delegates for the textfields are being registered to the custom tableviewcell – random Dec 22 '11 at 21:44
  • The same static variable can be created inside your custom tableviewcell, simply put the [currentlySelectedTextField resignFirstResponder]; in your custom cell's dealloc method and leave the rest as it is. – Eugene Dec 22 '11 at 21:46
  • I am still getting the crash when doing that. I believe I will have to change the textfield delegates to the class implementing the custom cell :/ – random Dec 22 '11 at 21:54
  • Please post your crash log. I'm starting to think that your problem is not the UIKeyboard – Eugene Dec 22 '11 at 22:00
  • The cell is being created `LocationCustomCell *cell = (LocationCustomCell*)[tableView dequeueReusableCellWithIdentifier:@"CustomCell2"];` so the `dealloc` is being called right after the cell is created. I will post the crash log in original post so formatting will be nicer. – random Dec 22 '11 at 22:01
  • If the UIKeyboard is not showing every things works beautifully. If I present the keyboard, then dismiss using the "return" key every things works great. I use this same custom cell in a different view that is not presented modally, it has in the top nab bar "restart", which calls `[self.navigation popToRootViewController]` . If the UIKeyboard is open and called all works good. It has to be something with dismissing the modal view while the UIKeyboard is presented. – random Dec 22 '11 at 22:09
0

I had a similar issue but not on the textFieldShouldReturn but rather on the textFieldDidBeginEditing.

The issue was that the textField was added in a modal view but there was also a textField on the underlying view. The underlying textField was created in Interface Builder and was therefor allocated and set to First Responder before the modal view was even on screen. This resulted in the same error as above.

This was easily fixed by NOT adding the underlying textField in IB. I added it programmatically later, once it was needed in the underlying view.

Easy fix, hard to find issue ;-)

Cheers Nick

Nick
  • 205
  • 3
  • 14
0

How about disabling the done button whenever a textfield is made active?

Just do: doneButton.enabled = NO;, when a textfield becomesFirstResponder, then enable the done button when the keyboard is dismissed (in textFieldShouldReturn:(UITextField*)textField).

WrightsCS
  • 50,551
  • 22
  • 134
  • 186
hanyx
  • 1
  • I think that is what I am going to do. I hoping to avoid this just because all of the fields are not required. In the user testing so far, users have tried to press "done" in the tabbar instead of pressing "return" on the keyboard, then pressing "done". – random Dec 22 '11 at 21:43
0

I was seeing _webthreadlockfromanythread crashes when needing to push a UIViewController after checking some values in textFieldShouldReturn.

If you need to use textFieldShouldReturn, here's what works:

  1. If textFieldShouldReturn returns YES you will also need to call resignFirstResponder on the textfield
  2. This will in turn call textFieldDidEndEditing:, and will start an animation to dismiss the keyboard.

The _webthreadlockfromanythread exception was being called because my App was pushing a view controller before the keyboard had fully been dismissed.

The solution is to add an observer for the UIKeyboardDidHideNotification and a method that receives the notification to push your view controller. Good luck!

Emil
  • 7,220
  • 17
  • 76
  • 135
nunavut
  • 49
  • 1
  • 1
  • 7