删除具有绑定布尔值的列表项 - SwiftUI
Posted
技术标签:
【中文标题】删除具有绑定布尔值的列表项 - SwiftUI【英文标题】:Deleting List Item With A Binding Boolean Value - SwiftUI 【发布时间】:2020-10-06 14:46:47 【问题描述】:在我的应用程序中,我有一个屏幕,用户可以使用 Swift Toggle
打开和关闭(地图的)过滤器,也可以删除它们(使用 Edit Button
)。我使用Binding
布尔变量来连接切换开关,以便我可以对每个更改做出反应,代码如下:
//The filter data model
class DSFilter
var filterId : Int
var filterTitle : String
var isActive : Bool
init(filterId : Int, filterTitle : String, isActive : Bool)
self.filterId = filterId
self.filterTitle = filterTitle
self.isActive = isActive
//The data model representable
struct filterItem : View
@Binding var filter : DSFilter
@Binding var isOn : Bool
@State var image = Image("defPerson")
var body : some View
HStack
image.resizable().frame(width: 45, height: 45).clipShape(Circle())
VStack(alignment: .leading, spacing: 8)
Text(filter.filterTitle).font(Font.custom("Quicksand-Bold",size: 15))
Text(filter.filterTitle).font(Font.custom("Quicksand-Medium",size: 13)).foregroundColor(.gray)
.padding(.leading)
Spacer()
HStack
Toggle("",isOn: $isOn).padding(.trailing, 5).onReceive([self.isOn].publisher.first()) (value) in
self.filter.isActive = self.isOn
.frame(width: 30)
.frame(height: 60)
.onAppear()
self.isOn = self.filter.isActive
//The view where the user has the control
struct FilterView: View
@State var otherFilters : [addedFilter] = []
@Binding var isActive : Bool
var body: some View
VStack
Capsule().fill(Color.black).frame(width: 50, height: 5).padding()
ZStack
HStack
Spacer()
Text("Filters").font(Font.custom("Quicksand-Bold", size: 20))
Spacer()
.padding()
HStack
Spacer()
EditButton()
.padding()
List
ForEach(0..<self.otherFilters.count, id:\.self) i in
filterItem(filter: self.$otherFilters[i].filter, isOn: self.$otherFilters[i].isOn)
.onDelete(perform:removeRows)
Spacer()
func removeRows(at offsets: IndexSet)
print("deleting")
print("deleted filter!")
self.otherFilters.remove(at: Array(offsets)[0]) //I use it like so because you can only delete 1 at a time anyway
//The class I use to bind the isOn value so I can react to change
struct addedFilter : Identifiable
var id : Int
var filter : DSFilter
var isOn : Bool
init(filter : DSFilter)
self.id = Int.random(in: 0...100000)
self.filter = filter
self.isOn = filter.isActive
当我现在使用删除时,我收到以下错误 (Xcode 12.0
):
Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444
2020-10-06 17:42:12.567235+0300 Hynt[5207:528572] Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444
我需要更改哪些内容才能删除这些项目?我应该对Toggle
的Binding
问题采取不同的方法吗?
【问题讨论】:
由于您尝试在索引 0 处删除,因此数组为空。如,长度 = 0。 我没有尝试在索引 0 中删除,我在 offsets 数组的第一个索引处删除(可以是 0 但不一定是),并且数组不为空 【参考方案1】:嗨,而不是保留三个不同的数组来管理 addedFilter、DSFilter、isOn。你为什么不试试下面的一个单一的模型
class FFilter: Identifiable
var id: Int
var addedFilter: addedFilter
var dsFilter: DSFilter
var isOn: Bool
init(addedFilter: addedFilter, dsFilter: DSFilter, isOn: Bool)
self.id = addedFilter.id
self.addedFilter = addedFilter
self.dsFilter = dsFilter
self.isOn = isOn
ViewModel 更改代码如下:
class ViewModel: ObservableObject
@Published var superFilters: [FFilter] = [
FFilter(
addedFilter: addedFilter(
filter: DSFilter(filterId: 1, filterTitle: "Title1", isActive: true)
),
dsFilter: DSFilter(filterId: 1, filterTitle: "Title1", isActive: true),
isOn: true),
FFilter(
addedFilter: addedFilter(
filter: DSFilter(filterId: 2, filterTitle: "Title2", isActive: false)
),
dsFilter: DSFilter(filterId: 2, filterTitle: "Title2", isActive: false),
isOn: false),
FFilter(
addedFilter: addedFilter(filter: DSFilter(filterId: 3, filterTitle: "Title3", isActive: true)),
dsFilter: DSFilter(filterId: 3, filterTitle: "Title3", isActive: true),
isOn: true),
FFilter(
addedFilter: addedFilter(
filter: DSFilter(filterId: 4, filterTitle: "Title4", isActive: true)
),
dsFilter: DSFilter(filterId: 4, filterTitle: "Title4", isActive: true),
isOn: true),
FFilter(
addedFilter: addedFilter(
filter: DSFilter(filterId: 5, filterTitle: "Title5", isActive: false)
),
dsFilter: DSFilter(filterId: 5, filterTitle: "Title5", isActive: false),
isOn: false)
]
然后更改您的列表代码
List
ForEach(viewModel.superFilters) filter in
filterItem(filter: .constant(filter.dsFilter), isOn: .constant(filter.isOn))
.onDelete(perform: removeRows(at:))
和 removeRows 方法:
func removeRows(at offsets: IndexSet)
print("deleting at \(offsets)")
self.viewModel.superFilters.remove(atOffsets: offsets)
我希望它会起作用。如果您仍然遇到任何问题,请告诉我
【讨论】:
DSFilter 模型仍有重构空间,您可以使用一次。 这行得通!但是只有一件事:我希望我的用户能够打开和关闭过滤器,并且按照您所说的不可能,我该如何更改它? @TheMachineX 如果我看看你的过滤器模型。添加了Filter和DSFilter。两者都做同样的工作。我说的是不是在两个不同的模型中保留相同的东西。只需创建一个简单的模型,它可以将过滤器设置为 ON 或 OFF。因此,从您的数组中创建一个自定义 getter 属性,只需迭代打开或关闭的那个。您可以随意拨打电话。如果您遇到任何问题,请告诉我。【参考方案2】:我建议您将removeRows()
函数中的行从
self.otherFilters.remove(at: Array(offsets)[0]
到
self.otherFilters.remove(atOffsets: offsets)
【讨论】:
【参考方案3】:所以,经过多次尝试和失败后,我得出了答案。
问题似乎出在绑定部分 - 如果我从中绑定一个元素,编译器将无法处理数组大小的变化,所以我做了以下操作:
首先,我将filterItem struct
更改为过滤器属性为@State
而不是@Binding
:
struct filterItem : View
@State var filter : DSFilter
@Binding var isOn : Bool
其次,我已将addedFilter struct
更改为以下内容:
struct addedFilter : Identifiable
var id : Int
var isOn : Bool
init(filter : DSFilter)
self.id = filter.filterId
self.isOn = filter.isActive
ForEach
循环如下:
ForEach(0..<self.normOthFilters.count, id:\.self) i in
filterItem(filter: self.normOthFilters[i], isOn: self.$otherFilters[i].isOn)
.onDelete(perform:removeRows)
到我添加的主视图:
@State var normDefFilters : [DSFilter] = []
@State var normOthFilters : [DSFilter] = []
最后是 onDelete 函数如下:
func removeRows(at offsets: IndexSet)
print("deleting")
print("deleted filter!")
self.normOthFilters.remove(atOffsets: offsets)
最后,我没有更改绑定值的值,因此我没有收到任何错误!
【讨论】:
以上是关于删除具有绑定布尔值的列表项 - SwiftUI的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 绑定布尔 if 语句(无法将类型 'Binding<Bool>' 的值转换为预期的条件类型 'Bool')