xamarin.ios段:创建自定义转换

曾经想过那些其他应用程序的光滑屏幕过渡是如何?使用转换确实给一个额外的波兰语的应用程序。但是,在用户体验(UX)方面,它将在您的应用程序(UX)时更重要。

在此帖子中,我们将实施以下转型:

定制

在我们进入代码之前。请注意,每次转换都遵循以下步骤::

  • 在目标视图控制器上配置转换
  • 实施转型委托
  • 实现转换动画
  • 如果适用实施解除转换动画

您不必实施驳回转换。但如果您导航回始发页面。你真的应该这样做。

配置自定义转换

较短的题外:在社区中没有共识,最好的方法是写下你的UI。有些人喜欢在代码(如我自己)中这样做,其他人更喜欢使用故事板。所以我们只会看看他们两个。良好的是,除了配置中,还有代码的其余部分是相同的。

使用代码配置自定义转换

IOS中的过渡实际上在目标视图控制器上定义。在我们的示例中,我们将在用户选择按钮时立即导航到下一个视图控制器:

_button.TouchUpInside += (e, s) =>
{
    var vc = new ModalViewController(this)
    {
        ModalPresentationStyle = UIModalPresentationStyle.Custom,
        TransitioningDelegate = new GrowTransitioningDelegate(_button)
    };

    NavigationController.PresentViewController(vc, true, null);
};

Note that we configure the ModalPresentationStyle and TransitionDelegate on the view controller that we are navigating to. The GrowTransitionDelegate takes the originating UIView. If you are not using Storyboards you can skip the section and dive right into how the GrowTransitioningDelegate is implemented.

使用Storyboard配置自定义转换

如果您使用的故事板,您将熟悉SEGUES。要定义自定义转换动画,您必须如下配置SEGUE:

配置故事板Segue以使用当前模态并将演示文稿和转换留给默认值。

Then in the originating view controller you can override the PrepareForSegue method:

public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue(segue, sender);

    var destinationVC = segue.DestinationViewController as SecondViewController;
    destinationVC.Callee = this;
    destinationVC.TransitioningDelegate = new GrowTransitioningDelegate(sender as UIView);
    destinationVC.ModalPresentationStyle = UIModalPresentationStyle.Custom;
}

The target view controller in our case is name SecondViewController. It looks fairly similar to the event handler we defined before. On the destination view controller we set the TransitioningDelegate to GrowTransitioningDelegate which takes the originating UIView as constructor parameter. The only difference is that we do no longer pass the instance of the calling view controller as constructor parameter but use a property named Callee on the SecondViewController.

设置代表

The GrowTransitionDelegate inherits from UIViewControllerTransitioningDelegate from which we can override methods to add a custom animation for presenting as follows:

public class GrowTransitioningDelegate : UIViewControllerTransitioningDelegate
{
    readonly UIView _animationOrigin;

    public GrowTransitioningDelegate(UIView animationOrigin)
    {
        _animationOrigin = animationOrigin;
    }

    public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForPresentedController(UIViewController presented, UIViewController presenting, UIViewController source)
    {
        var customTransition = new GrowTransitionAnimator(_animationOrigin);
        return customTransition;
    }
}

Let’s follow along the scenario of the user navigating to another page. The GetAnimationControllerForPresentedController provides an animation implementation that inherits from IUIViewControllerAnimatedTransitioning. The originating UIView, a button in this example, is passed to the animation in the constructor. And that is all the transitioning delegate has to implement. So let’s see how we implement the actual animation.

实现转换动画

The animations are defined in a class that inherit from UIViewControllerAnimatedTransitioning. The following two methods have to implemented:

public class GrowTransitionAnimator : UIViewControllerAnimatedTransitioning
{
    readonly UIView _animationOrigin;

    public GrowTransitionAnimator(UIView animationOrigin)
    {
        _animationOrigin = animationOrigin;
    }

    public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
    {
        // The animation 
    }

    public override double TransitionDuration(IUIViewControllerContextTransitioning transitionContext)
    {
        return 0.3;
    }
}

The TransitionDuration method defines how long the animation will take. Usually this should be around 300 to 500 milliseconds. In the AnimateTransition method the actual transition and animation are defined.

iOS SDK提供了创建动画等动画的功能。使用这些将显着降低创建动画所需的努力,以定义UI对象的初始和期望的目标状态。动画的渲染是由GPU上的iOS执行的。所以你的应用程序看起来不仅会很棒,因为GPU早餐吃转化就会变得更加糟糕 微笑

The AnimateTransition method usually follows the following pattern:

  1. 获取您的源和目标视图控制器和视图
  2. 获取动画容器视图并添加目的地
  3. 定义动画状态
    1. 目标视图的最终状态
    2. 目标视图的初始状态
  4. 执行动画并在完成后通知转换上下文

