【问题标题】:SwiftUI - multiple levels deep NavigationLink does not workSwiftUI - 多级深度 NavigationLink 不起作用
【发布时间】: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


【解决方案1】:

您缺少isDetailLink(false),它允许将多个屏幕推送到一个导航控制器上。

但代码也存在结构性问题。最好按照设计使用 SwiftUI View 数据结构,并让它们存储数据的层次结构。如果您继续使用自己的架构,那么您将失去像失效和差异这样的魔力,它也可能会变慢。

【讨论】:

  • 可惜是没有解决问题
  • 我并不惊讶,SwiftUI 代码很奇怪。
  • 您对如何改进有什么建议吗?
  • 按照设计使用 SwiftUI 查看数据结构,尽可能小。不需要花哨的图案。
猜你喜欢
  • 2021-12-15
  • 2019-11-14
  • 1970-01-01
  • 2021-12-19
  • 2019-11-15
  • 1970-01-01
  • 2016-06-27
  • 2021-05-02
  • 2020-08-05
相关资源
最近更新 更多