UIViewController 自定义过渡风格(由内而外呈现)

Posted

技术标签:

【中文标题】UIViewController 自定义过渡风格(由内而外呈现)【英文标题】:UIViewController custom transition style (inside out presentation) 【发布时间】:2017-02-16 20:15:22 【问题描述】:

我想制作一个自定义演示文稿(推送或模态)以在我搜索的“由内而外的演示文稿”中显示视图控制器,但大多数方法是从不同的方向(从右,从上)呈现视图控制器默认方向,但如果有任何解决方案,我需要的是包括在 viewController 中缩放缩放的东西..

【问题讨论】:

“由内而外的演示文稿”?你将不得不解释你在说什么。 你看我的回答了吗?它应该可以解决您的问题! 【参考方案1】:

您可以实现自己的自定义UIViewControllerAnimatedTransitioning。这是 C# 中自定义圆形淡入淡出(由内而外)过渡的示例(在 swift 中应该不难重现):

/// <summary>
/// Circle fade transition animator.
/// </summary>
public class CircleFadeTransitionAnimator : UIViewControllerAnimatedTransitioning

    public bool IsPresentation = true;
    nfloat screenHeight = UIScreen.MainScreen.Bounds.Height;
    nfloat screenWidth = UIScreen.MainScreen.Bounds.Width;

    public override void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
    
        //The current view controller
        var fromVC = transitionContext.GetViewControllerForKey(UITransitionContext.FromViewControllerKey);
        var fromView = fromVC.View;

        //The view controller we wish to present to the user
        var toVC = transitionContext.GetViewControllerForKey(UITransitionContext.ToViewControllerKey);
        var toView = toVC.View;

        // Set up some variables for the animation.
        var containerView = transitionContext.ContainerView;

        // Always add the "to" view to the container.
        // And it doesn't hurt to set its start frame.
        containerView.AddSubview(toView);

        // Creates two circular UIBezierPath instances; one is the size of the button, and the second has a radius
        // large enough to cover the entire screen. The final animation will be between these two bezier paths.
        CGSize size;
        if (fromVC is LogInViewController)
            size = new CGSize(5, 5);
        else
            size = new CGSize(screenWidth * 0.65, screenWidth * 0.65);

        var startingRect = new CGRect(screenWidth / 2 - size.Width/2, screenHeight / 2 - size.Height/2, size.Width, size.Height);
        var circleMaskPathInitial = UIBezierPath.FromOval(startingRect);
        var extremePoint = new CGPoint(screenWidth / 2, screenHeight / 2 - toView.Bounds.Height);
        var radius = (nfloat) Math.Sqrt((extremePoint.X * extremePoint.X) + (extremePoint.Y * extremePoint.Y));
        var circleMaskPathFinal = UIBezierPath.FromOval(startingRect.Inset(-radius, -radius));

        // Creates a new CAShapeLayer to represent the circle mask. Assign its path value with the final circular path 
        // after the animation to avoid the layer snapping back after the animation completes.
        var maskLayer = new CAShapeLayer();
        maskLayer.Path = circleMaskPathFinal.CGPath;
        toVC.View.Layer.Mask = maskLayer;

        // Creates a CABasicAnimation on the path key path that goes from circleMaskPathInitial to circleMaskPathFinal. 
        var maskLayerAnimation = CABasicAnimation.FromKeyPath("path");
        maskLayerAnimation.SetFrom(circleMaskPathInitial.CGPath);
        maskLayerAnimation.SetTo(circleMaskPathFinal.CGPath);
        maskLayerAnimation.Duration = TransitionDuration(transitionContext);
        maskLayerAnimation.Delegate = new CircleAnimationDelegate(transitionContext);
        maskLayer.AddAnimation(maskLayerAnimation, "path");
    

    /// <summary>
    /// How long will the transition last?
    /// </summary>
    /// <returns>The duration.</returns>
    /// <param name="transitionContext">Transition context.</param>
    public override double TransitionDuration(IUIViewControllerContextTransitioning transitionContext)
    
        return 0.3;
    

您还需要实现自己的CAAnimationDelegate,如下所示:

/// <summary>
/// Circle animation delegate.
/// </summary>
class CircleAnimationDelegate : CAAnimationDelegate

    IUIViewControllerContextTransitioning transitionContext;
    //UIView toView;

    /// <summary>
    /// Initializes a new instance of the <see cref="T:Plimes.CircleAnimationDelegate"/> class.
    /// </summary>
    /// <param name="transitionContext">Transition context.</param>
    public CircleAnimationDelegate(IUIViewControllerContextTransitioning transitionContext)
    
        this.transitionContext = transitionContext;
        //this.toView = toView;
    

    /// <summary>
    /// On Animations stopped.
    /// </summary>
    /// <param name="anim">Animation.</param>
    /// <param name="finished">If set to <c>true</c> finished.</param>
    public override void AnimationStopped(CAAnimation anim, bool finished)
    
        transitionContext.CompleteTransition(!transitionContext.TransitionWasCancelled);
        transitionContext.GetViewControllerForKey(UITransitionContext.ToViewControllerKey).View.Layer.Mask = null;
        transitionContext.GetViewControllerForKey(UITransitionContext.FromViewControllerKey).View.Layer.Mask = null;
        //toView.RemoveFromSuperview();
    

然后你可以继承你的UINavigationController 并像这样覆盖它的GetAnimationControllerForOperation() 委托方法:

/// <summary>
/// Navigation controller subclass for assigning custom delegate.
/// </summary>
public partial class NavigationController : UINavigationController

    /// <summary>
    /// Initializes a new instance of the <see cref="T:Plimes.NavigationController"/> class.
    /// </summary>
    /// <param name="handle">Handle.</param>
    public NavigationController (IntPtr handle) : base (handle)
    
        Delegate = new NavigationControllerDelegate();
    


/// <summary>
/// Navigation controller delegate for custom transition animations.
/// </summary>
public class NavigationControllerDelegate : UINavigationControllerDelegate 
 
    /// <summary>
    /// Gets the animation controller for operation.
    /// </summary>
    /// <returns>The animation controller for operation.</returns>
    /// <param name="navigationController">Navigation controller.</param>
    /// <param name="operation">Operation.</param>
    /// <param name="fromViewController">From view controller.</param>
    /// <param name="toViewController">To view controller.</param>
    public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForOperation(UINavigationController navigationController, UINavigationControllerOperation operation, UIViewController fromViewController, UIViewController toViewController)
    
        // If left-right transition is needed
        if (operation == UINavigationControllerOperation.Push)
        
            var animator = new CircleFadeTransitionAnimator();
            animator.IsPresentation = true;
            return animator;
        
       

希望这会有所帮助!

【讨论】:

以上是关于UIViewController 自定义过渡风格(由内而外呈现)的主要内容,如果未能解决你的问题,请参考以下文章

从 Tableview 自定义单元格翻转过渡到 UIViewController

自定义 UIViewController 过渡动画适用于演示,但不适用于解雇

如何使用自定义 UIViewController 转换来实现这一点?

UINavigationController 自定义模态过渡,导航栏太小

使用自定义转换旋转模态 UIViewController

UIViewController 过渡在下拉手势关闭时闪烁