Swiftpack.co - globulus/swiftui-segues as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by globulus.
globulus/swiftui-segues 1.1.0
Easy-to-use segues for SwiftUI.
⭐️ 5
🕓 2 weeks ago
iOS
.package(url: "https://github.com/globulus/swiftui-segues.git", from: "1.1.0")

SwiftUI Segues

Logo

Easy-to-use segues in SwiftUI, allowing for presenting views using common UIKIt Segue types - push, modal and popover.

Preview

Navigating between views/screens in SwiftUI is more difficult and convoluted than it is in UIKit, with different segues dispersed over multiple views (e.g, NavigationLink) and modifiers (popover, fullScreenCover). Moreover, part of this functionality isn't available on iOS 13.

This package contains two View Modifiers that allow for seamless integration of segues into your SwiftUI code, and it's fully compatible with iOS 13 and above. The segues are triggered by setting binding values, and can be dismissed by setting the value to nil. Yep, it's as easy as this:

.segue(.push, tag: .pushTest, selection: $route) {
    Text("Welcome to Push")
}

or

.segue(.push, selection: $value) { value in
    Text("Welcome to \(value)")
}

Installation

This component is distributed as a Swift package. Just add this repo's URL to XCode:

https://github.com/globulus/swiftui-segues

You can also use CocoaPods:

pod 'SwiftUISegues', '~> 1.1.0'

How to use

  • A good SwiftUI navigation practice is to define all routes, i.e transitions from the current view to subsequent ones, in an enum. Then, add a @State var to your view (or @Published var in your VM) whose value is an optional enum route. This is consistent with the tag/selection and item variants of NavigationLink / Popover / FullScreenCover.
  • Assign a value to the route binding to trigger a segue, and assign it to nil to dismiss it.
  • Available segue types:
    • push - a standard push/pop transition that requires a NavigationView somewhere in the view hierarchy.
    • modal - presents a full-screen cover can't readily be dismissed by the user.
    • popover - presents a part-screen cover that can be dismissed by the user by pulling down from the top. You can specify the PopoverAttachmentAnchor and Edge of the popover.
    • switch - conditionally replaces one view with the other, allowing you to specify the AnyTransition and Animation that take place when the switching happens. This is essentially the custom segue type.
  • You can also use value segues - whenever the selection binding's value is not nil, the segue will trigger and provide the unwrapped value to the destination block.

Mixed segues

To add a single segue of a certain type that's triggered when its route binding (selection)'s value is set to a certain tag, use the segue modifier. Specify its destination view in the view builder block:

struct MixedSegueTest: View {
    // All the routes that lead from this view to the next ones
    enum Route: Hashable {
        case pushTest, modalTest, popoverTest
    }
    
    // Triggers segues when its values are changes
    @State private var route: Route? = nil
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Button("Push") {
                    // Navigate by setting route values
                    route = .pushTest
                }
                Button("Modal") {
                    route = .modalTest
                }
                Button("Popover") {
                    route = .popoverTest
                }
            }
            .navigationBarTitle("SwiftUI Segues", displayMode: .inline)
            
            // Here are individual, mixed segues, with their destinations
            .segue(.push, tag: .pushTest, selection: $route) {
                Text("Welcome to Push")
            }
            .segue(.modal, tag: .modalTest, selection: $route) {
                Button("Welcome to modal") {
                    route = nil
                }
            }
            .segue(.popover(.rect(.bounds), .top), tag: .popoverTest, selection: $route) {
                Text("Welcome to Popover")
            }
        }
    }
}

Multiple segues of the same type

If all the segues bound to the same selection are of the same type (push, modal or popover), use the segues modifier:

struct PushSegueTest: View {
    @State private var route: Route? = nil
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Go to A") {
                    route = .a
                }
                Button("Go to B") {
                    route = .b
                }
                Button("Go to C") {
                    route = .c
                }
            }
            .segues(.push, selection: $route) { route in
                switch route {
                case .a:
                    Text("A")
                case .b:
                    Text("B")
                case .c:
                    Text("C")
                }
            }
        }
    }
    
    enum Route: Identifiable, CaseIterable, Hashable {
        case a, b, c
        
        var id: String {
            "\(self)"
        }
    }
}

struct ModalSegueTest: View {
    @State private var route: Route? = nil
    
    var body: some View {
        VStack {
            Button("Go to A") {
                route = .a
            }
            Button("Go to B") {
                route = .b
            }
            Button("Go to C") {
                route = .c
            }
        }
        .segues(.modal, selection: $route) { route in
            switch route {
            case .a:
                Button("A") {
                    self.route = nil // dismissed the segue
                }
            case .b:
                Button("B") {
                    self.route = nil
                }
            case .c:
                Button("C") {
                    self.route = nil
                }
            }
        }
    }
    
    enum Route: Identifiable, CaseIterable, Hashable {
        case a, b, c
        
        var id: String {
            "\(self)"
        }
    }
}

Recipe

Check out this recipe for in-depth description of the component and its code. Check out SwiftUIRecipes.com for more SwiftUI recipes!

Changelog

  • 1.1.0 - Added value segues.
  • 1.0.2 - Added checks to make sure push segue destinations are lazily loaded (SwiftUI bug workaround).
  • 1.0.1 - Set isDetailLink for push segues to allow for unpacking nested views by setting the binding to nil.
  • 1.0.0 - Initial release.

GitHub

link
Stars: 5
Last commit: 2 weeks ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics