Git Product home page Git Product logo

swiftui-navigation-transitions's Introduction

NavigationTransitions

CI

NavigationTransitions is a library that integrates seamlessly with SwiftUI's NavigationView and NavigationStack, allowing complete customization over push and pop transitions!

Overview

Instead of reinventing the entire navigation stack just to control its transitions, NavigationTransitions ships with a simple modifier that can be applied directly to SwiftUI's very own first-party navigation components.

The Basics

iOS 13+

NavigationView {
  // ...
}
.navigationViewStyle(.stack)
.navigationTransition(.slide)

iOS 16+

NavigationStack {
  // ...
}
.navigationTransition(.slide)

The API is designed to resemble that of built-in SwiftUI Transitions for maximum familiarity and ease of use.

You can apply custom animations just like with standard SwiftUI transitions:

.navigationTransition(
    .fade(.in).animation(.easeInOut(duration: 0.3))
)

You can combine them:

.navigationTransition(
    .slide.combined(with: .fade(.in))
)

And you can dynamically choose between transitions based on logic:

.navigationTransition(
    reduceMotion ? .fade(.in).animation(.linear) : .slide(.vertical)
)

Transitions

The library ships with some standard transitions out of the box:

In addition to these, you can create fully custom transitions in just a few lines of SwiftUI-like code!

struct Swing: NavigationTransition {
    var body: some NavigationTransition {
        Slide(axis: .horizontal)
        MirrorPush {
            let angle = 70.0
            let offset = 150.0
            OnInsertion {
                ZPosition(1)
                Rotate(.degrees(-angle))
                Offset(x: offset)
                Opacity()
                Scale(0.5)
            }
            OnRemoval {
                Rotate(.degrees(angle))
                Offset(x: -offset)
            }
        }
    }
}

The Demo app showcases some of these transitions in action.

Interactivity

A sweet additional feature is the ability to override the behavior of the pop gesture on the navigation view:

.navigationTransition(.slide, interactivity: .pan) // full-pan screen gestures!

This even works to override its behavior while maintaining the default system transition in iOS:

.navigationTransition(.default, interactivity: .pan) // ✨

Documentation

The documentation for releases and main are available here:

Other versions

swiftui-navigation-transitions's People

Contributors

davdroman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

swiftui-navigation-transitions's Issues

Dynamic change of interactivity is not supported?

My code is:

@State var enableInteractivity = false

NavigationStack(path: $stack) {
    ...
}
.navigationTransition(.default, interactivity: enableInteractivity ? .edgePan : .disabled)

I change enableInteractivity flag in the onAppear { } of views in the NavigationStack, however, it doesn't work on iOS 16 and iOS 17 - the initial value of enableInteractivity is always used.

Transition starts working only after second execution

Discussed in #117

Originally posted by izombieprod December 19, 2023
Every time after building the application, the first transition is the default slide every time. Your animation is applied only after you call those views one time with default transition animation. Do you know what I'm doing wrong?

slide-inverse

Would it be possible to add a slide-inverse animation also? I mean in a ltr application from left to right or if you prefer from leading to trailing.
It would be a convenient feature to have out of the box.
It's really fast to code too, I made it in my project like this:

import AtomicTransition
import SwiftUI
import NavigationTransitions

extension AnyNavigationTransition {
    /// A transition that moves both views in and out along the specified axis.
    ///
    /// This transition:
    /// - Pushes views left-to-right and pops views left-to-right when `axis` is `horizontal`.
    /// - Pushes views top-to-bottom and pops views top-to-bottom when `axis` is `vertical`.
    public static func inverseSlide(axis: Axis) -> Self {
        .init(InverseSlide(axis: axis))
    }
}

extension AnyNavigationTransition {
    /// Equivalent to `slide(axis: .horizontal)`.
    @inlinable
    public static var slide: Self {
        .inverseSlide(axis: .horizontal)
    }
}

/// A transition that moves both views in and out along the specified axis.
///
/// This transition:
/// - Pushes views left-to-right and pops views left-to-right when `axis` is `horizontal`.
/// - Pushes views top-to-bottom and pops views top-to-bottom when `axis` is `vertical`.
public struct InverseSlide: NavigationTransition {
    private let axis: Axis

    public init(axis: Axis) {
        self.axis = axis
    }

    /// Equivalent to `Move(axis: .horizontal)`.
    @inlinable
    public init() {
        self.init(axis: .horizontal)
    }

    public var body: some NavigationTransition {
        switch axis {
        case .horizontal:
            MirrorPush {
                OnInsertion {
                    Move(edge: .leading)
                }
                OnRemoval {
                    Move(edge: .trailing)
                }
            }
        case .vertical:
            MirrorPush {
                OnInsertion {
                    Move(edge: .top)
                }
                OnRemoval {
                    Move(edge: .bottom)
                }
            }
        }
    }
}

extension InverseSlide: Hashable {}

UI freezing on iOS 17 device

