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)")
          }
      }
  }
}

在这里,我试着在水平视图中切换按钮.我希望这是用来作为 Select 器,一次只 Select 一个类别.

因此,在用户 Select 任何一个类别后,应使用以下方法取消 Select 另一个类别:rs.unselectOtherCategory(id: category.id)

因此,我使用onChange通过跟踪值的变化来检测用户 Select 类别.但使用前面的方法取消 Select 所有其他类别会更改以前 Select 的类别的值,并再次调用onChange.

当我使用Else块来确保如果用户点击选定的按钮并取消 Select (这会使所有按钮取消选中)时,问题变得更加复杂,Else块可以对其进行计数并 Select 唯一选定的按钮.

我如何才能避免这种对onChange的重复调用.

我想让用户能够点击一个按钮并 Select 它,其他按钮将被取消 Select .但是,如果用户点击已经选中的按钮,该按钮将保持选中状态.

推荐答案

这看起来更像一个Picker,而不是一组Toggle.如果您希望一次只 Select 一件事,请不要使用Toggle.

您可以将Toggle更改为Button:

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()
        }
    }
}

现在,你甚至不需要在NewsCategory号拥有isSelected套房产了.如果你真的因为某些原因需要它,你可以在rs的基础上加一个selectCategory的方法.

// 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分吧.

Swift相关问答推荐

为什么Swift在某些链调用中不能对不可变值使用变异成员,而在其他链调用中则不能使用变异成员?

为什么在Swift属性Wrapper中需要类型注释?

Swift 5.10:无法从非隔离的deinit访问属性*,这是Swift 6中的错误''''

";async let和";async之间的差异让我们等待";

在Swift中,将秒设置为0也等于将1添加到分钟

为什么这个快速代码不显示文本字段?

如何将多个完成处理程序转换为异步?

SwiftUI 拖放:如果没有上下文类型,则无法解析对成员‘first’的引用

我如何遵守与演员的协议?

如何在 SwiftUI 中将图像传递到 2 个视图

使 Picker 与其他 BinaryInteger 类型兼容

如何更改任务栏 colored颜色 和 sf 符号 colored颜色 ?

在 Swift 中,如何查看 Process() 传递给 shell 的字符串?

macOS 守护进程应该由Command Line ToolXcode 模板制作吗?

Swift 动画 WithDuration 语法是什么?

在 Swift 中将计时器标签格式化为小时:分钟:秒

Swift - 迭代 struct 对象时如何对其进行变异

Swift 3:小数到 Int

URLComponents.url 为零

Switch 语句中的字符串:String不符合协议IntervalType