如何在IOS中从底部呈现模态UIViewController对话框(图像附加)

时间:2017-01-24 00:28:39

标签: ios uiview uiviewcontroller uipopovercontroller modalviewcontroller

如何呈现UIViewController(来自Storyboard说)是模态的,并从呈现视图控制器的底部向上滑动。要求是:

  • 从底部向上滑动,宽度与呈现视图控制器的宽度对齐
  • 不占用整个屏幕或整个父母呈现视图控制器(而只是显示自己所需的高度)
  • 可以在不占用整个屏幕的视图控制器的上下文中显示

enter image description here

2 个答案:

答案 0 :(得分:2)

我不使用故事板,所以我把它全部写完了。您可以将其粘贴到一个全新的项目中并运行它以使其正常工作。

你的PresentingController需要符合两件事。第一个协议是:UIViewControllerTransitioningDelegate,它允许控制器提供一个自定义的演示者(在下面我们的例子中就是自己)。无论你在这里返回什么(无论是自我还是其他一些对象)都需要符合UIViewControllerAnimatedTransitioning并提供自定义动画。对于这个自包含的示例,我选择当前的viewController作为演示者和动画师。

接下来,它需要符合协议:UIViewControllerAnimatedTransitioning,它为任何呈现或解除控制器提供自定义动画。

换句话说,当我们呈现或解除viewController时,将调用animateTransition协议中的UIViewControllerAnimatedTransitioning来确定子控制器应如何设置透视或从视口移除。< / p>

示例(使用过渡动画):

//
//  ViewController.m
//  SO
//
//  Created by Brandon T on 2017-01-23.
//  Copyright © 2017 XIO. All rights reserved.
//

#import "ViewController.h"


//Some view controller that will be presented modally.
//I have coloured it red.
@interface ModalController : UIViewController
@end

@implementation ModalController
- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor redColor];
}
@end



//The view controller that will present or dismiss some other view controller modally.
//I have coloured it white.
@interface ViewController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) bool presentingModalController;
@property (nonnull, nonatomic, strong) ModalController *modalController;
@property (nonnull, nonatomic, strong) UIButton *button;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor whiteColor];

    //For this example, I add a button to present and dismiss the redViewController.
    self.button = [[UIButton alloc] initWithFrame:CGRectMake(15, self.view.center.y - 100, self.view.frame.size.width - 30, 45)];
    [self.button setTitle:@"Present" forState:UIControlStateNormal];
    [self.button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [self.button setBackgroundColor:[UIColor lightGrayColor]];
    [self.button.layer setCornerRadius:5.0];
    [self.button addTarget:self action:@selector(onButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.button];


    //Create the redViewController and set its transitioning delegate to self (this controller will be providing the animation and presenter).
    //We also set the style to OverFullScreen because we don't want this controller to disappear.
    //When a view controller is presented, the one that presented it usually disappears or gets removed from the hierarchy until the child is dismissed. In the case of alerts, or controllers that need to display OVER the current one, we need to set the modalPresentationStyle.
    self.modalController = [[ModalController alloc] init];
    self.modalController.transitioningDelegate = self;
    self.modalController.modalPresentationStyle = UIModalPresentationOverFullScreen;
}


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

- (void)onButtonClicked:(UIButton *)button {
    if (self.modalController.view.window == nil) {
        [self presentViewController:self.modalController animated:YES completion:nil];
        [self.button setTitle:@"Dismiss" forState:UIControlStateNormal];

        //not a good idea but meh.. I need to keep this example short.
        [self.view.window addSubview:self.button];
    }
    else {
        [self.modalController dismissViewControllerAnimated:YES completion:nil];
        [self.button setTitle:@"Present" forState:UIControlStateNormal];
        [self.view addSubview:self.button];
    }
}


//Custom Animations and Presenters.
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    self.presentingModalController = true; //We are presenting the controller.
    return self; //Who is animating it? We are.
}

- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    self.presentingModalController = false; //We are dismissing the view controller.
    return self; //Who animated it? We did.
}

//How fast should it present? I chose 0.5 seconds.
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return 0.5;
}

