【发布时间】:2021-12-25 17:26:56
【问题描述】:
所以我想我找到了一种方法,使 SwiftUI 中的导航灵活且松耦合,但仍然基于状态,并且在某种程度上没有命令式导航错误(双推等)。
基本思想是有一个视图的链接列表(擦除到 AnyView)和一个带有 NavigationLink 的递归视图,当列表中存在相应的视图时,它是活动的
但它不起作用,我不明白为什么。在 iOS 设备上,它只推送一层深度,即使列表是多层深度并且绑定返回 true
这是一个 SwiftUI 错误还是我遗漏了什么?
struct ContentView: View {
@State
var navigationList: NavigationList?
var body: some View {
NavigationView {
Navigatable(list: $navigationList) {
Button("Push test", action: {
navigationList = .init(next: nil, screen: Screen {
TestView()
})
})
}
}
}
}
struct TestView: View {
@Environment(\.navigationList)
@Binding
var list
var body: some View {
Button("Push me", action: {
list = .init(next: nil, screen: Screen {
TestView()
})
})
}
}
struct Navigatable<Content: View>: View {
@Binding
var list: NavigationList?
let content: () -> Content
init(list: Binding<NavigationList?>, @ViewBuilder content: @escaping () -> Content) {
self._list = list
self.content = content
}
var body: some View {
ZStack {
NavigationLink(
isActive: isActive,
destination: {
Navigatable<Screen?>(list: childBinding) {
list?.screen
}
},
label: EmptyView.init
).hidden()
LazyView {
content()
}.environment(\.navigationList, $list)
}
}
var isActive: Binding<Bool> {
.init(
get: { list != nil },
set: {
if !$0 {
list = nil
}
}
)
}
var childBinding: Binding<NavigationList?> {
.init(
get: { list?.next },
set: { list?.next = $0 }
)
}
}
struct Screen: View {
let content: () -> AnyView
init<C: View>(@ViewBuilder content: @escaping () -> C) {
self.content = {
.init(content())
}
}
var body: some View {
content()
}
}
struct NavigationList {
@Indirect
var next: NavigationList?
let screen: Screen
}
enum NavigationListKey: EnvironmentKey {
static var defaultValue: Binding<NavigationList?> {
.constant(nil)
}
}
extension EnvironmentValues {
var navigationList: Binding<NavigationList?> {
get { self[NavigationListKey.self] }
set { self[NavigationListKey.self] = newValue }
}
}
struct LazyView<Content: View>: View {
@ViewBuilder var content: () -> Content
var body: some View {
content()
}
}
@propertyWrapper
struct Indirect<Wrapped> {
private final class Storage: CustomReflectable {
var wrapped: Wrapped
init(_ wrapped: Wrapped) {
self.wrapped = wrapped
}
var customMirror: Mirror {
.init(self, children: [(label: "wrapped", value: wrapped)])
}
}
private let storage: Storage
var wrappedValue: Wrapped {
get { storage.wrapped }
mutating set { storage.wrapped = newValue }
}
init(wrappedValue: Wrapped) {
self.storage = .init(wrappedValue)
}
}
【问题讨论】:
-
欢迎来到 SO - 请使用 tour 并阅读 How to Ask 以改进、编辑和格式化您的问题。如果没有Minimal Reproducible Example,就无法帮助您进行故障排除。最少的代码应该在未链接的问题中
-
有什么问题?
-
在 iOS 设备上它只推送一个屏幕
标签: swift swiftui view navigation swiftui-navigationlink