所以让我们逐步完成这一步。首先让我们获取源和目标视图控制器和视图:

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // Get the from and to View Controllers and their views
    var fromVC = transitionContext.GetViewControllerForKey(UITransitionContext.FromViewControllerKey);
    var fromView = fromVC.View;
    
    var toVC = transitionContext.GetViewControllerForKey(UITransitionContext.ToViewControllerKey);
    var toView = toVC.View;
     
    // ...
}

然后获取动画容器并将目的地添加到它:

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // ...

    // Add the to view to the transition container view
    var containerView = transitionContext.ContainerView;
    containerView.AddSubview(toView);
        
    // ...
}

现在是我们将定义动画的部分。我们希望在用户选择的按钮中间开始动画即e。我们的动画类已经收到了构造函数。

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // ...

    // Set the desired target for the transition
    var appearedFrame = transitionContext.GetFinalFrameForViewController(toVC);
    
    // Set how the animation shall start
    var initialFrame = new CGRect(_animationOrigin.Frame.GetMidX(), _animationOrigin.Frame.GetMidY(), 0, 0);
    var finalFrame = appearedFrame;
    toView.Frame = initialFrame;
    
    // ...
}

现在剩下的是执行动画并等待结果。然后将结果用于发信号通知动画已完成的转换上下文:

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // ...

    var isAnimationCompleted = await UIView.AnimateAsync(TransitionDuration(transitionContext), () => {
        toView.Frame = finalFrame;
    });
    
    transitionContext.CompleteTransition(isAnimationCompleted);
}

我们的过渡动画完成了。如果您正在寻求实现不同的动画。例如,动画从角落开始的一个。更改动画的原点将为您提供所需的效果。

副作用 While animations are great and you will perhaps notice that some UI elements are drawn early and then move into position during the transition. This can feel slightly off. So while working with animations it can make sense to move the layout code of the UI components to the ViewDidAppear method.

创建解除动画

For having the inverse animation for dismissing the view. First the GetAnimationControllerForDismissedController method has to be overwritten in the delegate class (same one as before):

public class GrowTransitioningDelegate : UIViewControllerTransitioningDelegate
{
    readonly UIView _animationOrigin;

    public GrowTransitioningDelegate(UIView animationOrigin)
    {
        _animationOrigin = animationOrigin;
    }

    public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForPresentedController(UIViewController presented, UIViewController presenting, UIViewController source)
    {
        // ...
    }

    public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForDismissedController(UIViewController dismissed)
    {
        var customTransition = new ShrinkTransitionAnimator(_animationOrigin);
        return customTransition;
    }
}

Then create an implementation which inherits UIViewControllerAnimatedTransitioning and has the desired reverse animation.

public class ShrinkTransitionAnimator : UIViewControllerAnimatedTransitioning
{
    readonly UIView _animationOrigin;

    public ShrinkTransitionAnimator(UIView animationTarget)
    {
        _animationOrigin = animationTarget;
    }

    public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
    {
        // Get the from and to View Controllers and their views
        var fromVC = transitionContext.GetViewControllerForKey(UITransitionContext.FromViewControllerKey);
        var fromView = fromVC.View;

        var toVC = transitionContext.GetViewControllerForKey(UITransitionContext.ToViewControllerKey);
        var toView = toVC.View;

        // Add the to view to the transition container view
        var containerView = transitionContext.ContainerView;

        // Set the desired target for the transition
        var appearedFrame = transitionContext.GetFinalFrameForViewController(fromVC);

        // Set how the animation shall end
        var finalFrame = new CGRect(_animationOrigin.Frame.GetMidX(), _animationOrigin.Frame.GetMidY(), 0, 0);
        fromView.Frame = appearedFrame;

        var isAnimationCompleted = await UIView.AnimateAsync(TransitionDuration(transitionContext), () => {
            fromView.Frame = finalFrame;
        });

        fromView.RemoveFromSuperview();

        transitionContext.CompleteTransition(isAnimationCompleted);
    }

    public override double TransitionDuration(IUIViewControllerContextTransitioning transitionContext)
    {
        return 0.3;
    }
}

虽然看起来有一些关键点。对于这一时间来说,这次是我们想要解雇的视图控制器。由于它已经是视图树的一部分,因此不再增加它。相反,在动画完成后,您实际上要删除它。

结论

In this post we went through the steps required to create a custom transition. Further we looked at how we can implement the dismiss animation. The steps can be reused for different kind of animations which are defined in the implementation of the UIViewControllerAnimatedTransitioning class.

您可以找到完整的代码样本 GitHub..

Updated: