Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
475 views
in Technique[技术] by (71.8m points)

ios - Custom segue animation (navigation animation) in SwiftUI

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 the animateTransition 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

enter image description here

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))

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...