|
import SwiftUI |
|
import UIKit |
|
|
|
struct HPSFNavigationStack<SliceID: Hashable, Content: View>: View { |
|
private var spec: Spec |
|
enum Action { |
|
case userNavigation([SliceID]) |
|
} |
|
init(slices: [SliceID], content: @escaping (SliceID) -> Content) { |
|
spec = Spec(slices: slices, content: content, action: noop) |
|
} |
|
var body: some View { |
|
Rep(spec: spec) |
|
} |
|
func action(_ action: @escaping (Action) -> Void) -> Self { |
|
var copy = self |
|
copy.spec.action = action |
|
return copy |
|
} |
|
} |
|
|
|
private extension HPSFNavigationStack { |
|
struct Spec { |
|
var slices: [SliceID] |
|
var content: (SliceID) -> Content |
|
var action: (Action) -> () |
|
} |
|
struct Rep: UIViewControllerRepresentable { |
|
var spec: Spec |
|
func makeUIViewController(context: Context) -> Impl { |
|
Impl(spec: spec) |
|
} |
|
func updateUIViewController(_ impl: Impl, context: Context) { |
|
impl.set(spec: spec) |
|
} |
|
|
|
final class Impl: UINavigationController, UINavigationControllerDelegate { |
|
init(spec: Spec) { |
|
self.spec = spec |
|
super.init(nibName: nil, bundle: nil) |
|
set(spec: spec) |
|
} |
|
required init?(coder aDecoder: NSCoder) { |
|
unsupported() |
|
} |
|
|
|
private var spec: Spec |
|
private var sliceViewControllers = [SliceIDAttachedHostingController]() |
|
func set(spec: Spec) { |
|
self.spec = spec |
|
let oldVCs = sliceViewControllers |
|
let newVCs = spec.slices.enumerated().map { (i, sliceID) in |
|
if oldVCs.indices.contains(i) { |
|
oldVCs[i] |
|
} |
|
else { |
|
SliceIDAttachedHostingController(sliceID: sliceID, rootView: spec.content(sliceID)) |
|
} |
|
} |
|
sliceViewControllers = newVCs |
|
setViewControllers(newVCs, animated: true) |
|
} |
|
|
|
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { |
|
let sliceIDs = viewControllers.map { $0 as! SliceIDAttachedHostingController }.map(\.sliceID) |
|
spec.action(.userNavigation(sliceIDs)) |
|
} |
|
|
|
final class SliceIDAttachedHostingController: UIHostingController<Content> { |
|
let sliceID: SliceID |
|
init(sliceID: SliceID, rootView: Content) { |
|
self.sliceID = sliceID |
|
super.init(rootView: rootView) |
|
} |
|
required init?(coder aDecoder: NSCoder) { |
|
unsupported() |
|
} |
|
} |
|
} |
|
} |
|
} |