如何避免递归 onChange 调用 SwiftUI

问题描述 投票:0回答:1
HStack{
  ForEach($rs.newsCategoryCollection){ $category in 
    Toggle(category.id, isOn: $category.isSelected)
      .toggleStyle(.button)
      .onChange(of: category.isSelected) {value in
          print("Changed")
          if value{
              rs.unselectOtherCategory(id: category.id)
              print("Selected: \(category.id)")
              viewModel.loadNews()
          }else{
              category.isSelected = true
              print("Deselected: \(category.id)")
          }
      }
  }
}

在这里,我尝试在水平视图中设置切换按钮。我希望将其用作选择器,一次仅选择一个类别。

因此,在用户选择任何一个类别后,应使用此方法取消选择另一个类别:

rs.unselectOtherCategory(id: category.id)

所以我使用 onChange 通过跟踪值的变化来检测用户选择类别。但是使用前面的方法取消选择所有其他类别会更改先前选择的类别的值并再次调用 onChange。

当我使用 else 块来确保如果用户点击选定的按钮并取消选择(这使得所有按钮都被取消选择)时,问题会变得更加复杂,else 块可以对抗它并选择唯一选定的按钮。

如何避免重复调用 onChange。

我希望用户能够点击一个按钮并选择它,其他按钮将被取消选择。但是,如果用户点击已选择的按钮,则该按钮将保持选中状态。

swift button swiftui toggle onchange
1个回答
0
投票

这看起来更像是一个

Picker
,而不是一组
Toggle
。当您希望一次选择一项时,请勿使用
Toggle

我建议使用

Picker
,带有
.segmented
选择器样式,看起来类似于一排按钮。

struct ContentView: View {
    
    @State var rs = ...

    @State var selectedCategory: NewsCategory.ID = "" // assuming NewsCategory.ID is a String

    var body: some View {
        
        Picker(selection: $selectedCategory) {
            ForEach(rs.newsCategoryCollection){ category in
                Text(category.id)
            }
        } label: {
            
        }
        .pickerStyle(.segmented)
    }
}

现在您甚至不需要

isSelected
中的
NewsCategory
属性。如果您出于某种原因确实需要它,您可以在
onChange(of: selectedCategory)
中添加
Picker

.onChange(of: selectedCategory) {
    rs.selectCategory(id: selectedCategory)
}

// selectCategory could be implemented as:
mutating func selectCategory(id: String) {
    for i in newsCategoryCollection.indices {
        if newsCategoryCollection[i].id != id {
            newsCategoryCollection[i].isSelected = false
        } else {
            newsCategoryCollection[i].isSelected = true
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.