从左向右转场的 dismissModalViewController

17

我曾经使用了一种很好的方法来关闭我的模态视图控制器:

[self dismissModalViewControllerWithTransition:2];

如何实现从左到右的幻灯片转换效果,就像导航控制器在弹出视图时所做的那样。

由于这种方法是非公开方法,苹果不会接受它。我该如何在我的代码中编程实现这种动画(从左向右滑动以关闭模态视图,从右向左滑动以呈现模态视图)?

提前感谢。

3个回答

42

我已经接受了Safecase的答案,但我想在这里发布我的最终解决方案:

1)为了以从右侧到左侧的转换方式呈现模态视图控制器,我编写了以下方法:

-(void) presentModalView:(UIViewController *)controller {
    CATransition *transition = [CATransition animation];
    transition.duration = 0.35;
    transition.timingFunction =
    [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionMoveIn;
    transition.subtype = kCATransitionFromRight;

    // NSLog(@"%s: self.view.window=%@", _func_, self.view.window);
    UIView *containerView = self.view.window;
    [containerView.layer addAnimation:transition forKey:nil];
    [self presentModalViewController:controller animated:NO];
}

2) 以从左向右的滑动转换方式关闭模态视图:

-(void) dismissMe {
    CATransition *transition = [CATransition animation];
    transition.duration = 0.35;
    transition.timingFunction =
    [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionMoveIn;
    transition.subtype = kCATransitionFromLeft;

    // NSLog(@"%s: controller.view.window=%@", _func_, controller.view.window);
    UIView *containerView = self.view.window;
    [containerView.layer addAnimation:transition forKey:nil];

    [self dismissModalViewControllerAnimated:NO];
}

谢谢大家!


3
为了更接近模仿从导航控制器弹出一个项目的行为,将transition.type设置为kCATransitionReveal。 - Ryan Grimm
太棒了,我已经寻找这个很长时间了。+1 - Unome

7
尝试这个:
我假设你正在从视图控制器1中解除视图控制器2。在视图控制器2中,您正在使用以下内容。
[self  dismissModalViewControlleAnimated: NO]];

现在,在第一个视图控制器中,在viewWillAppear:方法中添加以下代码:

CATransition *animation = [CATransition animation];

[animation setDelegate:self];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];

[animation setDuration:0.50];
[animation setTimingFunction:
 [CAMediaTimingFunction functionWithName:
  kCAMediaTimingFunctionEaseInEaseOut]];


[self.view.layer addAnimation:animation forKey:kCATransition];

我没有给你提示吗?这就是为什么你能得到正确答案的原因。 - Paresh Navadiya

1
以下是Swift 4中的ModalService类,它是一个预打包的灵活解决方案,可以轻松地添加到项目中,并在需要时从任何位置调用。该类将模态视图从指定方向带入当前视图之上,然后在退出时将其移动出来以显示其后面的原始视图。只需提供当前正在显示的presentingViewController和您创建并希望显示的modalViewController即可调用:
ModalService.present(modalViewController, presenter: presentingViewController)

然后,要关闭,请调用:


ModalService.dismiss(modalViewController)

当然,可以从modalViewController本身调用此方法,如ModalService.dismiss(self)。请注意,解除模态视图不必从presentingViewController调用,并且不需要了解或引用原始的presentingViewController。

该类提供合理的默认值,包括将模态视图从右侧转换进入并向左侧退出。可以通过传递方向来自定义此转换方向,可为进入和退出都进行自定义:

ModalService.present(modalViewController, presenter: presentingViewController, enterFrom: .left)

并且

ModalService.dismiss(self, exitTo: .left)

这可以设置为.left.right.top.bottom。如果需要,您也可以传递自定义的持续时间(以秒为单位):
ModalService.present(modalViewController, presenter: presentingViewController, enterFrom: .left, duration: 0.5)

并且

ModalService.dismiss(self, exitTo: .left, duration: 2.0)

向 @jcdmb 致敬,他在这个问题上的 Objective C 答案是解决方案中的核心。


这是完整的类。私有的transitionSubtype返回的值看起来很奇怪,但这样设置是有意义的。在做出更改之前,请先测试观察到的行为。 :)
import UIKit

class ModalService {

    enum presentationDirection {
        case left
        case right
        case top
        case bottom
    }

    class func present(_ modalViewController: UIViewController,
                       presenter fromViewController: UIViewController,
                       enterFrom direction: presentationDirection = .right,
                       duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = kCATransitionMoveIn
        transition.subtype = ModalService.transitionSubtype(for: direction)
        let containerView: UIView? = fromViewController.view.window
        containerView?.layer.add(transition, forKey: nil)
        fromViewController.present(modalViewController, animated: false)
    }

    class func dismiss(_ modalViewController: UIViewController,
                       exitTo direction: presentationDirection = .right,
                       duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = kCATransitionReveal
        transition.subtype = ModalService.transitionSubtype(for: direction, forExit: true)
        if let layer = modalViewController.view?.window?.layer {
            layer.add(transition, forKey: nil)
        }
        modalViewController.dismiss(animated: false)
    }

    private class func transitionSubtype(for direction: presentationDirection, forExit: Bool = false) -> String {
        if (forExit == false) {
            switch direction {
            case .left:
                return kCATransitionFromLeft
            case .right:
                return kCATransitionFromRight
            case .top:
                return kCATransitionFromBottom
            case .bottom:
                return kCATransitionFromTop
            }
        } else {
            switch direction {
            case .left:
                return kCATransitionFromRight
            case .right:
                return kCATransitionFromLeft
            case .top:
                return kCATransitionFromTop
            case .bottom:
                return kCATransitionFromBottom
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接