Dismiss view controller from @selector without cre

2020-04-16 19:20发布

问题:

I'm trying to dismiss the presented view controller by doing it from the button directly, instead of making a seperate method just for it, but I'm lost on how to get this to work, or if it's even possible.

Any help offered is appreciated!

Code I'm trying:

[dismissButton addTarget:self action:@selector(dismissViewControllerAnimated:YES completion:NULL) forControlEvents:UIControlEventTouchUpInside];

What I'm NOT wanting to do:

- (void)dismissThis
{
    [self dismissViewControllerAnimated:YES completion:NULL];
}

回答1:

It won't work like that. From the documentation of UIControls addTarget:action:forControlEvents::

The action message may optionally include the sender and the event as parameters, in that order.

So you have three possible selectors:

@selector(name)
@selector(nameWithParam:)
@selector(nameWithParam: otherParam:)

if your selector is @selector(dismissViewControllerAnimated:completion:) it will be called with the sender instead of the animated BOOL and the event instead of the completion handler block which will crash you app.

edit to clarify why it crashes: dismissViewControllerAnimated:completion: copies the completion block by sending the copy message. The event object doesn't implement copy and you will get an NSInvalidArgumentException.



回答2:

Apple's standard API doesn't support it, but it's easy to add this functionality through a category on UIControl. JTTargetActionBlock adds this functionality. It's also available as a Cocoapod.

[button addEventHandler:^(UIButton *sender, UIEvent *event) {
    [self dismissViewControllerAnimated:YES completion:nil];
} forControlEvent:UIControlEventTouchUpInside];


回答3:

The way I like to handle this is to subclass UIButton and add a block-based action:

@interface BlockButton : UIButton

@property (nonatomic, copy) void (^onPress)();

@end

@implementation BlockButton

-(id) initWithFrame:(CGRect)frame
{
    if(self = [super initWithFrame:frame]) {
        [self addTarget:self
                 action:@selector(pressed:)
       forControlEvents:UIControlEventTouchUpInside];
    }
    return self;
}

-(void) pressed:(id)sender
{
    if(self.onPress)self.onPress();
}

@end

Then instead of

[dismissButton addTarget:self action:@selector(dismissViewControllerAnimated:YES completion:NULL) forControlEvents:UIControlEventTouchUpInside];

- (void)dismissThis
{
    [self dismissViewControllerAnimated:YES completion:NULL];
}

you can use:

dismissButton.onPress = ^{
    [self dismissViewControllerAnimated:YES completion:NULL];
};

I'm sure you could adapt this slightly to use a UIButton category instead, if you really don't want a custom button class.