【问题标题】:Getting both index and value within forEach in SwiftUI在 SwiftUI 中的 forEach 中获取索引和值
【发布时间】:2020-07-19 21:41:04
【问题描述】:

我按照 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 格式相比,动画完全关闭,并且语言之间出现了奇怪的差距。

【问题讨论】:

    标签: arrays swift swiftui swift5


    【解决方案1】:

    在 ForEach 循环中使用 .indices 可能会破坏动画。请改用.enumerated。可以在这里找到一个很好的解释:How do you use .enumerated() with ForEach in SwiftUI?


    注意:您不需要 两个 ObservableObjects。您可以拥有一个但有两个 @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] = []
    }
    

    【讨论】:

    • 不幸的是,它没有完成这项工作。该应用程序管理运行,但所有问题仍然存在。
    【解决方案2】:

    经验法则:

    索引在突变时失效。

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

    而且不值得尝试。

    不使用索引,看看效果如何。

    【讨论】:

      猜你喜欢
      • 2019-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多