From 2720753fa85daf05ace5e3f048c64e2f4e56fa61 Mon Sep 17 00:00:00 2001 From: Zaphod Beeblebrox Date: Thu, 23 May 2024 01:09:54 +0200 Subject: [PATCH] Patch 1.0.0 feat: - Added an ability to inject EnvironmentObject (#20) docs: - GitHub documentation reworked fix: - Minor bugs fixed --- MijickNavigationView.podspec | 2 +- README.md | 123 +++++++++++++----- .../Internal/Managers/NavigationManager.swift | 4 +- .../Internal/Protocols/NavigatableView.swift | 4 +- .../Type Erasers/AnyNavigatableView.swift | 6 + Sources/Public/Public+NavigatableView.swift | 9 +- 6 files changed, 109 insertions(+), 39 deletions(-) diff --git a/MijickNavigationView.podspec b/MijickNavigationView.podspec index a9f6a06..72b9aa2 100644 --- a/MijickNavigationView.podspec +++ b/MijickNavigationView.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |s| NavigationView is a free and open-source library dedicated for SwiftUI that makes navigation easier and much cleaner. DESC - s.version = '0.7.0' + s.version = '1.0.0' s.ios.deployment_target = '15.0' s.swift_version = '5.0' diff --git a/README.md b/README.md index 6dad4d6..9f0d366 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,20 @@

- Implement navigation in your project in no time. Keep your code clean + Improve the navigation in your project in no time. Keep your code clean

Try demo we prepared + | + Roadmap + | + Propose a new feature


- Library in beta version Designed for SwiftUI Platforms: iOS Current Version @@ -44,14 +47,19 @@

- NavigationView Examples + NavigationView Examples + NavigationView Examples + NavigationView Examples + NavigationView Examples


-NavigationView is a free, and open-source library for SwiftUI that makes navigation easier and much cleaner. -* **Improves code quality.** Push your view using the `push(with:)` method.
- Pop the selected one with `pop()`. Simple as never. +NavigationView by Mijick is a powerful, open-source library dedicated for SwiftUI that makes navigation process super easy and much cleaner. +* **Custom animations.** Our library provides full support for any animation. +* **Remembers the current scroll view offset.** Library automatically saves the current scroll view offset when you leave the view. +* **Improves code quality.** Navigate through your screens with just one line of code. Focus on what’s important to you and your project, not on Swift's intricacies. +* **Stability at last!** At Mijick, we are aware of the problems that were (and still are) with the native NavigationView and how many problems it caused to developers. Therefore, during the development process we put the greatest emphasis on the reliability and performance of the library. * **Designed for SwiftUI.** While developing the library, we have used the power of SwiftUI to give you powerful tool to speed up your implementation process.
@@ -74,36 +82,54 @@ Once you have your Swift package set up, adding NavigationView as a dependency i dependencies: [ .package(url: "https://github.com/Mijick/NavigationView", branch(“main”)) ] -``` - +``` + + +#### [Cocoapods][cocoapods] +Cocoapods is a dependency manager for Swift and Objective-C Cocoa projects that helps to scale them elegantly. + +Installation steps: +- Install CocoaPods 1.10.0 (or later) +- [Generate CocoaPods][generate_cocoapods] for your project +```Swift +pod init +``` +- Add CocoaPods dependency into your `Podfile` +```Swift +pod 'MijickNavigationView' +``` +- Install dependency and generate `.xcworkspace` file +```Swift +pod install +``` +- Use new XCode project file `.xcworkspace`
+ # Usage ### 1. Setup library -Inside the `@main` structure, call the `implementNavigationView(config:)` method on the view that is to be the root view in your navigation structure. -The view to be the root must be of type `NavigatableView`. The method takes an optional argument - `config`, that can be used to configure some modifiers for all navigation views in the application. - +Inside your `@main` structure, call the `implementNavigationView` method with the view that is to be the root of the navigation stack. The view must be of type `NavigatableView`. The method takes an optional argument - `config`, which can be used to configure certain attributes of all the views that will be placed in the navigation stack. ```Swift - var body: some Scene { +@main struct NavigationView_Main: App { + var body: some Scene { WindowGroup { ContentView() .implementNavigationView(config: nil) } - } + } +} ``` -### 2. Declare structure of the view you want to push -NavigationView provides the ability to push (or pop) any view using its built-in stack. In order to do so, it is necessary to confirm to `NavigatableView` protocol. -So that an example view you want to push will have the following declaration: +### 2. Declare a view to be pushed to the navigation stack +NavigationView by Mijick provides the ability to push any view conforming to the `NavigatableView` protocol to the navigation stack. ```Swift struct ExampleView: NavigatableView { ... } ``` -### 3. Implement `body` method -Fill your view with content - +### 3. Implement `body` +Fill your view with content ```Swift struct ExampleView: NavigatableView { var body: some View { @@ -119,8 +145,7 @@ struct ExampleView: NavigatableView { ### 4. Implement `configure(view: NavigationConfig) -> NavigationConfig` method *This step is optional - if you wish, you can skip this step and leave the configuration as default.*
-Each view has its own set of methods that can be used to customise it, regardless of the config we mentioned in **step 1**. - +Each view has its own set of methods that can be used to create a unique look for each view in the stack. ```Swift struct ExampleView: NavigatableView { func configure(view: NavigationConfig) -> NavigationConfig { view.backgroundColour(.red) } @@ -135,9 +160,8 @@ struct ExampleView: NavigatableView { } ``` -### 5. Present your view from any place you want! -Just call `ExampleView().push(with:)` from the selected place - +### 5. Present your view - from any place in your code! +Just call `ExampleView().push(with:)` from the selected place. As simple as that! ```Swift struct SettingsViewModel { ... @@ -150,10 +174,9 @@ struct SettingsViewModel { } ``` -### 6. Closing views -There are two ways to do so: -- By calling one of the methods `pop`, `pop(to type:)`, `popToRoot` inside any view - +### 6. Close your view - it's even simpler! +There are two ways to do this: +- By calling one of the methods `pop`, `pop(to type:)`, `popToRoot` inside any view ```Swift struct ExampleView: NavigatableView { ... @@ -163,11 +186,45 @@ struct ExampleView: NavigatableView { ... } ``` -- By calling one of the static methods of NavigationManager: +- By calling one of the static `NavigationManager` methods: - `NavigationManager.pop()` - `NavigationManager.pop(to type:)` where type is the type of view you want to return to - - `NavigationManager.popToRoot()` - + - `NavigationManager.popToRoot()` + +### 7. Wait, there's even more! +We're almost done, but we'd like to describe three additional methods that you might like: +- With the `setAsNewRoot` method you can change the root of your navigation stack: +```Swift +ExampleView() + .push(with: .verticalSlide) + .setAsNewRoot() +``` + +- `EnvironmentObject` can be passed, but remember to do this **BEFORE** pushing the view to the stack: +```Swift +ExampleView() + .environmentObject(object) + .push(with: .verticalSlide) +``` + +- Use `onFocus`, not `onAppear`
+If you want to be notified every time a view is visible (is on top of the stack), use `onFocus` method: +```Swift +struct ExampleView: NavigatableView { + var body: some View { + VStack(spacing: 0) { + Text("Witaj okrutny świecie") + Spacer() + Button(action: pop) { Text("Pop") } + } + .onFocus(self) { + // Do something + } + } + ... +} +``` +
# Try our demo @@ -192,7 +249,9 @@ NavigationView is released under the MIT license. See [LICENSE][License] for det [MIT]: https://en.wikipedia.org/wiki/MIT_License -[SPM]: https://www.swift.org/package-manager +[spm]: https://www.swift.org/package-manager +[cocoapods]: https://cocoapods.org/ +[generate_cocoapods]: https://github.com/square/cocoapods-generate [Demo]: https://github.com/Mijick/NavigationView-Demo [License]: https://github.com/Mijick/NavigationView/blob/main/LICENSE diff --git a/Sources/Internal/Managers/NavigationManager.swift b/Sources/Internal/Managers/NavigationManager.swift index 5b9d30c..cb09704 100644 --- a/Sources/Internal/Managers/NavigationManager.swift +++ b/Sources/Internal/Managers/NavigationManager.swift @@ -36,10 +36,10 @@ extension NavigationManager { // MARK: - On Attributes Will/Did Change private extension NavigationManager { - func onViewsWillUpdate(_ newValue: [AnyNavigatableView]) { + func onViewsWillUpdate(_ newValue: [AnyNavigatableView]) { if newValue.count != views.count { transitionType = newValue.count > views.count || !transitionType.isOne(of: .push, .pop) ? .push : .pop transitionAnimation = (transitionType == .push ? newValue.last?.animation : views[newValue.count].animation) ?? .no - } + }} func onTransitionsBlockedUpdate() { if !transitionsBlocked, case let .replaceRoot(newRootView) = transitionType { views = views.appendingAsFirstAndRemovingDuplicates(newRootView) }} diff --git a/Sources/Internal/Protocols/NavigatableView.swift b/Sources/Internal/Protocols/NavigatableView.swift index 4496cb3..5ee4c2f 100644 --- a/Sources/Internal/Protocols/NavigatableView.swift +++ b/Sources/Internal/Protocols/NavigatableView.swift @@ -11,10 +11,12 @@ import SwiftUI public protocol NavigatableView: View { + var id: String { get } + func configure(view: NavigationConfig) -> NavigationConfig } // MARK: - Internals -extension NavigatableView { +public extension NavigatableView { var id: String { .init(describing: Self.self) } } diff --git a/Sources/Internal/Type Erasers/AnyNavigatableView.swift b/Sources/Internal/Type Erasers/AnyNavigatableView.swift index b247e8e..a6aec61 100644 --- a/Sources/Internal/Type Erasers/AnyNavigatableView.swift +++ b/Sources/Internal/Type Erasers/AnyNavigatableView.swift @@ -23,6 +23,12 @@ struct AnyNavigatableView: NavigatableView, Equatable { self._body = AnyView(view) self._configure = view.configure } + init(_ view: some NavigatableView, _ environmentObject: some ObservableObject) { + self.id = view.id + self.animation = .no + self._body = AnyView(view.environmentObject(environmentObject)) + self._configure = view.configure + } } extension AnyNavigatableView { static func == (lhs: AnyNavigatableView, rhs: AnyNavigatableView) -> Bool { lhs.id == rhs.id } diff --git a/Sources/Public/Public+NavigatableView.swift b/Sources/Public/Public+NavigatableView.swift index 81cb725..917a1b5 100644 --- a/Sources/Public/Public+NavigatableView.swift +++ b/Sources/Public/Public+NavigatableView.swift @@ -25,11 +25,14 @@ public extension NavigatableView { func configure(view: NavigationConfig) -> NavigationConfig { view } } -// MARK: - Pushing Views To Stack +// MARK: - Presenting Views public extension NavigatableView { /// Pushes a new view. Stacks previous one - @discardableResult func push(with animation: TransitionAnimation) -> Self { NavigationManager.performOperation(.insert(self, animation)); return self } + @discardableResult func push(with animation: TransitionAnimation) -> some NavigatableView { NavigationManager.performOperation(.insert(self, animation)); return self } /// Sets the selected view as the new navigation root - @discardableResult func setAsNewRoot() -> Self { NavigationManager.replaceRoot(self); return self } + @discardableResult func setAsNewRoot() -> some NavigatableView { NavigationManager.replaceRoot(self); return self } + + /// Supplies an observable object to a view’s hierarchy + @discardableResult func environmentObject(_ object: some ObservableObject) -> any NavigatableView { AnyNavigatableView(self, object) } }