SwiftUI:打开绑定到新数组元素的工作表失败

问题描述 投票:0回答:1

在学习 SwiftUI 时,我正在构建一个简单的 iOS 应用程序,其中包含一个包含项目数组的模型。可以创建和编辑这些项目。

主视图列出了项目和用于创建新项目的

+
按钮。点击一个项目打开另一个视图以编辑工作表中的项目。这是通过绑定
editItem
发生的,这是一个
@State
使其可变(基于 this answer)。

当我尝试在项目创建后立即打开编辑视图时出现问题:创建新项目时代码在运行时(iPhone 14 Pro 模拟器)失败:

Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range

我已将应用程序归结为以下最小示例:

import SwiftUI

struct Model {
    var items: [Item]
}

struct Item: Identifiable {
    var id: String
}

struct MainView: View {
    @Binding var model: Model
    @State var editItem: Binding<Item>?
    
    var body: some View {
        VStack {
            List($model.items) { $item in
                ItemCardView(item: $item.wrappedValue)
                    .onTapGesture {
                        editItem = $item
                    }
            }
            Button(action: {
                model.items.append(Item(id: "New item"))
                editItem = $model.items.last // WORKS WITHOUT THIS
            }) {
                Image(systemName: "plus")
            }
        }.sheet(item: $editItem) { $item in
            let _=print($item)
            ItemEditView(item: $item)
        }
    }
}

struct ItemCardView: View {
    var item: Item
    var body: some View {
        Text(item.id)
    }
}

struct ItemEditView: View {
    @Binding var item: Item
    @State private var editingItem: Item = Item(id: "dummy")
    
    var body: some View {
        TextField("Title", text: $editingItem.id)
        .onAppear {
            editingItem = item
        }
        .onDisappear {
            item = editingItem
        }
    }
}

@main
struct MyApp: App {
    @State var model = Model(items: [])
    
    var body: some Scene {
        WindowGroup {
            MainView(model: $model)
        }
    }
}

该应用程序无需线路即可运行

editItem = $model.items.last // WORKS WITHOUT THIS

如果删除此行,

+
按钮将创建一个新项目,您可以通过点击它进行编辑。

我无法查明调试器中的故障,因为我的代码中没有触发异常:传递给

ItemEditView
的绑定在打印时看起来很好(尽管它打印了两次,我不知道是否这是一个提示)。

我既有兴趣了解为什么我的方法不起作用,也有兴趣提出解决潜在问题的更好方法。

我在 M1 笔记本电脑上使用 XCode 14.3,目标是 iOS 16.4。

ios swiftui indexoutofboundsexception
1个回答
0
投票

这一切都归结为@State 和@StateObject 之间的区别。 SwiftUI 的 State 属性包装器是为当前视图本地的简单数据而设计的,但它并不是为了在视图之间共享而设计的,因此仅在视图内使用,并且也应该在视图内初始化。它应该存储简单数据(值类型基本上如 Apple 所声称的那样)。另一方面,我们将 StateObject 用作引用类型的真实来源 - 因此可以作为引用传递并且不会被复制。 潜在的解决方案是:

@main
struct BattlePlanApp: App {
    @StateObject var model = Model()
    
    var body: some Scene {
        WindowGroup {
            MainView(model: model)
        }
    }
}


class Model: ObservableObject {
    var items: [Item] = []
}

struct MainView: View {
    @State var model: Model
    @State var editItem: Binding<Item>?
    
    var body: some View {
        VStack {
            List($model.items) { $item in
                ItemCardView(item: $item.wrappedValue)
                    .contentShape(Rectangle())
                    .onTapGesture {
                        editItem = $item
                    }
            }
            
            Button(action: {
                model.items.append(Item(id: UUID(), title: "New item"))
                editItem = $model.items.last
            }) {
                Image(systemName: "plus")
            }.frame(width: 40, height: 40)
        }.sheet(item: $editItem) { item in
            ItemEditView(item: item)
        }
    }
}

阅读

https://developer.apple.com/documentation/swiftui/state https://developer.apple.com/documentation/combine/observableobject

© www.soinside.com 2019 - 2024. All rights reserved.