Skip to content

Commit 48157b0

Browse files
committed
Add modifiers for title and isHidden
1 parent 7baaffe commit 48157b0

File tree

1 file changed

+181
-19
lines changed

1 file changed

+181
-19
lines changed

Sources/UIPilot/UIPilot.swift

Lines changed: 181 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ public class UIPilot<T: Equatable>: ObservableObject {
1414
var onPush: ((T) -> Void)?
1515
var onPopLast: ((Int, Bool) -> Void)?
1616

17-
1817
public init(initial: T? = nil, debug: Bool = false) {
1918
logger = debug ? DebugLog() : EmptyLog()
2019
logger.log("UIPilot - Pilot Initialized.")
@@ -68,46 +67,62 @@ public class UIPilot<T: Equatable>: ObservableObject {
6867
logger.log("UIPilot - \(popped) route popped by system")
6968
}
7069
}
71-
7270
}
7371

7472
public struct UIPilotHost<T: Equatable, Screen: View>: View {
75-
76-
@ObservedObject
77-
var pilot: UIPilot<T>
73+
74+
@StateObject
75+
var navigationStyle = NavigationStyle()
76+
77+
let pilot: UIPilot<T>
7878
@ViewBuilder
79-
var routeMap: (T) -> Screen
79+
let routeMap: (T) -> Screen
8080

8181
public init(_ pilot: UIPilot<T>, @ViewBuilder _ routeMap: @escaping (T) -> Screen) {
8282
self.pilot = pilot
8383
self.routeMap = routeMap
8484
}
8585

8686
public var body: some View {
87-
NavigationControllerHost(uipilot: pilot, routeMap: routeMap)
88-
.environmentObject(pilot)
87+
NavigationControllerHost(
88+
navTitle: navigationStyle.title,
89+
navHidden: navigationStyle.isHidden,
90+
uipilot: pilot,
91+
routeMap: routeMap
92+
)
93+
.environmentObject(pilot)
94+
.environment(\.uipNavigationStyle, navigationStyle)
95+
8996
}
9097
}
9198

9299
struct NavigationControllerHost<T: Equatable, Screen: View>: UIViewControllerRepresentable {
100+
101+
let navTitle: String
102+
let navHidden: Bool
103+
93104
let uipilot: UIPilot<T>
105+
94106
@ViewBuilder
95-
let routeMap: (T) -> Screen
107+
var routeMap: (T) -> Screen
96108

97109
func makeUIViewController(context: Context) -> UINavigationController {
98110
let navigation = PopAwareUINavigationController()
111+
99112
navigation.popHandler = {
100113
uipilot.onSystemPop()
101114
}
102115

103116
for path in uipilot.routes {
104117
navigation.pushViewController(
105-
UIHostingController(rootView: routeMap(path)), animated: false)
118+
UIHostingController(rootView: routeMap(path)), animated: true
119+
)
106120
}
107121

108122
uipilot.onPush = { route in
109123
navigation.pushViewController(
110-
UIHostingController(rootView: routeMap(route)), animated: true)
124+
UIHostingController(rootView: routeMap(route)), animated: true
125+
)
111126
}
112127

113128
uipilot.onPopLast = { numToPop, animated in
@@ -118,25 +133,172 @@ struct NavigationControllerHost<T: Equatable, Screen: View>: UIViewControllerRep
118133
navigation.popToViewController(popTo, animated: animated)
119134
}
120135
}
121-
136+
122137
return navigation
123138
}
124139

125-
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
126-
140+
func updateUIViewController(_ navigation: UINavigationController, context: Context) {
141+
navigation.topViewController?.navigationItem.title = navTitle
142+
navigation.navigationBar.isHidden = navHidden
143+
}
144+
145+
static func dismantleUIViewController(_ navigation: UINavigationController, coordinator: ()) {
146+
navigation.viewControllers = []
147+
(navigation as! PopAwareUINavigationController).popHandler = nil
127148
}
128149

129150
typealias UIViewControllerType = UINavigationController
130151
}
131152

