{"id":13778612,"url":"https://github.com/1amageek/Router","last_synced_at":"2025-05-11T12:31:08.220Z","repository":{"id":94515421,"uuid":"324912163","full_name":"1amageek/Router","owner":"1amageek","description":"Router is a library that assists with SwiftUI view transitions.","archived":false,"fork":false,"pushed_at":"2021-02-13T07:05:22.000Z","size":57,"stargazers_count":73,"open_issues_count":0,"forks_count":3,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-08-03T18:13:09.940Z","etag":null,"topics":["navigation","router","swift","swiftui"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1amageek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-12-28T04:33:25.000Z","updated_at":"2024-04-10T06:39:01.000Z","dependencies_parsed_at":"2023-07-28T07:15:43.635Z","dependency_job_id":null,"html_url":"https://github.com/1amageek/Router","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2FRouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2FRouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2FRouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2FRouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1amageek","download_url":"https://codeload.github.com/1amageek/Router/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225048961,"owners_count":17412901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["navigation","router","swift","swiftui"],"created_at":"2024-08-03T18:00:55.340Z","updated_at":"2024-11-17T14:30:40.720Z","avatar_url":"https://github.com/1amageek.png","language":"Swift","funding_links":[],"categories":["Navigation"],"sub_categories":["Content"],"readme":"\n![SwiftUI Router](router.png \"SwiftUI Router\")\n\n# Router\n\nRouter is a library that assists with SwiftUI view transitions.\n\n## Installation\n\n```swift\n.package(name: \"Router\", url: \"git@github.com:1amageek/Router.git\", .upToNextMajor(from: \"0.2.0\")),\n```\n\n## Usage\n\n### Router\nThe `Router` specifies the View to be navigated.\nThe argument of `Router` is the Path of the first View to be displayed. By default, `/` is specified.\n\n### Route\n`Route` will show the View of the Path specified in the argument.\nPath has placeholders and the parameters can be accessed from context.\n\n```swift\nimport SwiftUI\nimport Router\n\nstruct ContentView: View {\n\n    @State var isShow: Bool = false\n\n    var body: some View {\n        Router(\"/weather\") {\n            Route(\"/weather\") { \n                ListView()\n            }\n            Route(\"/weather/{weatherLabel}\") { context in\n                DetailView(label: context.paramaters[\"weatherLabel\"]!)\n            }\n        }\n        .environmentObject(DataStore())\n    }\n}\n```\n\n### Navigator\n\nIt transitions between screens by giving `Navigator` a path.\nYou can specify the transition animation. In the example below, we call the push animation.\n\n```swift\nstruct ListView: View {\n\n    @Environment(\\.navigator) private var navigator: Binding\u003cNavigator\u003e\n\n    @EnvironmentObject var dataStore: DataStore\n\n    var body: some View {\n\n        List {\n            Section(header:\n                        Text(\"Weather\")\n                        .font(.system(size: 24, weight: .black, design: .rounded))\n                        .padding()\n            ) {\n                ForEach(dataStore.data, id: \\.label) { data in\n                    Button(action: {\n                        navigator.push {\n                            navigator.wrappedValue.path = \"/weather/\\(data.label)\"\n                        }\n                    }) {\n                        Label(data.title, systemImage: data.systemImage)\n                            .font(.system(size: 20, weight: .bold, design: .rounded))\n                        Spacer()\n                    }\n                    .buttonStyle(PlainButtonStyle())\n                }\n            }\n        }\n        .listStyle(InsetGroupedListStyle())\n    }\n}\n```\n\nNavigator is defined as an environment, so it can be called from anywhere.\n\n```swift\nstruct DetailView: View {\n\n    @Environment(\\.navigator) private var navigator: Binding\u003cNavigator\u003e\n\n    @EnvironmentObject var dataStore: DataStore\n\n    var label: String\n\n    var weather: Weather? {\n        return self.dataStore.data.filter({$0.label == self.label}).first\n    }\n\n    var body: some View {\n        ZStack {\n            VStack(spacing: 10) {\n                Image(systemName: self.weather!.systemImage)\n                    .font(.system(size: 120, weight: .bold, design: .rounded))\n                Text(label)\n                    .font(.system(size: 30, weight: .bold, design: .rounded))\n            }\n            VStack(alignment: .leading) {\n                HStack {\n                    Button(action: {\n                        navigator.pop {\n                            navigator.wrappedValue.path = \"/weather\"\n                        }\n                    }) {\n                        Image(systemName: \"chevron.backward\")\n                            .font(.system(size: 20, weight: .bold, design: .rounded))\n                    }\n                    .buttonStyle(PlainButtonStyle())\n\n                    Spacer()\n                }\n                Spacer()\n            }\n            .padding()\n        }\n    }\n}\n```\n\n## Custom Transition Animation\n\nTo customize the transition animations, you must first extend AnyTransition.\n\n```swift\npublic extension AnyTransition {\n\n    struct NavigationFrontModifier: ViewModifier {\n        let offset: CGSize\n        public func body(content: Content) -\u003e some View {\n            ZStack {\n                Color(UIColor.systemBackground)\n                content\n            }\n            .offset(offset)\n        }\n    }\n\n    static var navigationFront: AnyTransition {\n        AnyTransition.modifier(\n            active: NavigationFrontModifier(offset: CGSize(width: UIScreen.main.bounds.width, height: 0)),\n            identity: NavigationFrontModifier(offset: .zero)\n        )\n    }\n\n    struct NavigationBackModifier: ViewModifier {\n        let opacity: Double\n        let offset: CGSize\n        public func body(content: Content) -\u003e some View {\n            ZStack {\n                content\n                    .offset(offset)\n                Color.black.opacity(opacity)\n            }\n        }\n    }\n    \n    static var navigationBack: AnyTransition {\n        AnyTransition.modifier(\n            active: NavigationBackModifier(opacity: 0.17, offset: CGSize(width: -UIScreen.main.bounds.width / 3, height: 0)),\n            identity: NavigationBackModifier(opacity: 0, offset: .zero)\n        )\n    }\n}\n```\n\nNext, we will extend Binding\u003cNavigator\u003e.\n\n```swift\npublic extension Binding where Value == Navigator {\n\n    func push\u003cResult\u003e(_ animation: Animation? = .default, _ body: () throws -\u003e Result) rethrows -\u003e Result {\n        let insertion: AnyTransition = .navigationFront\n        let removal: AnyTransition = .navigationBack\n        let transition: AnyTransition = .asymmetric(insertion: insertion, removal: removal)\n        self.wrappedValue.zIndex = 0\n        self.wrappedValue.transition = transition\n        self.wrappedValue.uuid = UUID()\n        return try withAnimation(animation, body)\n    }\n\n    func pop\u003cResult\u003e(_ animation: Animation? = .default, _ body: () throws -\u003e Result) rethrows -\u003e Result {\n        let insertion: AnyTransition = .navigationBack\n        let removal: AnyTransition = .navigationFront\n        let transition: AnyTransition = .asymmetric(insertion: insertion, removal: removal)\n        self.wrappedValue.zIndex = 1\n        self.wrappedValue.transition = transition\n        self.wrappedValue.uuid = UUID()\n        return try withAnimation(animation, body)\n    }\n}\n```\n\nIt can be called as follows\n\n```swift\nnavigator.push {\n    navigator.wrappedValue.path = \"/weather/\\(data.label)\"\n}\n\nnavigator.pop {\n    navigator.wrappedValue.path = \"/weather\"\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1amageek%2FRouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1amageek%2FRouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1amageek%2FRouter/lists"}