【问题标题】:Custom segue animation (navigation animation) in SwiftUISwiftUI 中的自定义 segue 动画(导航动画)
【发布时间】:2019-12-29 13:53:37
【问题描述】:

在 SwiftUI 中,我们使用 NavigationViewNavigationLink 视图来执行导航(我们过去在 UIKit 中称为 segue)。 UIKit 中的标准segue 是show segue。在 SwiftUI 中我们可以简单地做:

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: Text("Destination")) {
                    Text("Navigate!")
                }
            }
        }
    }
}

具有完全相同的效果(即使 segue 这个词已经消失)。

有时(实际上相当频繁)我们需要自定义转场动画。

  • 我们可以决定根本不为 segue 设置动画,实际上在 故事板我们可以找到属性Animates (true/false) 通过单击 segue 来进行属性检查器。这样,目标视图控制器会立即出现在源视图控制器的位置。
  • 或者我们可以决定执行自定义动画。通常这是通过实现一个符合UIViewControllerAnimatedTransitioning 协议的对象来完成的。所有的魔法都发生在 animateTransition 方法中,它使我们能够访问源视图控制器和目标视图控制器。

例如,一个简单的交叉淡入淡出转场动画可能是这样的:

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    UIView* containerView = [transitionContext containerView];
    UIViewController* fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController* toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    toVC.view.alpha = 0;
    [containerView addSubview:toVC.view];

    [UIView animateWithDuration:1 animations:^{
        toVC.view.alpha = 1;
        fromVC.view.alpha = 0;
    } completion:^(BOOL finished) {
        [fromVC.view removeFromSuperview];
        [transitionContext completeTransition:YES];
    }];
}

现在的问题是:如何在 SwiftUI 中获得相同的效果?是否可以不为导航设置动画或自定义导航动画?我希望能够做到:

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: Text("Destination")) {
                    Text("Navigate!")
                }
            }
        }
        .animation(nil)
    }
}

或类似的东西阻止动画(或添加自定义动画),但没有任何变化。

【问题讨论】:

  • 有些过渡可以使用甚至组合,但我自己没有尝试过。
  • 我认为 SwiftUI 还没有内置这个功能,但可以通过一些黑客攻击来实现它。如果我以后想到什么我会发布答案
  • 要禁用导航动画检查这个答案:stackoverflow.com/a/59481362/8397245
  • @FRIDDAY 谢谢,非常好的解决方法,我赞成你的回答。如果我们也能找到一种自定义过渡动画的方法,那就太好了。
  • 自定义动画也存在于 SwiftUI 中 swiftui-lab.com/advanced-transitions

标签: ios swift animation uikit swiftui


【解决方案1】:

不幸的是,仍然无法在 SwiftUI (xCode 11.3.1) 中自定义 NavigationView 过渡动画。

寻找一种方法,我最终创建了这个名为swiftui-navigation-stack (https://github.com/biobeats/swiftui-navigation-stack) 的开源项目,其中包含NavigationStackView,这是一个模仿标准NavigationView 导航行为的视图,添加了一些有用的特征。例如,您可以关闭转场动画,也可以使用您喜欢的转场对其进行自定义。

几个月前,我在这里问过如何关闭过渡动画。为此,您可以这样使用NavigationStackView

struct ContentView : View {
    var body: some View {
        NavigationStackView(transitionType: .none) {
            ZStack {
                Color.yellow.edgesIgnoringSafeArea(.all)

                PushView(destination: View2()) {
                    Text("PUSH")
                }
            }
        }
    }
}

struct View2: View {
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            PopView {
                Text("POP")
            }
        }
    }
}

如果您将.none 指定为transitionType,您可以关闭动画。 PushViewPopView 是两个允许你推送和弹出视图的视图(类似于 SwiftUI NavigationLink)。

结果是:

如果您想要自定义过渡动画(正如我在上面的问题中使用交叉淡入淡出示例所问的那样),您可以这样做:

struct ContentView : View {
    let navigationTransition = AnyTransition.opacity.animation(.easeOut(duration: 2))

    var body: some View {
        NavigationStackView(transitionType: .custom(navigationTransition)) {
            ZStack {
                Color.yellow.edgesIgnoringSafeArea(.all)

                PushView(destination: View2()) {
                    Text("PUSH")
                }
            }
        }
    }
}

struct View2: View {
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)

            PopView {
                Text("POP")
            }
        }
    }
}

对于我指定的交叉转换:

let navigationTransition = AnyTransition.opacity.animation(.easeOut(duration: 2))

持续 2 秒的交叉淡入淡出过渡。然后我将这个过渡传递给NavigationStackView

NavigationStackView(transitionType: .custom(navigationTransition))

【讨论】:

  • 感谢您构建此框架!有没有办法在直接视图之外导航?我担心我的视图会过多了解应用的导航结构,即我会将视图紧密耦合在一起。
  • 另外,作为奖励,如果您能谈谈如何直接回答问题,即如何使用您的框架自定义动画,那就太好了。
  • 嗨@Zorayr!关于你的第一个问题:为了得到你想要的,你必须改变框架。 ATM 导航堆栈是由 NavigationStackView 创建的,因此您只能在 NavigationStackView 内部访问它。不过,改变这一点并不难:您可以让 NavigationStackView 在创建时将 NavigationStack 作为参数。然后,您可以在任意位置创建 NavigationStack 并将其注入 NavigationStackView。这样,您就可以在 NavigationStackView 之外使用 NavigationStack(例如 navStack.push(MyView()))进行导航。
  • 关于第二条评论:实际上,正如我在回答中所说,没有办法自定义与使用标准 NavigationView/NavigationLink 执行的导航相关的动画。我很确定这个功能很快就会推出(也许在下一次 WWDC 期间?),但目前 SwiftUI 仍然缺少这个东西。
猜你喜欢
  • 2015-08-21
  • 2012-06-02
  • 1970-01-01
  • 2013-10-15
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
相关资源
最近更新 更多