SwiftUI 中的自定义转场动画(导航动画) [英] Custom segue animation (navigation animation) in SwiftUI
问题描述
在 SwiftUI 中,我们使用 NavigationView
和 NavigationLink
视图来执行导航(我们过去在 UIKit
segue>).UIKit 中的标准转场是 show
转场.在 SwiftUI 中,我们可以简单地做:
struct ContentView: 查看 {var主体:一些视图{导航视图{虚拟堆栈{NavigationLink(目的地:文本(目的地")){文本(导航!")}}}}}
具有完全相同的效果(即使 segue
这个词已经消失).
有时(实际上相当频繁)我们需要自定义转场动画.
- 我们可以决定根本不为转场设置动画,事实上在我们可以在storyboard中找到属性
Animates
(true/false)通过单击 segue 来查看属性检查器.这样,目标视图控制器会立即出现在源视图控制器的位置. - 或者我们可以决定执行自定义动画.通常这是通过实现一个符合
UIViewControllerAnimatedTransitioning
协议的对象来完成的.所有的魔法都发生在animateTransition
方法中,该方法使我们能够访问源视图控制器和目标视图控制器.
例如,一个简单的淡入淡出转场动画可能类似于:
-(void)animateTransition:(id)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 动画:^{toVC.view.alpha = 1;fromVC.view.alpha = 0;}完成:^(BOOL完成){[fromVC.view removeFromSuperview];[transitionContext completeTransition:YES];}];}
现在的问题是:如何在 SwiftUI 中获得相同的结果?是否可以不为导航设置动画或自定义导航动画?我希望能够做到:
struct ContentView: 查看 {var主体:一些视图{导航视图{虚拟堆栈{NavigationLink(目的地:文本(目的地")){文本(导航!")}}}.动画(无)}}
或类似的东西来阻止动画(或添加自定义动画),但没有任何变化.
遗憾的是,仍然无法在 SwiftUI (xCode 11.3.1) 中自定义 NavigationView
过渡动画.
为了找到一种方法,我最终创建了这个名为 swiftui-navigation-stack
(
如果您想自定义过渡动画(正如我在上面的交叉淡入淡出示例中提出的问题),您可以这样做:
struct ContentView : 查看 {让 navigationTransition = AnyTransition.opacity.animation(.easeOut(duration: 2))var主体:一些视图{NavigationStackView(transitionType: .custom(navigationTransition)) {ZStack {Color.yellow.edgesIgnoringSafeArea(.all)PushView(目的地:View2()){文本(推")}}}}}结构视图2:查看{var主体:一些视图{ZStack {Color.green.edgesIgnoringSafeArea(.all)弹出视图{文本(POP")}}}}
对于我指定的跨阶段过渡:
let navigationTransition = AnyTransition.opacity.animation(.easeOut(duration: 2))
持续 2 秒的淡入淡出过渡.然后我将这个转换传递给了 NavigationStackView
:
NavigationStackView(transitionType: .custom(navigationTransition))
In SwiftUI we use NavigationView
and NavigationLink
views to perform navigations (what we used to call segue
in UIKit
). The standard segue in UIKit is the show
segue. In SwiftUI we can simply do:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Destination")) {
Text("Navigate!")
}
}
}
}
}
to have the exact same effect (even if the word segue
has disappeared).
Sometimes (actually rather often) we need to customise the segue animation.
- We can decide not to animate the segue at all, indeed in the
storyboard we can find the attribute
Animates
(true/false) in the attribute inspector by clicking on a segue. This way the destination view controller appears immediately in place of the source view controller. - Or we can decide to perform a custom animation. Usually this is done by implementing an object that conforms to the
UIViewControllerAnimatedTransitioning
protocol. All the magic happens in theanimateTransition
method that gives us access to the source view controller and the destination view controller.
For example, a simple cross-fade segue animation could be something like:
-(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];
}];
}
Now the question: how can I get the same in SwiftUI? Is it possible not to animate a navigation or to customise the navigation animation? I expected to be able to do:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Destination")) {
Text("Navigate!")
}
}
}
.animation(nil)
}
}
or something similar to prevent the animation (or to add a custom animation), but nothing changes.
Unfortunately it's still not possible to customise the NavigationView
transition animations in SwiftUI (xCode 11.3.1).
Looking for a way to do that I ended up creating this open source project called swiftui-navigation-stack
(https://github.com/biobeats/swiftui-navigation-stack) that contains the NavigationStackView
, a view that mimics the navigation behaviours of the standard NavigationView
adding some useful features. For example you can turn off the transition animations or you can customise them with the transition you prefer.
Months ago I asked here above how to turn off transition animations. To do that you can use the NavigationStackView
this way:
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")
}
}
}
}
If you specify .none
as transitionType
you can turn off the animations. PushView
and PopView
are two views that allow you push and pop views (similar to the SwiftUI NavigationLink
).
The result is:
If you, instead, want to customise the transition animation (as I asked in the question here above with the cross-fade example) you can do like this:
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")
}
}
}
}
For a cross-fase transition I specified:
let navigationTransition = AnyTransition.opacity.animation(.easeOut(duration: 2))
a cross fade transition that lasts 2 seconds. Then I passed this transition to the NavigationStackView
:
NavigationStackView(transitionType: .custom(navigationTransition))
这篇关于SwiftUI 中的自定义转场动画(导航动画)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!