在SwiftUI中的forEach中获取索引和值



我遵循了SwiftUI教程,并完成了它,结果运行良好,但作为一个挑战,我想尝试将相同的代码转换为MVVM结构,但最终的输出并不相同,必须在我的forEach块中同时获取索引值和索引本身。这是非MVVM格式代码:

struct Language:Identifiable{
var id:Int
var lang:String
}
struct ContentView_1:View{
@State private var languages : [Language] =
[ Language(id: 1, lang: "English"), 
Language(id: 2, lang:"Spanish"),
Language(id: 3, lang:"German"),
Language(id: 4, lang:"Japanese"),
Language(id: 5, lang:"Chinese"),
Language(id: 6, lang:"Korean"),
Language(id: 7, lang:"Others")
]
@State private var completed : [Language] = []
@Namespace var nameSpace
var body: some View{
VStack{
VStack{
if !self.languages.isEmpty{
ForEach(self.languages){ language in
Text(language.lang)
.frame(width:100,height:100)
.background(Color.yellow)
.matchedGeometryEffect(id: language.id, in:self.nameSpace)
.onTapGesture{
self.completed.append(language)
self.languages.removeAll { (lang) -> Bool in
if lang.id == language.id{
return true
}else { return false }
}
}
}
}
}
HStack{
Text("Completed Languages")
.font(.title)
.fontWeight(.bold)
}
HStack{
ForEach(self.completed){ language in
Text(language.lang)
.frame(width:100,height:100)
.background(Color.green)
.matchedGeometryEffect(id: language.id, in:self.nameSpace)
.onTapGesture{
self.languages.append(language)
self.completed.removeAll { (lang) -> Bool in
if lang.id == language.id{
return true
}else { return false }
}
}
}
}
}
.animation(.easeOut)
}}

上面的工作原理很好,正如我在下面的教程中所概述的那样,但这是我设法将其组合为MVVM结构

struct LanguageModel:Identifiable{
var id:Int
var lang:String
}
class LanguageViewModel:ObservableObject{
@Published var languageModel:[ LanguageModel ] =
[LanguageModel(id: 1, lang: "English"),
LanguageModel(id: 2, lang:"Spanish"),
LanguageModel(id: 3, lang:"German"),
LanguageModel(id: 4, lang:"Japanese"),
LanguageModel(id: 5, lang:"Chinese"),
LanguageModel(id: 6, lang:"Korean"),
LanguageModel(id: 7, lang:"Others")
]
}
struct ContentView_2:View{
@ObservedObject var langData = LanguageViewModel()
@ObservedObject var langDataCompleted = LanguageViewModel()
@Namespace var nameSpace
var body: some View{
VStack{
VStack{
if !self.langData.languageModel.isEmpty{
ForEach(langData.languageModel.indices, id:.self){ language in
Text(langData.languageModel[language].lang)
.frame(width:100,height:100)
.background(Color.yellow)
.matchedGeometryEffect(id: language, in: self.nameSpace)
.onTapGesture{
self.langDataCompleted.languageModel.append(langData.languageModel[language])
self.langData.languageModel.removeAll { (lang) -> Bool in
if lang.id == self.langData.languageModel[language].id{
return true
}else { return false }
}
}
}
}
HStack{
Text("Completed Languages")
.font(.title)
.fontWeight(.bold)
}
HStack{
ForEach(self.langDataCompleted.languageModel.indices ,id:.self){ language2 in
Text(self.langDataCompleted.languageModel[language2].lang)
.frame(width:100,height:100)
.background(Color.green)
.matchedGeometryEffect(id: self.langDataCompleted.languageModel[language2].id, in: self.nameSpace)
.onTapGesture{
self.langData.languageModel.append(langDataCompleted.languageModel[language2])
self.langDataCompleted.languageModel.removeAll { (lang) -> Bool in
if lang.id == self.langDataCompleted.languageModel[language2].id{
return true
}else{ return false }
}
}
}
}
.padding(.all)
}
}
.animation(.easeOut)
.onAppear{
self.langDataCompleted.languageModel = []
}
}
}

我知道有很多代码,但其中大部分只是设计元素和SwiftUI修饰符。确切的问题包括语言在被点击后没有直接移动到已完成的部分,与非MVVM格式相比,动画完全关闭,并且语言之间出现了奇怪的间隙。

在ForEach循环中使用.indices可能会破坏动画。请改用.enumerated。这里可以找到一个很好的解释:如何在SwiftUI中将.enumered((与ForEach一起使用?


注意:您不需要两个可观测对象。你可以有一个,但有两个@Published属性:

class LanguageViewModel: ObservableObject{
@Published var languages: [LanguageModel] = [
LanguageModel(id: 1, lang: "English"),
LanguageModel(id: 2, lang:"Spanish"),
LanguageModel(id: 3, lang:"German"),
LanguageModel(id: 4, lang:"Japanese"),
LanguageModel(id: 5, lang:"Chinese"),
LanguageModel(id: 6, lang:"Korean"),
LanguageModel(id: 7, lang:"Others")
]
@Published var completedLanguages: [LanguageModel] = []
}
经验法则:

索引在突变时无效。

当涉及追加/删除时,您不应该使用索引。

这不值得尝试。

在没有指数的情况下进行,看看进展如何。

最新更新