thermogl / TITokenField

An iOS version of the NSTokenField (See To: field in Mail and Messages).
http://thermoglobalnuclearwar.com/opensource/
599 stars 172 forks source link

Crashes when modalView is closed from delegate #21

Open travisjbeck opened 12 years ago

travisjbeck commented 12 years ago
#pragma mark View Handlers
- (void)layoutTokensAnimated:(BOOL)animated {
    CGFloat newHeight = [self layoutTokensInternal];
    if (self.bounds.size.height != newHeight){

        // Animating this seems to invoke the triple-tap-delete-key-loop-problem-thing™
        [UIView animateWithDuration:(animated ? 0.3 : 0) animations:^{
            [self setFrame:((CGRect){self.frame.origin, {self.bounds.size.width, newHeight}})];
            [self sendActionsForControlEvents:TITokenFieldControlEventFrameWillChange];

        } completion:^(BOOL complete){
            [self sendActionsForControlEvents:TITokenFieldControlEventFrameDidChange]; //Crashes on this line
        }];
    }
}
thermogl commented 12 years ago

Can you provide instructions on how to reproduce?

travisjbeck commented 12 years ago

I added a navigationcontroller with uiviewcontroller as root view controller subclassed with your sample code as MessageComposeViewController.

setup a modal seque from a presentingViewController to my messageComposeViewController.

when i hit compose in my presentingViewController i perform the modal seque and set itself as the delegate for messageViewController

so the topleft navbaritem on my MessageComposeViewController is "cancel" and the top right is "send" both of these fire methods in the delegate which handles the response.

all im doing in the send method is capturing the MessageComposeViewController messageView.text and an array of tokens added.

then i dissmiss the MessageComposeViewController which causes the crash under the following circumstance:

More than one token has been added && messageView.text.length > 1

If i only add one token it does not crash, but more than one token.. crash.

Hope this helps.

thermogl commented 12 years ago

Any chance you could create a sample project? I do the same thing in one of my own apps with no problem.

hsoi commented 12 years ago

I'm running into this same problem, or at least a similar one.

What seems to have fixed it for me was in the completion block to check complete and only -sendActions if complete, e.g.:

if (complete) {
    [self sendActions...]
}

which seems to make sense, but could potentially have side-effects, but probably safe in this case given the limited scope of the animation.

Nevertheless, there's something deeper at play. If I can come up with a simple repro I will.

thermogl commented 12 years ago

Great, I'll try a push those changes out soon.

hsoi commented 12 years ago

Well... I'm not 100% sure it is a RIGHT fix, just that it works and does APPEAR to make sense. It'd still be nice to know why the real behavior is happening, but my guess is it's all timing.

What seems to happen, at least in my case, is I have added a couple tags, seems to be related to the fact that the tags are long and it causes the tokenfield to adjust its height to be taller to show all the tags. Tap "Cancel" to dismiss my modal view, -dismissModalViewControllerAnimated: is invoked, which for some reason calses -[TITokenFieldView becomeFirstResponder] to be invoked... not sure why, could be because I also have a UISearchBar also in the same view, it was the first responder when Cancel was tapped, and maybe as it's being destructed the responder switches to the TITokenField? I don't know, but that might be the crux of the problem.

Anyways, that cascades down like this:

#0  0x000f3ab9 in -[TITokenField layoutTokensAnimated:] at TITokenField.m:689
#1  0x000f3fec in -[TITokenField setResultsModeEnabled:animated:] at TITokenField.m:719
#2  0x000f3f8f in -[TITokenField setResultsModeEnabled:] at TITokenField.m:714
#3  0x000f2892 in -[TITokenField addToken:] at TITokenField.m:579
#4  0x000f199c in -[TITokenField didBeginEditing] at TITokenField.m:488
#5  0x026c0e99 in -[NSObject performSelector:withObject:withObject:] ()
#6  0x005a10a1 in -[UIApplication sendAction:fromSender:toTarget:forEvent:] ()
#7  0x00648697 in -[UIControl(Deprecated) sendAction:toTarget:forEvent:] ()
#8  0x00647d86 in -[UIControl(Internal) _sendActionsForEventMask:withEvent:] ()
#9  0x006549c8 in -[UITextField willAttachFieldEditor:] ()
#10 0x00660564 in -[UIFieldEditor becomeFieldEditorForView:] ()
#11 0x0064df02 in -[UITextField _becomeFirstResponder] ()
#12 0x0068c093 in -[UIResponder becomeFirstResponder] ()
#13 0x000f181b in -[TITokenField becomeFirstResponder] at TITokenField.m:484
#14 0x000ee896 in -[TITokenFieldView becomeFirstResponder] at TITokenField.m:165
#15 0x0068c38c in -[UIResponder resignFirstResponder] ()
#16 0x0064e219 in -[UITextField resignFirstResponder] ()
#17 0x006599a6 in -[UIView(UITextField) endEditing:] ()
#18 0x0088438c in -[UIWindowController _prepareKeyboardForTransition:fromView:] ()
#19 0x00885c17 in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:] ()
#20 0x0066da9c in -[UIViewController _dismissViewControllerWithTransition:from:completion:] ()
#21 0x0066cf91 in -[UIViewController dismissViewControllerWithTransition:completion:] ()
#22 0x0066cf91 in -[UIViewController dismissViewControllerWithTransition:completion:] ()
#23 0x0066dc43 in -[UIViewController dismissModalViewControllerAnimated:] ()

But I end up inside of -layoutTokensAnimated: a few times as things snake through. A couple times because -addToken: was invoked.... and then the animation block itself calls -setFrame: which itself can invoke -layoutTokensAnimated:.... but then it seems the "final" invocation is because a -performSelector happens that ultimately does invoke -[TITokenField didEndEditing] which hits the -layoutTokensAnimated:. And then at that point somewhere in there the -dealloc of the field happens... but the animation completion is still out there, and boom.

Like I said... I do have that UISearchBar also in there (that and the TITokenField are the only textually editable views in my modal view). If the TITokenField is the current responder when I dismiss the modal view, no crash. It requires the UISearchBar to be the responder. AND it doesn't seem to matter how many tokens are in the field, just that it's enough tokens (when they draw with the token rect cosmetics) that it would cause the field to grow to be more than 1 line tall.

So.... hopefully that information may be of use to find the real problem.

To @travisjbeck , does any of this perhaps ring a similar bell for you?

thermogl commented 12 years ago

Thanks so much for all this, I'm super swamped at the moment so I'll try to dig a little deeper ASAP.

hsoi commented 12 years ago

You're welcome. If I can help any further, please let me know.

alistra commented 11 years ago

The same method crashes for me if i minimise my app and then the completion block fires and gets EXC_BAD_ACCESS

Minakshi24 commented 10 years ago

I am having same issue. But checking complete flag didn't solved my problem if (complete) { [self sendActions...] }

Is there any other solution?