//The actual animation code.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    if (self.presentingModalController) { 
        //If we are presenting, we need to add the new controller's view as a sub-view.

        UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

        //We need a starting frame for the animation.
        CGRect startingFrame = transitionContext.containerView.bounds;
        startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
        startingFrame.size.height = 100; //Has a height of 100.

        //We need an end frame for the animation.
        CGRect finalFrame = transitionContext.containerView.bounds;
        finalFrame.origin.y = finalFrame.size.height - 100; //100 from the bottom of the parent.
        finalFrame.size.height = 100; //Present with a size of 100 height.

        //Add the controller's view as a subview of the context.
        [transitionContext.containerView addSubview:toViewController.view];
        [toViewController.view setFrame:startingFrame];

        //Start animating from "startFrame" --> "endFrame" with 0.5 seconds duration and no delay. I chose easeIn style.
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
            [toViewController.view setFrame:finalFrame];
        } completion:^(BOOL finished) {
            //We are finished animating, complete the transition!
            [transitionContext completeTransition:YES];
        }];
    }
    else {
        //If we are dismissing the view controller, we need to animate it down the screen and then remove its view from the context.

        UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

        //We only need one frame. This is the first frame. We are animating from "endFrame" --> "startingFrame" (backwards/reverse animation).
        CGRect startingFrame = transitionContext.containerView.bounds;
        startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
        startingFrame.size.height = 100; //Has a height of 100.


        //Start the animation with 0.5 seconds duration and I chose easeOut style.
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
            [fromViewController.view setFrame:startingFrame];
        } completion:^(BOOL finished) {

            //Remove the view controller's view from the context and complete the transition!
            [fromViewController.view removeFromSuperview];
            [transitionContext completeTransition:YES];
        }];
    }
}
@end

示例(没有过渡动画):

//
//  ViewController.m
//  SO2
//
//  Created by Brandon Thomas on 2017-01-23.
//  Copyright © 2017 XIO. All rights reserved.
//

#import "ViewController.h"


@interface ModalController : UIViewController
@end

@implementation ModalController
- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor redColor];
}
@end



@interface ViewController ()
@property (nonatomic, assign) bool presentingModalController;
@property (nonnull, nonatomic, strong) ModalController *modalController;
@property (nonnull, nonatomic, strong) UIButton *button;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor whiteColor];

    self.button = [[UIButton alloc] initWithFrame:CGRectMake(15, self.view.center.y - 100, self.view.frame.size.width - 30, 45)];
    [self.button setTitle:@"Present" forState:UIControlStateNormal];
    [self.button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [self.button setBackgroundColor:[UIColor lightGrayColor]];
    [self.button.layer setCornerRadius:5.0];
    [self.button addTarget:self action:@selector(onButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.button];


    self.modalController = [[ModalController alloc] init];
}


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

- (void)onButtonClicked:(UIButton *)button {
    if (self.modalController.view.window == nil) {
        //Present
        CGRect startingFrame = self.view.bounds;
        startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
        startingFrame.size.height = 100; //Has a height of 100.

        CGRect finalFrame = self.view.bounds;
        finalFrame.origin.y = finalFrame.size.height - 100; //100 from the bottom of the parent.
        finalFrame.size.height = 100; //Present with a size of 100 height.

        [self.modalController.view setFrame:startingFrame];

        [self.modalController willMoveToParentViewController:self];
        [self addChildViewController:self.modalController];
        [self.view addSubview:self.modalController.view];
        [self.modalController didMoveToParentViewController:self];

        [UIView animateWithDuration:0.5 animations:^{
            [self.modalController.view setFrame:finalFrame];
        } completion:^(BOOL finished) {

        }];
    }
    else {
        //Dismiss
        CGRect startingFrame = self.view.bounds;
        startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
        startingFrame.size.height = 100; //Has a height of 100.

        [UIView animateWithDuration:0.5 animations:^{
            [self.modalController.view setFrame:startingFrame];
        } completion:^(BOOL finished) {
            [self.modalController.view removeFromSuperview];
            [self.modalController willMoveToParentViewController:nil];
            [self.modalController removeFromParentViewController];
            [self.modalController didMoveToParentViewController:nil];
        }];
    }
}
@end

答案 1 :(得分:1)

请查看Apple documentation

  

使用自定义动画呈现视图控制器

     

要使用自定义动画显示视图控制器,请执行以下操作   在现有视图控制器的操作方法中:

     

创建要显示的视图控制器。创建你的   自定义转换委托对象并将其分配给视图   controller的transitioningDelegate属性。你的方法   转换委托应该创建并返回您的自定义动画师   被问到的对象。打电话给   presentViewController:animated:completion:用于呈现视图的方法   控制器。当你打电话给   presentViewController:animated:completion:方法,UIKit启动   演示过程。演示文稿在下一个运行循环期间开始   迭代并继续,直到您的自定义动画师调用   completeTransition:方法。交互式过渡允许您   在转换过程中处理触摸事件,但是   非交互转换运行的持续时间由   动画师对象。

编辑:

您可以选择创建一个包含自定义大小的容器,并为添加为UIViewController的子视图的UIViewController设置动画:

[self addChildViewController:content];
content.view.frame = [self frameForContentController];
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self];

Taken from this Thread