SwiftUI中的UITableView被告知在视图层次结构之外布局其可见单元格和其他内容。

48

我在一个视图中有一个列表(我们称之为视图A),通过NavigationLink从视图A推送视图B,并操作CurrentSubjectValue以返回到初始视图A。一旦数据被添加并且didChangd.send(value)被分派到视图A,我会收到警告,即正在更新不属于该视图的单元格。似乎视图A已从窗口中删除,这与我以前的UINavigationController行为不符。 有什么想法吗?

视图A(MainView)

struct MainView: View {

    @ObjectBinding var currentProfileIdStore = CurrentProfileIdStore.shared

    var body: some View {

        NavigationView {
            if (currentProfileIdStore.didChange.value == nil) {
                NavigationLink(destination: ProfilesView()){ Text("show profiles view")}
            } else {
                List {
                    Section {
                        ForEach(0..<1) { _ in
                            PresentationLink(destination: ProfilesView()) {
                                Text("Profiles")
                            }
                        }
                    }

                    Section {
                        ForEach(0..<1) { _ in
                            Text("Items")
                        }
                    }
                }.listStyle(.grouped)
            }
        }.navigationBarTitle(currentProfileIdStore.didChange.value ?? "unknown")

    }
}

然后展示个人资料视图。

The translated text is:

然后展示个人资料视图。

struct ProfilesView : View {

    @ObjectBinding var profileManager = CurrentProfileIdStore.shared
    @ObjectBinding var profileProvider = ProfileProvider.shared

    var body: some View {

        NavigationView {

            List {
                Section {
                    ForEach(0..<1) { _ in
                        NavigationLink(destination: NewProfile()) {
                            Text("Add Profile")
                        }
                    }
                }


                Section {
                    ForEach(self.profileProvider.didChange.value) { profile in
                        VStack {

                            if profile.id == self.profileManager.didChange.value {
                                Image(systemName: "g.circle").transition(.move(edge: .top))
                            }

                            ProfileCellView(profile: profile)
                            Text(self.profileManager.didChange.value ?? "unknown").font(.footnote).color(.secondary)
                        }
                    }
                }

            }
            .navigationBarTitle("Profiles")
                .listStyle(.grouped)
        }
    }
}

然后我们就可以添加一个新的个人资料

struct NewProfile: View {

    @State var name: String = ""
    @State var successfullyAdded: Bool = false

    @ObjectBinding var keyboardStatus = KeyboardStatus()
    var profileProvider = ProfileProvider.shared

    var body: some View {

        NavigationView {
                VStack {
                    if self.successfullyAdded  {
                        Text("Successfully added please go back manually :)").lineLimit(nil)
                    }
                    Form {
                        Section(header: Text("Enter name")) {
                            TextField("Enter your name", text: $name)
                        }
                    }
                    if name.count > 3 {
                        Button(action: {
                            self.profileProvider.addProfile(new: Profile(name: self.name, id: UUID().uuidString)) {
                                assert(Thread.isMainThread)
                                self.successfullyAdded = true
                            }
                        }) {
                            Text("Add")
                        }.transition(.move(edge: .bottom))
                    }
                }.padding(.bottom, self.keyboardStatus.didChange.value)
        }.navigationBarTitle("Add Profile")

    }
}

然后还有提供者。

class ProfileProvider: BindableObject {

    enum Query {
        case all

        var filter: (Profile) -> Bool {
            switch self {
            case .all:
                return { p in true }
            }
        }
    }

    static var samples: [Profile] = []

    static var shared: ProfileProvider = {

        return ProfileProvider(query: .all)
    }()

    var didChange = CurrentValueSubject<[Profile], Never>([])

    private var query: Query?

    private init(query: Query? = nil) {

        self.query = query
        assert(Thread.isMainThread)

        dispatchDidChange()
    }

    func dispatchDidChange() {
        assert(Thread.isMainThread)
        if let valid = self.query {
            didChange.send(ProfileProvider.samples.filter {  valid.filter($0) })
        } else {
            didChange.send([])
        }
    }

