【问题标题】:Binding an element of an array of an ObservableObject : 'subscript(_:)' is deprecated绑定 ObservableObject 数组的元素:'subscript(_:)' 已弃用
【发布时间】:2019-12-10 23:09:23
【问题描述】:

我正在使用 ObservableObject 'DataStore',它包含对象('exampleObject')的数组('exampleList')。

@Published exampleList = [exampleObject]()

我通过@EnvironmentObject ('dataStore') 调用DataStore。

@EnvironmentObject var dataStore = DataStore()

然后我用迭代列表

ForEach(0..<dataStore.exampleList.count) { index in ....

要将 item 的元素绑定到详细视图,我这样做:

DetailView(itemBinding: $dataStore.exampleList[index])

在 Xcode11 beta 4 之前,它运行良好。从 XCode11 beta 5 开始,它仍然可以工作,但 Xcode 给了我这个警告:

'subscript(_:)' 已弃用:请参阅发行说明了解迁移路径

我尝试了更简单的东西,一个简单的@State var 包含一个字符串数组,这是同样的问题:当调用这个数组的一个元素,并尝试将值使用到 TextField 中时:

TextField("test", text: $test[0])

我收到同样的警报。

我不明白如何解决它。这是否意味着我们不再可以在数组中绑定值? 那么,我们如何迭代一个数组并绑定一个特定的项呢?

这是我关于 Stack Overflow 的第一个问题,如果我的问题很笨拙,我深表歉意... 非常感谢您的回答,我多年来一直在使用 Stack Overflow,这太棒了,我总能找到现有且有用的答案,但这是我第一次找不到任何答案,这就是我问的原因。

【问题讨论】:

  • 首先,好问题。我对此表示赞同,因为我收到了与 Slider 类似的警告。我发现了和你一样的东西——如果你绑定到 ObservableObject 中的一个数组,出了点问题(但它仍然有效),但是如果你绑定到一个本地状态变量,一切都很好(但是你如何更新模型?)。如果您想查看它,我有一个示例项目(忽略名称,这是一个老问题)。 github.com/justdfd/ListBug如果我找到答案,我会发布。
  • @dfd,感谢您的评论,我很高兴知道世界上并非只有我一个人遇到此问题。我仍然不明白为什么,因为对于具有复杂数据的模型来说,使用数组似乎是一种常见情况。对我来说,ObservableObject 的兴趣是在修改数组元素时刷新所有父视图。如果我不能再那样做了,有什么好方法?
  • 这不是我今天想做的工作,但在花了一个小时(我一生中永远不会回来)之后,我找不到答案。我听了一些与苹果相关的播客,昨天一个在谈论“软件”如何落后于“硬件”,他们正在思考苹果在 9 月份可能会做什么。我的看法?由于对我来说毫无意义的更改,我的应用程序在 beta 4 和 5 中都打破了 BIG 时间。现在,我正在研究 (a) 可以发出警告的东西,并且 (b) 不知道 10 天内 beta 6 会发生什么变化。我会在此期间监控您的问题。
  • 来自 beta 5 的发行说明:删除了 Binding 结构对 Collection 协议的条件一致性。 (51624798)。准确地说,它没有被删除,它已被弃用。这就是它仍然有效的原因。如果他们遵循与其他 beta 弃用相同的模式,他们可能会在 beta 6 或 7 中永久删除它。我没有时间分析替代方案。发行说明提供了一些示例,但它们似乎需要做太多的工作才能实现迄今为止如此容易的事情。我的赌注是(续)
  • 我敢打赌,在 beta 6 或 7 中情况会再次发生变化。也许对 Collection 的一致性不会回来,但可能会提供一些东西。虽然它仍然有效,但我不想浪费太多时间来研究它。如果很快有另一个变化,这一切可能都是徒劳的。暂时,我想我会放过它,当他们最终删除它时我会担心。

标签: swift swiftui xcode11


【解决方案1】:

Xcode 11,beta 6 更新:

好消息!正如我所怀疑的,在 beta 6 中,BindingMutableCollection 的一致性已被其他内容取代。它不再符合 MutableCollection,而是让您通过 @dynamicMemberLookup 访问元素。结果是您现在可以继续执行$text[3] 并且不再收到警告!看来这个问题现在可以结束了。

Xcode 11,beta 5。旧答案:

我终于有时间稍微调查一下。正如我在 cmets 中提到的,我认为等到完全删除 Collection 一致性(或替换为其他内容)是明智的。但为了满足我们的好奇心,我在Binding 上创建了一个扩展,我认为它与当前的Collection 一致。唯一的区别是,我没有通过下标访问,而是实现了一个名为 element(_ idx: Int) 的函数来获取元素的 Binding&lt;T&gt;

如果有一天完全删除了一致性,我可能会更改实现,并自己遵守Collection。我现在不能这样做,因为它会与现有的(和已弃用的)实现冲突。目前,我认为这演示了如果您绝对想摆脱警告,如何处理它们。

只是为了清楚。我没有使用此代码。只要我仍然可以通过下标访问元素,我仍然会这样做并忽略警告。这仅用于学术目的。

扩展名是:

extension Binding where Value: MutableCollection, Value.Index == Int {
    func element(_ idx: Int) -> Binding<Value.Element> {
        return Binding<Value.Element>(
            get: {
                return self.wrappedValue[idx]
        }, set: { (value: Value.Element) -> () in
            self.wrappedValue[idx] = value
        })
    }
}

而且可以这样使用:

struct MainView: View {
    @Binding var text: [String]

    var body: some View {
        TextField("", text: $text.element(0))
        TextField("", text: $text.element(1))
        TextField("", text: $text.element(2))
    }
}

【讨论】:

  • 我在使用 iOS 13 beta 6 或 7 的真实设备上使用此解决方案时遇到了 dyld: Symbol not found: _$s7SwiftUI7BindingVyxGAA0C11ConvertibleAAMc 的问题。有人可以确认此解决方案当前适用于 beta 6 或 7 吗?
  • 感谢您的更新!不管怎样,因为我有这个问题,我想了其他方法来设计我的代码,我认为现在更好。我看到将数组用于绑定可能会导致索引超出范围的许多风险,如果包含例如 texfield 的视图仍然在后台或导航后面的某个位置。也许现在它在 beta 6 中效果更好?我仍然在我的 observable 对象中使用数组,但只是在用户验证表单时复制状态值,而不是直接复制到文本字段中。并且再也不会像那样索引超出范围。 (对不起我的英语不好)
  • 嗨,Kontiki,您似乎对这里的工作原理非常了解。您是否知道在视图中获取绑定数组并将其与 ForEach 一起使用以创建多个正确绑定到其父数据的子视图的方法?使用此方法在一定程度上有效,但索引似乎没有更新,因此当我的应用程序中的数据发生变化时,我会遇到“索引越界”故障。 ForEach(myArray.indices, id: \.self){ index in MyChildView(data: self.$myArray[index]) }
  • @TomMillard 如果不查看代码的整个上下文,很难说。我建议您发布一个单独的问题...更详细一点...理想情况下,可以粘贴到 Xcode 并重现您的问题的最小示例。
【解决方案2】:

我最近不得不绑定一个可观察对象的数组,在稳定的 XCode11 上没有收到任何警告。我是这样做的

struct ScheduleTimer: Identifiable {
    var id: Int
    var name: String
    var start: Date
    var end: Date
    var isActive: Bool
}

struct ScheduleView: View {
    @ObservedObject var scheduleController = ScheduleController()
    var body: some View {
        NavigationView {
            Form {
                ForEach(scheduleController.timers) { timer in
                    ScheduleForm(scheduleController: self.scheduleController, timer: timer)
                }
            }
        }
    }
}


struct ScheduleForm: View {
    @ObservedObject var scheduleController: ScheduleController
    var timer: ScheduleTimer
    var scheduleIndex: Int {
        scheduleController.timers.firstIndex(where: { $0.id == timer.id })!
    }
    @State var start = Date()
    var body: some View {
        Section(header: Text(self.scheduleController.timers[scheduleIndex].name)){
            DatePicker("From", selection: self.$scheduleController.timers[scheduleIndex].start, displayedComponents: .hourAndMinute)
            DatePicker("To", selection: self.$scheduleController.timers[scheduleIndex].end, displayedComponents: .hourAndMinute)
            Toggle(isOn: self.$scheduleController.timers[scheduleIndex].isActive) {
                Text("")
            }.toggleStyle(DefaultToggleStyle())
        }
    }
}

class ScheduleController: ObservableObject {
    @Published var timers = [ScheduleTimer]()
...

【讨论】:

    猜你喜欢
    • 2022-11-20
    • 2022-08-02
    • 2011-07-21
    • 1970-01-01
    • 1970-01-01
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 2020-04-03
    相关资源
    最近更新 更多