Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm building an app on iOS 8 and am looking to replicate the functionality of iOS's mail application when creating a new email / message. It's shown below: the compose view controller is presented on top of the inbox view controller, but the compose vc doesn't take up the whole screen. Is there any easier way to do this than hacking around with the frames of the view controllers? Thanks!

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
399 views
Welcome To Ask or Share your Answers For Others

1 Answer

This effect can be achieved with UIPresentationController, made available in iOS 8. Apple has a WWDC '14 video on this topic as well as some useful sample code found at the bottom of this post (original link I had posted here no longer works).

*The demo is called "LookInside: Presentation Controllers Adaptivity and Custom Animator Objects." There are a couple bugs in Apple's code that correspond to outdated API usage, which can be solved by changing the broken method name (in multiple places) to the following:

initWithPresentedViewController:presentingViewController:

Here's what you can do to replicate the animation on the iOS 8 mail app. To achieve the desired effect, download the project I mentioned above, and then all you have to do is change a couple things.

First, go to AAPLOverlayPresentationController.m and make sure you've implemented the frameOfPresentedViewInContainerView method. Mine looks something like this:

- (CGRect)frameOfPresentedViewInContainerView
{
    CGRect containerBounds = [[self containerView] bounds];
    CGRect presentedViewFrame = CGRectZero;
    presentedViewFrame.size = CGSizeMake(containerBounds.size.width, containerBounds.size.height-40.0f);
    presentedViewFrame.origin = CGPointMake(0.0f, 40.0f);
    return presentedViewFrame;
}

The key is that you want the frame of the presentedViewController to be offset from the top of the screen so you can achieve the look of one view controller overlapping the other (without having the modal fully cover the presentingViewController).

Next, find the animateTransition: method in AAPLOverlayTransitioner.m and replace the code with the code below. You may want to tweak a few things based on your own code, but overall, this appears to be the solution:

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *fromView = [fromVC view];
    UIViewController *toVC   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *toView = [toVC view];

    UIView *containerView = [transitionContext containerView];

    BOOL isPresentation = [self isPresentation];

    if(isPresentation)
    {
        [containerView addSubview:toView];
    }

    UIViewController *bottomVC = isPresentation? fromVC : toVC;
    UIView *bottomPresentingView = [bottomVC view];

    UIViewController *topVC = isPresentation? toVC : fromVC;
    UIView *topPresentedView = [topVC view];
    CGRect topPresentedFrame = [transitionContext finalFrameForViewController:topVC];
    CGRect topDismissedFrame = topPresentedFrame;
    topDismissedFrame.origin.y += topDismissedFrame.size.height;
    CGRect topInitialFrame = isPresentation ? topDismissedFrame : topPresentedFrame;
    CGRect topFinalFrame = isPresentation ? topPresentedFrame : topDismissedFrame;
    [topPresentedView setFrame:topInitialFrame];

    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                          delay:0
         usingSpringWithDamping:300.0
          initialSpringVelocity:5.0
                        options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState
                     animations:^{
                         [topPresentedView setFrame:topFinalFrame];
                         CGFloat scalingFactor = [self isPresentation] ? 0.92f : 1.0f;
                         //this is the magic right here
                         bottomPresentingView.transform = CGAffineTransformScale(CGAffineTransformIdentity, scalingFactor, scalingFactor);

                    }
                     completion:^(BOOL finished){
                         if(![self isPresentation])
                         {
                             [fromView removeFromSuperview];
                         }
                        [transitionContext completeTransition:YES];
                    }];
}

I don't, at this time, have a solution for OS versions prior to iOS 8, but please feel free to add an answer if you come up with one. Thanks.

UPDATE (03/2016):

It appears as though the link above no longer works. The same project can be found here: https://developer.apple.com/library/ios/samplecode/LookInside/LookInsidePresentationControllersAdaptivityandCustomAnimatorObjects.zip

UPDATE (12/2019):

It appears as though this transition style is now the default behavior when presenting view controllers modally on iOS 13. I'm not positive about previous versions of the OS, but if you want to replicate this functionality / transition in your own apps without writing lots of code, you can either just present a view controller on iOS 13 as-is, or you can set that view controller's modalPresentationStyle to .pageSheet and then present it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...