    func addProfile(new: Profile, comp: () -> Void) {

        ProfileProvider.samples.append(new)
        comp()
        dispatchDidChange()
    }

请记住,多个视图共享同一个提供程序主题。因此,当添加新配置文件时会出现问题。

self.profileProvider.addProfile(new

还有两个问题,一个是当返回到主视图时,按钮不再起作用。 另外,在以模态方式呈现时,我还不知道如何手动返回。


3
#1. 这是一个警告,对吧?重要的问题是 - 它是否按照你的期望工作?记住,SwiftUI 只是版本 1 的 beta 3!我也会收到这些警告。希望在未来的 beta 版本中能有所改进。 (1) 要关闭这些警告,请在构建中将 OS_ACTIVITY_MODE 设置为 disable。不一定推荐这样做 - 但它可以消除噪音。 (2) 如果行为不是你期望的,请发布一些代码。这样我们可以提供帮助。 - user7014451
#2. 可能是一个时间问题吗?如果是的话,请看我上周回答的一个问题:https://stackoverflow.com/questions/57008251/index-out-of-range-when-binding-a-slider-value-to-a-nested-array-in-environmento - user7014451
考虑添加一些代码。一个最简化的示例可以展示出错误的情况。否则,这只是纯粹的猜测。请参阅https://stackoverflow.com/help/how-to-ask。 - undefined
4
我也看到了这个。Xcode 11.2.1 - undefined
你的代码有些奇怪,可能会引起问题:你把用于添加新个人资料的 NavigationLink 放在了一个没有任何理由的 ForEach 中。更糟糕的是,你给 ForEach 提供的对象不是 Identifiable 类型。这意味着当数据发生变化时,旧的 NavigationLink 不应该存在。移除这个 ForEach,它什么也不做,因为它只遍历了 0..1(一个索引)。我不知道这是否解决了你的问题,但这段代码是无用的,所以请将其移除。 - undefined
1个回答

1
看一下你的代码,有几个问题需要考虑解决:
SwiftUI生命周期和BindableObject,SwiftUI的生命周期和视图刷新方式与UIKit有些不同。在SwiftUI中,视图应该是轻量级和可重新创建的,因此视图经常会刷新。你的警告很可能是因为SwiftUI试图更新一个不再在当前层次结构中的视图。
此外,请记住,SwiftUI 2.0引入了新的ObservableObject协议来替代BindableObject。通过这样做,你可以改变视图监听数据变化的方式。
在嵌套的NavigationView中,应避免在另一个NavigationView中嵌套一个NavigationView。这可能会导致意外的行为,并影响导航堆栈。移除嵌套的NavigationView,系统应该会处理导航操作。
手动返回。如果您希望手动弹出视图,您可以使用@Environment(\.presentationMode)变量,并调用其wrappedValue.dismiss()方法来手动关闭视图。
也许我下面列出的要点可以帮助解决您的一些问题:
  1. 更新可观察模式: 将@ObjectBinding替换为@ObservedObject,将BindableObject替换为ObservableObject。对于您希望视图观察的属性,请使用@Published属性包装器。
请参考下面的示例:

class ProfileProvider: ObservableObject {
    @Published var profiles: [Profile] = []
    ...

}


然后,在视图中你可以这样做:

@ObservedObject var profileProvider = ProfileProvider.shared

2. 删除嵌套的 NavigationViews,只保留最外层的 NavigationView。
3. 在你的 NewProfile 视图中尝试类似以下的代码:

@Environment(\.presentationMode) var presentationMode

...

if self.successfullyAdded  {
    Text("Successfully added, please go back manually :)")
        .lineLimit(nil)
    Button("Go Back") {
        self.presentationMode.wrappedValue.dismiss()
    }
}

当更新视图时,SwiftUI视图应该是数据的反映。如果数据发生变化,视图应该自动更新。如果没有更新,考虑重新审视数据流和视图结构。
在使用NavigationLink时,请记住NavigationLink具有isActive绑定,可以用于以编程方式弹出视图。
对于MainView中不起作用的按钮,可能是由于您放置在是否应该显示的逻辑周围的原因。在添加更多逻辑之前,请考虑简化该部分并使用静态视图进行测试。
SwiftUI与UIKit相比提倡一种不同的思维方式。专注于确保您的数据驱动您的UI。一旦您的数据和逻辑设置正确,UI应该自动反映变化。
尽量确保所有视图都是轻量级且易于重新创建的,并且您的数据源(如ObservableObject实例)正确地通知视图进行更改。
希望这能帮助解决您的问题 =)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接