132-
class PopAwareUINavigationController: UINavigationController
153+
class PopAwareUINavigationController: UINavigationController, UINavigationControllerDelegate
133154
{
134155
var popHandler: (() -> Void)?
156+
157+
var popGestureBeganController: UIViewController?
158+
159+
override func viewDidLoad() {
160+
super.viewDidLoad()
161+
self.delegate = self
162+
}
163+
164+
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
165+
166+
if let coordinator = viewController.transitionCoordinator {
167+
coordinator.notifyWhenInteractionChanges { [weak self] (context) in
168+
if !context.isCancelled {
169+
self?.popHandler?()
170+
}
171+
}
172+
}
173+
}
174+
}
175+
176+
177+
extension View {
178+
public func uipNavigationBarHidden(_ hidden: Bool) -> some View {
179+
return modifier(NavHiddenModifier(isHidden: hidden))
180+
}
181+
182+
public func uipNavigationTitle(_ title: String) -> some View {
183+
return modifier(NavTitleModifier(title: title))
184+
}
185+
186+
}
187+
188+
private struct NavigationTitleKey: EnvironmentKey {
189+
static let defaultValue: Binding<String> = .constant("")
190+
}
191+
192+
private struct NavigationHiddenKey: EnvironmentKey {
193+
static let defaultValue: Binding<Bool> = .constant(false)
194+
}
195+
196+
private struct NavigationStyleKey: EnvironmentKey {
197+
static let defaultValue: NavigationStyle = NavigationStyle()
198+
}
135199

136-
override func popViewController(animated: Bool) -> UIViewController?
137-
{
138-
popHandler?()
139-
return super.popViewController(animated: animated)
200+
201+
extension EnvironmentValues {
202+
203+
var uipNavigationStyle: NavigationStyle {
204+
get { self[NavigationStyleKey.self] }
205+
set {
206+
self[NavigationStyleKey.self] = newValue
207+
}
208+
}
209+
210+
var upNavigationHidden: Binding<Bool> {
211+
get { self[NavigationHiddenKey.self] }
212+
set {
213+
self[NavigationHiddenKey.self] = newValue
214+
}
215+
}
216+
217+
var upNavigationTitle: Binding<String> {
218+
get { self[NavigationTitleKey.self] }
219+
set {
220+
self[NavigationTitleKey.self] = newValue
221+
}
222+
}
223+
}
224+
225+
class NavigationStyle: ObservableObject {
226+
@Published
227+
var isHidden = false
228+
var isHiddenOwner: String = ""
229+
230+
@Published
231+
var title = ""
232+
var titleOwner: String = ""
233+
}
234+
235+
struct NavTitleModifier: ViewModifier {
236+
let title: String
237+
238+
@State var id = UUID().uuidString
239+
@State var initialValue: String = ""
240+
241+
@Environment(\.uipNavigationStyle) var navStyle
242+
243+
init(title: String) {
244+
self.title = title
245+
}
246+
247+
func body(content: Content) -> some View {
248+
249+
// In case where title change after onAppear
250+
if navStyle.titleOwner == id && navStyle.title != title {
251+
DispatchQueue.main.async {
252+
navStyle.title = title
253+
}
254+
}
255+
256+
return content
257+
.onAppear {
258+
initialValue = navStyle.title
259+
260+
navStyle.title = title
261+
navStyle.titleOwner = id
262+
}
263+
.onDisappear {
264+
if navStyle.titleOwner == id {
265+
navStyle.title = initialValue
266+
navStyle.titleOwner = ""
267+
}
268+
}
269+
}
270+
}
271+
272+
struct NavHiddenModifier: ViewModifier {
273+
let isHidden: Bool
274+
275+
@State var id = UUID().uuidString
276+
@State var initialValue: Bool = false
277+
278+
@Environment(\.uipNavigationStyle) var navStyle
279+
280+
func body(content: Content) -> some View {
281+
282+
// In case where isHidden change after onAppear
283+
if navStyle.isHiddenOwner == id && navStyle.isHidden != isHidden {
284+
DispatchQueue.main.async {
285+
navStyle.isHidden = isHidden
286+
}
287+
}
288+
289+
return content
290+
.onAppear {
291+
initialValue = navStyle.isHidden
292+
293+
navStyle.isHidden = isHidden
294+
navStyle.isHiddenOwner = id
295+
}
296+
.onDisappear {
297+
if navStyle.isHiddenOwner == id {
298+
navStyle.isHidden = initialValue
299+
navStyle.isHiddenOwner = ""
300+
}
301+
}
140302
}
141303
}
142304

0 commit comments

Comments
 (0)