如何避免 Toggle 视图递归 onChange 调用 SwiftUI

How to avoid Toggle view recursive onChange call SwiftUI

提问人:Mahin 提问时间:10/14/2023 最后编辑:Mahin 更新时间:10/14/2023 访问量:63

问:

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 按钮 SwiftUI 切换 更改

评论

0赞 Sweeper 10/14/2023
听起来你不应该在这里使用。创建自己的选取器/单选按钮视图。或与选择器样式一起使用。TogglePicker.segmented
0赞 malhal 10/15/2023
如果你重新组织你的真实来源,你可以完全消除对事实来源的需求。onChange

答:

1赞 Sweeper 10/14/2023 #1

这看起来更像是一组 s。当您希望一次选择一件事时,不要使用 s。PickerToggleToggle

您可以将 s 更改为 s:ToggleButton

HStack{
    ForEach($rs.newsCategoryCollection){ $category in
        Button(category.id) {
            if !category.isSelected {
                category.isSelected = true
                rs.unselectOtherCategory(id: category.id)
                viewModel.loadNews()
            }
        }
        .padding(8)
        .background(.accent.opacity(category.isSelected ? 0.1 : 0), in: RoundedRectangle(cornerRadius: 10))
    }
}

也就是说,我建议使用 ,带有选择器样式,它看起来类似于一排按钮。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)
        .onChange(of: selectedCategory) {
            viewModel.loadNews()
        }
    }
}

现在,您甚至不需要 中的属性。如果出于某种原因确实需要它,可以添加一个方法,例如isSelectedNewsCategoryselectCategoryrs

// 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
        }
    }
}

然后将其调入 .onChange

评论

0赞 Mahin 10/14/2023
多谢。但我正在寻找一个 UI,其中将有水平对齐的选项,每个选项由每个选项分隔,例如在 youtube 应用程序的顶部,您可以在其中选择全部、音乐或其他内容。
0赞 Sweeper 10/14/2023
@Mahin看到编辑。也许你应该把 s 改成 s。ToggleButton
0赞 Mahin 10/14/2023
美妙!我错过了一个非常简单的解决方案。多谢!