【问题标题】:Animate presentModalViewController from right /left从右/左动画 presentModalViewController
【发布时间】:2012-01-25 08:55:57
【问题描述】:

目前我正在使用 [self presentModalViewController :newVC animated:YES] 。我想从左/右/上/下呈现 newViewcontroller 并具有推动效果。我尝试使用 CATransition,但它在转换之间显示黑屏。

【问题讨论】:

标签: iphone objective-c animation presentmodalviewcontroller


【解决方案1】:

在场时:

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[self.view.window.layer addAnimation:transition forKey:nil];

[self presentModalViewController:viewCtrl animated:NO];

解散时:

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[self.view.window.layer addAnimation:transition forKey:nil];
[self dismissModalViewControllerAnimated:NO];

【讨论】:

  • 这似乎不再适用于 iOS 8,因为呈现的 VC 正在移动而不是呈现的 VC。知道如何解决吗?
  • ¿如何消除褪色效果?
  • 什么是[tabCtrl release];在演示结束时?
  • @jalopezsuarez 你有没有想过如何做到这一点?
  • @wcrane 我也出现黑屏闪烁。实际上淡入淡出和淡入淡出动画正在发生。我试图去除黑色阴影的任何提示
【解决方案2】:

我遇到了同样的问题。假设您想从视图控制器 1 中显示视图控制器 2。在第一个视图控制器中使用

 [self presentModalViewController: controller animated: NO]];

在第二个视图控制器中,在 viewWillAppear: 方法中添加代码

    CATransition *animation = [CATransition animation];

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

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


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

它会正常工作的。如果再次出现黑屏,如果您正在使用导航控制器,请更换

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

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

【讨论】:

  • 我也有黑屏问题。在模态控制器被推入之前出现黑屏。有人解决了这个问题吗?
【解决方案3】:

在 X >= 4 小时的工作之后,这可以在没有“撕裂”或其他背景伪影的情况下工作:

class AboutTransition: NSObject, UIViewControllerAnimatedTransitioning {

    let presenting: Bool
    let duration: NSTimeInterval

    init(presenting: Bool, duration: NSTimeInterval = 0.25) {
        self.presenting = presenting
        self.duration = duration
    }

    @objc func transitionDuration(ctx: UIViewControllerContextTransitioning) -> NSTimeInterval {
        return duration
    }

    @objc func animateTransition(ctx: UIViewControllerContextTransitioning) {
        let duration = transitionDuration(ctx)
        let containerView = ctx.containerView()
        let fromViewController = ctx.viewControllerForKey(UITransitionContextFromViewControllerKey)!
        let toViewController = ctx.viewControllerForKey(UITransitionContextToViewControllerKey)!
        let fromView = fromViewController.view // 7.0 & 8.0 compatible vs. viewForKey:
        let toView = toViewController.view     // 7.0 & 8.0 compatible vs. viewForKey:

        containerView.addSubview(fromView)
        containerView.addSubview(toView)

        let offScreenRight = CGAffineTransformMakeTranslation(containerView.frame.width, 0)
        let offScreenLeft = CGAffineTransformMakeTranslation(-containerView.frame.width, 0)

        fromView.transform = CGAffineTransformIdentity
        toView.transform = self.presenting ? offScreenRight : offScreenLeft

        UIView.animateWithDuration(duration, delay:0, options:UIViewAnimationOptions(0), animations: {
            fromView.transform = self.presenting ? offScreenLeft : offScreenRight
            toView.transform = CGAffineTransformIdentity

        }, completion: { (finished: Bool) in
            ctx.completeTransition(finished)
            if finished {
                fromView.removeFromSuperview()
                fromView.frame = toView.frame // reset the frame for reuse, otherwise frame transforms will accumulate
            }
        })
    }
}

然后在AppDelegate.swift:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UINavigationControllerDelegate {

    lazy var window: UIWindow? = {
        return UIWindow(frame: UIScreen.mainScreen().bounds)
    }()

    lazy var storyboard = UIStoryboard(name: "Main", bundle: nil)

    lazy var nav: UINavigationController = {
        var r = self.storyboard.instantiateInitialViewController() as! UINavigationController
        r.delegate = self
        return r
    }()

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        self.window!.rootViewController = nav
        self.window!.makeKeyAndVisible()

        // .. other setup

        return true
    }

    // ...


    // MARK: - UINavigationControllerDelegate

    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        // Example of a SettingsViewController which pushes AboutViewController
        if fromVC is SettingsViewController && toVC is AboutViewController { // Push to About
            return AboutTransition(presenting: true)
        } else if fromVC is AboutViewController && toVC is SettingsViewController { // Pop to Settings
            return AboutTransition(presenting: false)
        }
        return nil
    }
}

这假设应用使用默认情节提要,初始 VC 为 UINavigationController

Swift 3/4 更新

class LTRTransition: NSObject, UIViewControllerAnimatedTransitioning
{ 
let presenting: Bool
let duration: TimeInterval

init(presenting: Bool, duration: TimeInterval = Theme.currentTheme.animationDuration) {
    self.presenting = presenting
    self.duration = duration
}

@objc func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
    return duration
}

