简体   繁体   中英

present and dismiss UIViewController with completion blocks - without protocols and delegates

I would like to present an instance of VC2 from an instance of VC1 and pass it a completion block to be executed when the VC2 dismisses itself. The passed completion block will be a method call on the VC1 instance.

What would be the correct way of doing this?

While presenting VC2 from VC1 normally is:

    VC2 *vc2 = [[VC2 alloc] init];
    [self presentViewController:vc2 animated:YES completion: nil];

and in VC2

    [self dismissViewControllerAnimated:YES completion: nil];

(ps normally I would dismiss VC2 like this in VC2 - ie call a VC1 methods declared in a protocol

  [self.delegate dismissVC2]; // basically meaning VC1 to dismiss VC2

...but I guess VC2 can dismiss itself too - however I am not sure this is ALWAYS ok.

While in the Apple docs, they still recommend delegation scheme - however self dismissing works too.

Could you comment on this as well?)

I would like to do sth like this in VC2:

    [self dismissViewControllerAnimated:YES completion: passedBlockFromVC1];

And when presenting VC2 pass this passedBlockFromVC1 somehow to VC2 - while containing a VC1 method.

What would be the correct way of doing this ?

In summary, I am looking for a solution to present VC2 from VC1 and when VC2 get's dismissed it calls a VC1 method at completion - all without the need to define a protocol or use a delegate (which I find quite cumbersome to some extend in this case - BUT extremely reliable)

Is that possible and recommended?

Many thanks!

This is possible but you have to watch out for retain cycles. Remember that a block will capture any variables referenced within it including self. If VC1 keeps a strong reference to VC2 then be careful not to let the block have a strong reference to VC1 as well. If needed make a __weak reference to self outside the block and use that.

See the Apple Documentation for more information about retain cycles using blocks and how to avoid them.

The easiest thing to do would be to subclass UIViewController and make your own methods and properties to achieve this.

You can declare a property to store a block as an instance variable like so:

@property (nonatomic, copy) dispatch_block_t completionBlock;

using the standard libdispatch block type.

Then define some methods for setting this up:

-(void)presentViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion dismissCompletion:(dispatch_block_t)dismissCompletion{
    self.completionBlock = dismissCompletion;
    [super presentViewController:viewController animated:animated completion:completion];
}

then override the dismiss method to call the completion block if there is one.

-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion{
    if (self.completionBlock && ! completion){
        [super dismissViewControllerAnimated:flag completion:self.completionBlock];
        self.completionBlock = nil;
        return;
    }
    [super dismissViewControllerAnimated:flag completion:completion];
}

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