SwiftUI:从 ForEach 中删除项目导致索引超出范围
Posted
技术标签:
【中文标题】SwiftUI:从 ForEach 中删除项目导致索引超出范围【英文标题】:SwiftUI: Deleting an item from a ForEach results in Index Out of Range 【发布时间】:2021-02-18 04:41:41 【问题描述】:我在我的 ContentView 组件中构建了一个水平滚动的 ForEach UI,它显示了一组自定义对象(结构形式)。当我尝试删除项目时,我收到“致命错误:索引超出范围”错误。
问题是当我删除一个项目时,实际的数组本身会更新,但特定的 AssetView(下面的组件)组件没有更新,因此它最终会迭代到不再存在的索引。知道问题可能是什么吗?以下是我的代码:
内容视图
struct ContentView: View
@ObservedObject var assetStore: AssetStore
var body: some View
ScrollView (.horizontal)
ForEach(assetStore.assets.indices, id: \.self) index in
AssetView(
asset: $assetStore.assets[index],
assetStore: assetStore,
smallSize: geo.size.height <= 667
)
.padding(.bottom)
资产视图
struct AssetView: View
@Binding var asset: Asset
@ObservedObject var assetStore: AssetStore
var smallSize: Bool
@State var editAsset: Bool = false
var body: some View
VStack(alignment: .center, spacing: smallSize ? -10 : 0)
HStack
TagText(tagName: asset.name)
.onTapGesture
editAsset.toggle()
Spacer()
DisplayCurrentValues(
forCurrentValueText: asset.getCurrentValueString,
forCurrentValueLabel: "Current Value"
)
.onTapGesture
editAsset.toggle()
DisplayStepper(asset: $asset, title: "YoY Growth", type: .growth)
DisplayStepper(asset: $asset, title: "Recurring Deposit", type: .recurring)
.sheet(isPresented: $editAsset, content:
EditAsset(assetStore: assetStore, currentValue: String(asset.currentValue), name: asset.name, asset: $asset)
)
资产商店
这是我将所有资产对象读/写到我的应用程序的 Documents 文件夹的地方。
class AssetStore: ObservableObject
let assetsJSONURL = URL(fileURLWithPath: "Assets",
relativeTo: FileManager.documentsDirectoryURL).appendingPathExtension("json")
@Published var assets: [Asset] = []
didSet
saveJSONAssets()
init()
print(assetsJSONURL)
loadJSONAssets()
private func loadJSONAssets()
guard FileManager.default.fileExists(atPath: assetsJSONURL.path) else
return
let decoder = JSONDecoder()
do
let assetsData = try Data(contentsOf: assetsJSONURL)
assets = try decoder.decode([Asset].self, from: assetsData)
catch let error
print(error)
private func saveJSONAssets()
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do
let assetsData = try encoder.encode(assets)
try assetsData.write(to: assetsJSONURL, options: .atomicWrite)
catch let error
print(error)
public func deleteAsset(atIndex id: Asset)
let index = assets.firstIndex(where: $0.id == id.id)
assets.remove(at: index!)
资产对象
struct Asset: Identifiable, Codable, Hashable
// currently set for 10 years
let id = UUID()
enum Frequency: String, Codable, CaseIterable
case month = "Month"
case year = "Year"
case none = "None"
let years: Int
var name: String
var currentValue: Int
var growth: Int
var recurringDeposit: Int
var recurringFrequency: Frequency
enum CodingKeys: CodingKey
case id
case years
case name
case currentValue
case growth
case recurringDeposit
case recurringFrequency
init(
name: String = "AssetName",
currentValue: Int = 1000,
growth: Int = 10,
years: Int = 10,
recurringDeposit: Int = 100,
recurringFrequency: Frequency = .month
)
self.name = name
self.currentValue = currentValue
self.growth = growth
self.recurringDeposit = recurringDeposit
self.recurringFrequency = recurringFrequency
self.years = years
【问题讨论】:
这是否回答了您的问题***.com/a/61706902/12299030? 不,不幸的是,这不起作用。我最终还是得到了同样的错误。 【参考方案1】:我认为这是因为您的 ForEach
依赖于索引,而不是 Asset
s 本身。但是,如果您摆脱了indices
,您将不得不为Asset
编写一个新的绑定。我认为它可能是这样的:
ForEach(assetStore.assets, id: \.id) asset in
AssetView(
asset: assetStore.bindingForId(id: asset.id),
assetStore: assetStore,
smallSize: geo.size.height <= 667
)
.padding(.bottom)
然后在你的AssetStore
:
func bindingForId(id: UUID) -> Binding<Asset>
Binding<Asset> () -> Asset in
self.assets.first(where: $0.id == id ) ?? Asset()
set: (newValue) in
self.assets = self.assets.map asset in
if asset.id == id
return newValue
else
return asset
【讨论】:
我注意到 Binding 给了我一个错误:Cannot find Binding in scope
。这是因为 AssetStore 是一个类(不是结构)吗?
更正 - 这行得通!我只需要import SwiftUI
。我只导入了Combine 和Foundation。谢谢!以上是关于SwiftUI:从 ForEach 中删除项目导致索引超出范围的主要内容,如果未能解决你的问题,请参考以下文章
如果列表行在 SwiftUI 中包含文本字段,则无法从 foreach 中删除元素