【问题标题】:How to reload ForEach loop items after sheet is dismissed关闭工作表后如何重新加载 ForEach 循环项目
【发布时间】:2021-10-22 12:41:33
【问题描述】:

我目前正在工作表视图中添加新播放器,在我关闭工作表视图后,主视图 ForEach 循环应该更新所有项目,但它没有。

在 ViewModel 中,我将播放器保存到 json 文件中,保存后我立即获取它。

在工作表视图中保存新用户名后,它会使用新用户名打印数组,但 ForEach 循环不会在视图中更新。

关闭工作表视图后如何重新加载视图?

我的主视图:

struct PlayersView: View {
  @ObservedObject var viewModel: PlayersViewModel

  @State var course: Course
  @State var isSelected: Bool = false
  @State private var selectedItem: [Bool]
  @State var selectedPlayers = [String]()

  init(viewModel: PlayersViewModel, course: Course) {
    self.viewModel = viewModel
    self._selectedItem = State(initialValue: Array(repeating: false, count: viewModel.players.count))
    self.course = course
  }

  var body: some View {
   VStack {
    ForEach(0..<viewModel.players.count) { item in
      PlayerCell(isSelected: selectedItem[item], player: viewModel.players[item].playerName)
        .onTapGesture {
          selectedItem[item].toggle()
          if selectedPlayers.contains(viewModel.players[item].playerName) {
            self.selectedPlayers = selectedPlayers.filter({ $0 != viewModel.players[item].playerName})
          } else {
            selectedPlayers.append(viewModel.players[item].playerName)
          }
        }
    }
  }

我的 viewModel 看起来像这样:

class PlayersViewModel: ObservableObject {

 @Published var players = [Player]()

 init() {
  readJSON()
 }

func saveJSON(username: String) {
  var array = players
  array.append(Player(playerName: username))
 do {
  let fileURL = try FileManager.default
    .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    .appendingPathComponent("example2.json")

  try JSONEncoder().encode(array)
    .write(to: fileURL)
  readJSON()
} catch {
  print(error.localizedDescription)
}
}

func readJSON() {
 do {
  let fileURL = try FileManager.default
    .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    .appendingPathComponent("example2.json")

  let data = try Data(contentsOf: fileURL)
  let players = try JSONDecoder().decode([Player].self, from: data)
  self.players = players
  print(players)
} catch {
  print(error.localizedDescription)
  }
 }
 }

【问题讨论】:

  • 你好,PlayersView是sheet的code还是要更新的View的code?无论如何,请添加缺少的视图的代码。
  • 您能否显示工作表视图以及如何调用它,因为您可以在此工作表中进行更新。
  • 你需要将courseselectedItems从PlayersView移动到视图模型中,否则你没有正确使用SwiftUI状态变量,你可能会遇到麻烦。此外,当您部分在视图上执行逻辑并部分在视图模型中执行逻辑时,您很难得到正确的逻辑。

标签: swift foreach swiftui reload


【解决方案1】:

查看您的控制台,您会看到以下警告:

ForEach>> 计数 (2) != 它的初始计数 (1)。 ForEach(_:content:) 应该只用于 constant 数据。而是将数据符合Identifiable 或使用ForEach(_:id:content:) 并提供明确的id

您可以将0..&lt;viewModel.players.count 替换为viewModel.players.indices 来修复它

但如果你这样做了,你将面临崩溃,因为你在 init 中初始化 selectedItem,而当新项目被添加时它没有足够的项目

您可以以某种方式将新的false 添加到此数组中,但是如果您想在乞求中添加新项目,则在更新selectedItem 标志数组时会遇到更多问题

我建议您将唯一标识符添加到您的 Player 项目中,并为选定的玩家存储 Set 的此 ID

struct PlayersView: View {
    @ObservedObject var viewModel: PlayersViewModel

    @State var isSelected: Bool = false
    @State private var selectedIds = Set<String>()
    @State var selectedPlayers = [String]()

    init(viewModel: PlayersViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {
        VStack {
            ForEach(viewModel.players) { player in
                let selected = selectedIds.contains(player.id)
                Text("\(String(describing: selected)) \(player.playerName)")
                    .onTapGesture {
                        if selected {
                            selectedIds.remove(player.id)
                        } else {
                            selectedIds.insert(player.id)
                        }
                        selectedPlayers = viewModel.players
                            .filter { selectedIds.contains($0.id) }
                            .map { $0.playerName }
                    }
            }
        }
    }
}

struct Player: Codable, Identifiable, Hashable {
    let id: String
    let playerName: String
}

class PlayersViewModel: ObservableObject {
    
    @Published var players = [Player]()
    
    init() {
        readJSON()
    }
    
    func saveJSON(username: String) {
        var array = players
        array.append(Player(id: UUID().uuidString, playerName: username))
        do {
            let fileURL = try FileManager.default
                .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appendingPathComponent("example2.json")
            
            try JSONEncoder().encode(array)
                .write(to: fileURL)
            readJSON()
        } catch {
            print(error.localizedDescription)
        }
    }
    
    func readJSON() {
        do {
            let fileURL = try FileManager.default
                .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
                .appendingPathComponent("example2.json")
            
            let data = try Data(contentsOf: fileURL)
            let players = try JSONDecoder().decode([Player].self, from: data)
            self.players = players
            print(players)
        } catch {
            print(error.localizedDescription)
        }
    }
}

【讨论】:

    猜你喜欢
    • 2020-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-17
    • 1970-01-01
    • 2012-09-21
    • 1970-01-01
    • 2019-01-21
    相关资源
    最近更新 更多