@objc func animateTransition(using ctx: UIViewControllerContextTransitioning) {
    let duration = transitionDuration(using: ctx)
    let containerView = ctx.containerView
    let fromViewController = ctx.viewController(forKey: UITransitionContextViewControllerKey.from)!
    let toViewController = ctx.viewController(forKey: UITransitionContextViewControllerKey.to)!
    guard let fromView = fromViewController.view, // 7.0 & 8.0 compatible vs. viewForKey:
        let toView = toViewController.view     // 7.0 & 8.0 compatible vs. viewForKey:
        else {
            assertionFailure("fix this")
            return
    }
    containerView.addSubview(fromView)
    containerView.addSubview(toView)

    let offScreenRight = CGAffineTransform(translationX: containerView.frame.width, y: 0)
    let offScreenLeft = CGAffineTransform(translationX: -containerView.frame.width, y: 0)

    fromView.transform = CGAffineTransform.identity
    toView.transform = self.presenting ? offScreenRight : offScreenLeft

    UIView.animate(withDuration: duration, delay:0, options:UIViewAnimationOptions(rawValue: 0), animations: {
        fromView.transform = self.presenting ? offScreenLeft : offScreenRight
        toView.transform = CGAffineTransform.identity

    }, completion: { (finished: Bool) in
        ctx.completeTransition(finished)
        if finished {
            fromView.removeFromSuperview()
             // this screws up dismissal. at least on ios10 it does               fromView.frame = toView.frame // reset the frame for reuse, otherwise frame transforms will accumulate
        }
    })
}
}

以及正在推送/关闭的 VC 的 transitioniningDelegate 实现:

extension WhateverVCyouArePresentingFrom: UIViewControllerTransitioningDelegate
{
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
    return LTRTransition(presenting: true)
}

public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
    return LTRTransition(presenting: false)
}
}

【讨论】:

    【解决方案4】:

    它看起来很棘手,但只需几行代码即可完成。

    首先,以模态方式呈现 LeftSlideViewController。您需要将 modalPresentationStyle 指定为 .overCurrentContext。

    if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LeftSlideViewController") as? LeftSlideViewController {
             vc.modalPresentationStyle = .overCurrentContext
            self.present(vc, animated: false, completion: nil)
        }
    

    然后,打开 LeftSlideViewController。

    从左侧移入(使用 CATransition)

    • 在 loadView 中隐藏视图。

      self.view.isHidden = true
      
    • 在 viewDidAppear 中的视图中添加左移过渡动画

      self.view.isHidden = false
      
      let transition = CATransition.init()
      transition.duration = 0.3
      transition.timingFunction = CAMediaTimingFunction.init(name: .easeInEaseOut)
      transition.type = .moveIn
      transition.subtype = .fromLeft
      
      self.view.layer.add(transition, forKey: nil)
      

    向左移动(使用 UIView 动画)

    • 使用 UIView 的动画为视图的框架设置动画,并在动画完成时关闭 ViewController

      UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
          self.view.frame = CGRect(x: self.view.frame.width * -1, y: 0, width: self.view.frame.width, height: self.view.frame.height)
      }) { (finished) in
          self.dismiss(animated: false, completion: nil)
      }
      

    【讨论】:

      【解决方案5】:

      只有四种 UIModalTransitionStyles:

      UIModalTransitionStyleCoverVertical
      UIModalTransitionStyleFlipHorizontal
      UIModalTransitionStyleCrossDissolve
      UIModalTransitionStylePartialCurl
      

      例如:

      UIViewController *controller = [[[MyViewController alloc] init] autorelease];
      UIModalTransitionStyle trans = UIModalTransitionStyleFlipHorizontal;
      [UIView beginAnimations: nil context: nil];
      [UIView setAnimationTransition: trans forView: [self window] cache: YES];
      [navController presentModalViewController: controller animated: NO];
      [UIView commitAnimations];
      

      【讨论】:

        【解决方案6】:

        您可以在要呈现的视图控制器上设置transitioningDelegate。您必须遵守UIViewControllerTransitioningDelegateUIViewControllerAnimatedTransitioning 协议。通过实现animateTransition(transitionContext: UIViewControllerContextTransitioning),您可以为任一视图控制器子视图设置动画。

        执行转换的函数

        class fromViewController : UIViewController {
            let transitionManager = TransitionManager()
        
            func transitionToViewController() {
              toController.transitioningDelegate = transitionManager
              presentViewController(toController, animated: true, completion: nil)
            }
        }
        

        那么 TransitionManager 看起来像:

        import UIKit
        
        class TransitionManager : UIPercentDrivenInteractiveTransition {
            var presenting = false
        }
        
        // MARK: UIViewControllerAnimatedTransitioning
        extension TransitionManager : UIViewControllerAnimatedTransitioning {
            func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
                let container = transitionContext.containerView()
                let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
                let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
        
                let offScreenRight = CGAffineTransformMakeTranslation(container.frame.width, 0)
                let offScreenLeft = CGAffineTransformMakeTranslation(-container.frame.width, 0)
        
                toView.transform = self.presenting ? offScreenRight : offScreenLeft
        
                container.addSubview(toView)
                container.addSubview(fromView)
                let duration = self.transitionDuration(transitionContext)
        
                UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0.8, options: nil, animations: {
                  fromView.transform = self.presenting ? offScreenLeft : offScreenRight
                  toView.transform = CGAffineTransformIdentity
        
                  }, completion: { finished in
                    transitionContext.completeTransition(true)
                })
            }
        
        
            func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
                return 0.3
            }
        }
        
        
        // MARK: UIViewControllerTransitioningDelegate
        extension TransitionManager : UIViewControllerTransitioningDelegate {
            func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
                self.presenting = true
                return self
            }
        
            func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
                self.presenting = false
                return self
            }
        }
        

        【讨论】:

        • 不起作用。 self.presenting == false 的情况导致黑屏和错误的帧
        • @Barry 这适用于我不使用 segues 而是在不同故事板中的控制器之间手动转换。通过查看您在上面发布的代码,您正试图覆盖 UINavigationController 的推送转换。您是否将故事板中的 segue 设置为推送而不是呈现?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-01
        • 2011-07-06
        • 1970-01-01
        • 1970-01-01
        • 2019-09-04
        • 1970-01-01
        相关资源
        最近更新 更多