简体   繁体   中英

iOS: Pop-up menu does not behave in accordance with first responder set

I have several objects inheriting UIView in my application that are tracking taps on them and presenting Copy/Paste pop-up if they contain some specific data. When pop-up is presented I change the appearance of the object as well.

This is how it is implemented:

- (void)viewDidReceiveSingleTap:(NSNotification *)n {
    MyObject *mo = (MyObject *)n.object;

    [mo becomeFirstResponder];
    UIMenuController *menu = [UIMenuController sharedMenuController];
    [menu update];

    [menu setTargetRect:CGRectMake(...) inView:mo];

    [menu setMenuVisible:YES animated:YES];
}

MyObject class, in turn, defines canBecomeFirstResponder: and canResignFirstResponder: as always returning YES . becomeFirstResponder: , resignFirstResponder: , and canPerformAction:withSender: is also defined accordingly (this is where I change the appearance of the object).

This is what goes wrong:

I tap object 1. The method viewDidReceiveSingleTap: above is getting called, and the object's canBecomeFirstResponder: and becomeFirstResponder: are getting called too. The pop-up menu is displayed as expected.

I tap another object 2. viewDidReceiveSingleTap: is called again and here's where the trouble starts. First, canResignFirstResponder: , resignFirstResponder: of object 1 are called, but not always, and I can't figure out the pattern. canBecomeFirstResponder: and becomeFirstResponder: of object 2 are called properly, but the pop-up menu does not relocate. It just disappears (though in the viewDidReceiveSingleTap: I clearly call setMenuVisible:YES ). In order to make it appear, I have to tap object 2 (or any other) again -- in this case I can see from the debugger that object 2 was set as a first responder, it's just the pop-up that wasn't appearing.

What am I doing wrong? Any clues on relation between pop-up menu visibility and first responder?

The issue is that, when you tap anywhere except on the menu, it does an animated hide of itself. That hide is taking precedence over your animated show. So, if you tap a view to make the menu show, tap anywhere else (either on another one of those views or just anywhere else), and then tap another one of those views, the menu will show every time.

I think there's a good argument to be made for keeping that behavior, because it's the standard behavior that users will expect. But, of course, you have a better idea of what makes sense for your app. So, here's the trick:

[menu setMenuVisible:NO animated:NO];
[menu setMenuVisible:YES animated:YES];

Here's the code I used to try it out:

@implementation MenuView

- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor blueColor];
    }
    return self;
}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self becomeFirstResponder];
    UIMenuController *menu = [UIMenuController sharedMenuController];
    UIMenuItem *item = [UIMenuItem alloc] initWithTitle:@"Test"
                                                 action:@selector(test)];
    NSArray *items = [NSArray arrayWithObject:item];
    [item release];
    [menu setMenuItems:items];

    [menu setTargetRect:self.bounds inView:self];

    [menu setMenuVisible:NO animated:NO];
    [menu setMenuVisible:YES animated:YES];
}

- (void)test {
    NSLog(@"Test!");
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    return action == @selector(test);
}

- (BOOL)canBecomeFirstResponder {
    NSLog(@"Can become called");
    return YES;
}

- (BOOL)canResignFirstResponder {
    NSLog(@"Can resign called");
    return YES;
}

- (BOOL)becomeFirstResponder {
    [super becomeFirstResponder];
    NSLog(@"Become called");

    return YES;
}

- (void)dealloc {
    [super dealloc];
}


@end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM