Skip to content

Instantly share code, notes, and snippets.

@eonil
Last active December 20, 2023 22:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eonil/44b267e94c557e17a418d6e93cf6aff0 to your computer and use it in GitHub Desktop.
Save eonil/44b267e94c557e17a418d6e93cf6aff0 to your computer and use it in GitHub Desktop.
HPSFNavigationStack
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()
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment