简体   繁体   中英

IOS/Objective-C: Possible to Use Custom Segue in Modal Transition in Absence of a Storyboard Segue?

I have two View Controllers in Storyboard and I would like to present the second one from the first modally using a custom downward segue (the opposite of the regular cover vertical).

I have subclassed storyboard segue with code that I hope will work.

However, in this instance, I did not drag a segue in Storyboard from one view controller to the other. Instead, I handle the Modal Presentation in code.

Is it possible to invoke a custom segue if you are calling the second view controller in code? Or do you have to have an actual segue in storyboard to which you assign the custom class?

If possible in code, where would you put the code?

Here is code I am using to present the modal View Controller

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"chat" bundle:nil];
    UIViewController *myVC = [storyboard instantiateViewControllerWithIdentifier:@"chatVC"];
    [(UINavigationController*)self.window.rootViewController presentViewController:myVC animated:NO completion:nil];

It seems like you're trying to accomplish many custom animation transitions within your application, so I'd recommend you roll with your own animator classes instead of trying to create custom segues. Similar to the example I posted to you previous question (linked in my comment above), you're going to want to setup some sort of delegate (in the previous example it was a UINavigationControllerDelegate) to handle determining which custom animator classes to use for presenting/dismissing (in the previous example it was for pushing/popping) certain view controllers:

Here's an example of what I mean:

ViewController.m (using this as the UIViewControllerTransitioningDelegate):

#import "ViewController.h"
#import "SlideDownAnimator.h"
#import "SlideUpAnimator.h"

@interface ViewController () <UIViewControllerTransitioningDelegate>

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (IBAction)_presentCustomDownward:(id)sender {
    // setup a fake view controller we're going to present
    UIViewController *fakeSecondViewController = [[UIViewController alloc] init];
    fakeSecondViewController.view.backgroundColor = [UIColor blueColor];
    // make sure to set the desination view controller's transition delegate
    // this doesn't have to be set to self here, you can set it anything that will implement: UIViewControllerTransitioningDelegate
    fakeSecondViewController.transitioningDelegate = self;
    // when we call present on the view controller it asks the transitioningDelegate for an animation coordinator which we're going to provide below
    [self.navigationController presentViewController:fakeSecondViewController animated:YES completion:^{
        // dismis after a couple seconds to show what the custom dismiss looks like (obviously this is just for the example code, you will handle your own dismissal)
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self.navigationController dismissViewControllerAnimated:YES completion:nil];
        });
    }];
}

// delegate call for presentation
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
                                                                  presentingController:(UIViewController *)presenting
                                                                      sourceController:(UIViewController *)source {

    // return our own custom Animator class (which adheres to UIViewControllerAnimatedTransitioning protocol)
    return [[SlideDownAnimator alloc] init];
}

// delegate call for dismissal
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    // return our own custom Animator class (which adheres to UIViewControllerAnimatedTransitioning protocol)
    return [[SlideUpAnimator alloc] init];
}

SlideDownAnimator.h (Custom Down Animator):

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface SlideDownAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@end

NS_ASSUME_NONNULL_END

SlideDownAnimator.m:

#import "SlideDownAnimator.h"

@implementation SlideDownAnimator

- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.4f;
}

- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
    // grab the toViewController (the vc being presented)
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    // manually add it to our container view
    [[transitionContext containerView] addSubview:toViewController.view];
    // get the frame so we can do some of our own animations on it
    CGRect toVCFrame = toViewController.view.frame;
    CGFloat toVCHeight = toVCFrame.size.height;
    // offset the y coordiante by it's height so that it's completely above our current screen
    toVCFrame.origin.y = -toVCHeight;
    // set the initial frame so it's above our screen
    [toViewController.view setFrame:toVCFrame];

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        // animate the y position to 0 so it slides down
        CGRect finalVCFrame = toViewController.view.frame;
        finalVCFrame.origin.y = 0;
        [toViewController.view setFrame:finalVCFrame];
    } completion:^(BOOL finished) {
        // make sure to call this so we'll call back to our presentViewdController's completion block
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end

My guess is that you're going to want a custom slide up animation upon dismissal as well, so you'll want one more animator which implements that custom transition:

SlideUpAnimator.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface SlideUpAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@end

NS_ASSUME_NONNULL_END

SlideUpAnimator.m:

#import "SlideUpAnimator.h"

@implementation SlideUpAnimator

- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.4f;

}

- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
    // grab our fromViewController (the vc being dismissed)
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    // and our toViewController (the vc that will be shown after dismissal)
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    // add our toViewController as a subview of our container view (make sure to add it beneath the dismissing view controller so we can let it complete it's animation first)
    [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        // push the view back up above the screen
        CGRect fromVCFrame = fromViewController.view.frame;
        fromVCFrame.origin.y = -fromVCFrame.size.height;
        [fromViewController.view setFrame:fromVCFrame];
    } completion:^(BOOL finished) {
        // make sure to call this so we'll call back to our presentViewdController's completion block
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];

}

@end

The complete animation will end up looking like this:

Custom_Downward_Animation_Transition

Here's some links that should be helpful:

https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html

https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate?language=objc

https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning?language=objc

Edit:

As far as your other question as to where to put this in your own code: This is highly dependent on your application structure, so without seeing more context of the classes you're attempting to use them on, it's hard to say the best approach.

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