我有一个数组,我想遍历它,根据数组值初始化视图,并希望根据数组项索引执行操作
当我遍历对象时
B
所以,我尝试了另一种方法
C
但是第二种方法的问题是,例如,当我更改数组时,import
是否遵循了
ForEach(array, id: \.self) { item in
CustomView(item: item)
.tapAction {
self.doSomething(index) // Can't get index, so this won't work
}
}
ForEach((0..<array.count)) { index in
CustomView(item: array[index])
.tapAction {
self.doSomething(index)
}
}
中的视图不会更改,即使值已更改。我相信,发生这种情况是因为doSomething
并没有改变。
有解决方案吗?预先感谢。
答案 0 :(得分:23)
另一种方法是使用:
ForEach(Array(array.enumerated()), id: \.offset) { index, element in
// ...
}
答案 1 :(得分:14)
我需要一个更通用的解决方案,该解决方案可以处理所有类型的数据(实现RandomAccessCollection
),还可以通过使用范围防止未定义的行为。
我结束了以下内容:
public struct ForEachWithIndex<Data: RandomAccessCollection, ID: Hashable, Content: View>: View {
public var data: Data
public var content: (_ index: Data.Index, _ element: Data.Element) -> Content
var id: KeyPath<Data.Element, ID>
public init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (_ index: Data.Index, _ element: Data.Element) -> Content) {
self.data = data
self.id = id
self.content = content
}
public var body: some View {
ForEach(
zip(self.data.indices, self.data).map { index, element in
IndexInfo(
index: index,
id: self.id,
element: element
)
},
id: \.elementID
) { indexInfo in
self.content(indexInfo.index, indexInfo.element)
}
}
}
extension ForEachWithIndex where ID == Data.Element.ID, Content: View, Data.Element: Identifiable {
public init(_ data: Data, @ViewBuilder content: @escaping (_ index: Data.Index, _ element: Data.Element) -> Content) {
self.init(data, id: \.id, content: content)
}
}
extension ForEachWithIndex: DynamicViewContent where Content: View {
}
private struct IndexInfo<Index, Element, ID: Hashable>: Hashable {
let index: Index
let id: KeyPath<Element, ID>
let element: Element
var elementID: ID {
self.element[keyPath: self.id]
}
static func == (_ lhs: IndexInfo, _ rhs: IndexInfo) -> Bool {
lhs.elementID == rhs.elementID
}
func hash(into hasher: inout Hasher) {
self.elementID.hash(into: &hasher)
}
}
这样,问题中的原始代码就可以替换为:
ForEachWithIndex(array, id: \.self) { index, item in
CustomView(item: item)
.tapAction {
self.doSomething(index) // Now works
}
}
获取索引以及元素。
请注意,该API已镜像到SwiftUI的API-这意味着带有
id
参数的content
闭包的初始化程序不是一个@ViewBuilder
。 br /> 唯一的变化是id
参数可见并且可以更改
答案 2 :(得分:7)
对于基于非零的数组,避免使用枚举,而是使用 zip:
ForEach(Array(zip(items.indices, items)), id: \.0) { index, item in
// Add Code here
}
答案 3 :(得分:3)
以下方法的优点是,即使状态值发生更改,ForEach中的视图甚至也会更改:
struct ContentView: View {
@State private var array = [1, 2, 3]
func doSomething(index: Int) {
self.array[index] = Int.random(in: 1..<100)
}
var body: some View {
let arrayIndexed = array.enumerated().map({ $0 })
return List(arrayIndexed, id: \.element) { index, item in
Text("\(item)")
.padding(20)
.background(Color.green)
.onTapGesture {
self.doSomething(index: index)
}
}
}
}
...这也可以用于例如删除最后的分隔线 在列表中:
struct ContentView: View {
init() {
UITableView.appearance().separatorStyle = .none
}
var body: some View {
let arrayIndexed = [Int](1...5).enumerated().map({ $0 })
return List(arrayIndexed, id: \.element) { index, number in
VStack(alignment: .leading) {
Text("\(number)")
if index < arrayIndexed.count - 1 {
Divider()
}
}
}
}
}
答案 4 :(得分:1)
这对我有用:
{
"type": "response",
"@timestamp": "2019-07-24T19:41:20Z",
"tags": [],
"pid": 1,
"method": "get",
"statusCode": 200,
"req": {
"url": "/ui/fonts/inter_ui/Inter-UI-SemiBold.woff2",
"method": "get",
"headers": {
"host": "0.0.0.0:5601",
"connection": "keep-alive",
"pragma": "no-cache",
"cache-control": "no-cache",
"origin": "http://0.0.0.0:5601",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/74.0.3723.0 Safari/537.36",
"accept": "*/*",
"referer": "http://0.0.0.0:5601/login?next=%2Fs%2Finfrastructure%2Fapp%2Fkibana",
"accept-encoding": "gzip, deflate"
},
"remoteAddress": "127.0.0.1",
"userAgent": "127.0.0.1",
"referer": "http://0.0.0.0:5601/login?next=%2Fs%2Finfrastructure%2Fapp%2Fkibana"
},
"res": {
"statusCode": 200,
"responseTime": 4,
"contentLength": 9
},
"message": "GET /ui/fonts/inter_ui/Inter-UI-SemiBold.woff2 200 4ms - 9.0B"
}
{
"type": "log",
"@timestamp": "2019-07-24T19:43:18Z",
"tags": [
"reporting",
"browser-driver",
"chromium-driver-factory",
"headless-chromium-driver",
"error"
],
"pid": 1,
"message": "waitForSelector [data-shared-item],[data-shared-items-count] failed on http://0.0.0.0:5601/login?next=%2Fs%2Finfrastructure%2Fapp%2Fkibana#/visualize/edit/6f653c90-8ef8-11e9-bcc9-fbde2741907a?_g=()&_a=(filters:!(),linked:!f,query:(language:kuery,query:''),uiState:(vis:(legendOpen:!t)),vis:(aggs:!((enabled:!t,id:'1',params:(field:rabbitmq.queue.messages.total.count),schema:metric,type:max),(enabled:!t,id:'2',params:(drop_partials:!f,extended_bounds:(),field:'@timestamp',interval:auto,min_doc_count:1,useNormalizedEsInterval:!t),schema:segment,type:date_histogram),(enabled:!t,id:'3',params:(field:rabbitmq.queue.name,missingBucket:!f,missingBucketLabel:Missing,order:desc,orderBy:'1',otherBucket:!f,otherBucketLabel:Other,size:15),schema:group,type:terms)),params:(addLegend:!t,addTimeMarker:!t,addTooltip:!t,categoryAxes:!((id:CategoryAxis-1,labels:(show:!t,truncate:100),position:bottom,scale:(type:linear),show:!t,style:(),title:(),type:category)),grid:(categoryLines:!t,valueAxis:!n),legendPosition:right,seriesParams:!((data:(id:'1',label:'Max%20rabbitmq.queue.messages.total.count'),drawLinesBetweenPoints:!t,interpolate:cardinal,mode:normal,show:true,showCircles:!t,type:line,valueAxis:ValueAxis-1)),times:!(),type:line,valueAxes:!((id:ValueAxis-1,labels:(filter:!f,rotate:0,show:!t,truncate:100),name:LeftAxis-1,position:left,scale:(mode:normal,type:linear),show:!t,style:(),title:(text:'Max%20rabbitmq.queue.messages.total.count'),type:value))),title:'RabbitMQ%20Queued%20Messages',type:line))&forceNow=2019-07-24T19:41:16.435Z"
}
答案 5 :(得分:1)
我为此基于Stone出色的解决方案创建了专用的View
:
struct EnumeratedForEach<ItemType, ContentView: View>: View {
let data: [ItemType]
let content: (Int, ItemType) -> ContentView
init(_ data: [ItemType], @ViewBuilder content: @escaping (Int, ItemType) -> ContentView) {
self.data = data
self.content = content
}
var body: some View {
ForEach(Array(self.data.enumerated()), id: \.offset) { idx, item in
self.content(idx, item)
}
}
}
现在您可以像这样使用它:
EnumeratedForEach(items) { idx, item in
...
}
答案 6 :(得分:0)
这是一个简单的解决方案,尽管对上述解决方案效率不高。
在“点击操作”中,通过项目
.tapAction {
var index = self.getPosition(item)
}
然后创建一个函数,通过比较id查找该项目的索引
func getPosition(item: Item) -> Int {
for i in 0..<array.count {
if (array[i].id == item.id){
return i
}
}
return 0
}
答案 7 :(得分:0)
2021 解决方案如果您使用基于非零的数组,请避免使用枚举:
ForEach(array.indices,id:\.self) { index in
VStack {
Text(array[index].name)
.customFont(name: "STC", style: .headline)
.foregroundColor(Color.themeTitle)
}
}
}