Hi @davdroman, thanks so much for making this library.
Recently, I bumped into a strange issue which is 100% reproducible on iOS 17 device (not simulator)

Reproducible setup within Demo app

  • Use default transition and pan interactive gesture
  • PageOne has navigation bar visible, while PageTwo has navigation bar hidden

Reproduce steps
From PageOne, go to PageTwo then using pan gesture to go back to PageOne, then tap Show me! button again.
The UI will hang forever

You can check out my branch for the demo: https://github.com/muzix/swiftui-navigation-transitions/tree/muzix/freezing-issue-ios-17

Investigating
I think this is a SwiftUI bug. But I found that adding setNavigationBarHidden(false, animated: true|false) into NavigationTransitionDelegate somehow fixed this issue.

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        navigationController.setNavigationBarHidden(false, animated: true)
        initialAreAnimationsEnabled = UIView.areAnimationsEnabled
	UIView.setAnimationsEnabled(transition.animation != nil)
	baseDelegate?.navigationController?(navigationController, willShow: viewController, animated: animated)
}

But it has another side effect which make navigation bar blinking when transitioning between screens with hidden nav bar.

how to create a 'nothing' transition

i was wondering if there is a way to create a (custom) transition that does nothing. i.e. immediately jumps from one view to the next.

for transitioning between two views this appearance can be achieved by clearing and immediately setting the navigation path.

but for the transition from an empty path to a single element or from a single element to to an empty path the default transition is always used.

thanks
andre

Change transition ?

Hi,
I'm using it and it works fine, except 1 problem:

I want to use default transition for subview transition

What i do:

NavigationStack(path: $path) {
...
.navigationDestination(for: MediaItemInfo.self) { item in
    ...
}
.navigationStackTransition(
    .slide(axis: .vertical).animation(.linear(duration: 0.5))
)

Later, in the new view, i need the default transition for all subviews

NavigationLink(destination: ...) {
}
.navigationStackTransition(.default)

What is strange, is the transition are still the .slide one, but when i pop the new view, i have the standard transition.

What i'm missing ? Thanks

3D Transitions

A bit of an unknown for me as I've never really worked with the 3D transform APIs, but good to explore nonetheless.

Error When Archive (Command SwiftCompile failed with a nonzero exit code)

Only with the last update 0.10.0 my app works good when build, but when archive to submit to Appstore I got error (Command SwiftCompile failed with a nonzero exit code)

6.	While evaluating request QualifiedLookupRequest(0x12dd764b0 StructDecl name=NotionAppsView, {Apps.(file).NotionAppsView@/Users/itarek/Library/Mobile Documents/NotionAppsView.swift:14:8}, 'Body', { NL_ProtocolMembers, NL_RemoveNonVisible, NL_RemoveOverridden, NL_OnlyTypes })
7.	While evaluating request DirectLookupRequest(directly looking up 'Body' on SwiftUI.(file).View with options {  })
8.	While loading members for extension of View (in module 'NavigationTransitions')
9.	While deserializing 'navigationTransition' (FuncDecl @ 392035) in 'NavigationTransitions'
10.	    ...decl is named 'navigationTransition(_:interactivity:)'
11.	While deserializing '_' (OpaqueTypeDecl @ 374635) in 'NavigationTransitions'
12.	*** DESERIALIZATION FAILURE ***
*** If any module named here was modified in the SDK, please delete the ***
*** new swiftmodule files from the SDK and keep only swiftinterfaces.   ***
module 'NavigationTransitions', builder version '5.8.1(5.8.1)/Apple Swift version 5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)', built from source, non-resilient, loaded from '/Users/itarek/Library/Developer/Xcode/DerivedData/BuildProductsPath/Release-iphoneos/NavigationTransitions.swiftmodule/arm64-apple-ios.swiftmodule'
module with extension is not loaded (SwiftUIIntrospect)

If i switch back to version [0.9.3] everything works again.

Can you help?

Navigation bar disappears after rotating device in destination view and coming back

Can be reproduced in iOS 17 using Xcode 15. Not sure about other versions.

I have a NavigationStack inside a sheet.

Inside that NavigationStack, I navigate to a destination using the following transition:

.navigationTransition(.fade)

If I reproduce the following behaviors:

  • Open the sheet in portrait mode
  • Navigate to the destination in portrait mode
  • Switch to landscape mode
  • Navigate back to the previous screen

The navigation bar will be hidden.

I was wondering if there is a workaround to fix this, or if a fix could be introduced for this.

Many thanks!

DocC

I wanted to get the library out asap so I didn't have time to generate DocC, but now that it's out this should be my next task.

present animation

hello, i want to show view like present full screen view animation

i tried slide(axis: .vertical)) but animation not like present

Change fade transient view background color

thank you for creating this library @davdroman!

I have a global background color in my app (implemented using the ZStack approach). How do I change the fade transition to have the same color?

RPReplay_Final1703972675.mov

notice how the background changes to white (black if dark mode) during the fade transition.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.