swift - 如何更改 @Published var 会影响另一个 @Published var,例如 Binding
问题描述
有没有办法让@Published var 改变它会影响另一个@Published var,比如Binding var?
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
ForEach(viewModel.items, id: \.self){ item in
Button("Select \(item.title)"){
viewModel.selectedItem = item
viewModel.selectedItem.title = "grape"
print(viewModel.items) // <-- This will print [Test.Item(title: "Apple"), Test.Item(title: "Banana"), Test.Item(title: "Orange")]
}
}
}
}
struct Item: Hashable {
var title: String
}
class ViewModel: ObservableObject {
@Published var items: [Item] = [Item(title: "Apple"), Item(title: "Banana"), Item(title: "Orange")]
@Published var selectedItem: Item = Item(title: "default") //<-- I want this to be a binding of an item in the bucket above, so what I modify to selectedItem will affect item in the bucket.
}
解决方案
三种可能的解决方案——就一个 @Published 对另一个作出反应而言,最后一个可能最接近您的要求。
这是使用的版本didSet
:
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
ForEach(viewModel.items, id: \.self){ item in
Button("Select \(item.title)"){
viewModel.selectedItem = item
viewModel.selectedItem?.title = "grape"
print(viewModel.items)
}
}
}
}
struct Item: Hashable {
var id = UUID()
var title: String
}
class ViewModel: ObservableObject {
@Published var selectedItem : Item? {
didSet { //when there's a new value, see if it should be mapped into the original item set
self.items = self.items.map {
if let selectedItem = selectedItem, selectedItem.id == $0.id {
return selectedItem
}
return $0
}
}
}
@Published var items: [Item] = [Item(title: "Apple"), Item(title: "Banana"), Item(title: "Orange")]
}
这是一种可能性,使用自定义Binding
:
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
ForEach(viewModel.items, id: \.self){ item in
Button("Select \(item.title)"){
viewModel.selectedItemID = item.id
viewModel.selectedItem?.wrappedValue.title = "grape"
print(viewModel.items)
}
}
}
}
struct Item: Hashable {
var id = UUID()
var title: String
}
class ViewModel: ObservableObject {
@Published var selectedItemID : UUID?
@Published var items: [Item] = [Item(title: "Apple"), Item(title: "Banana"), Item(title: "Orange")]
var selectedItem : Binding<Item>? {
guard let selectedItemID = selectedItemID else {
return nil
}
return .init { () -> Item in
self.items.first(where: {$0.id == selectedItemID}) ?? Item(title: "")
} set: { (item) in
self.items = self.items.map {
if $0.id == selectedItemID { return item }
return $0
}
}
}
}
最后,使用 Combine 的版本:
import SwiftUI
import Combine
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
ForEach(viewModel.items, id: \.self){ item in
Button("Select \(item.title)"){
viewModel.selectedItem = item
viewModel.selectedItem?.title = "grape"
print(viewModel.items)
}
}
}
}
struct Item: Hashable {
var id = UUID()
var title: String
}
class ViewModel: ObservableObject {
@Published var selectedItem : Item?
@Published var items: [Item] = [Item(title: "Apple"), Item(title: "Banana"), Item(title: "Orange")]
private var cancellable : AnyCancellable?
init() {
cancellable = $selectedItem
.compactMap { $0 } //remove nil values
.sink(receiveValue: { (newValue) in
self.items = self.items.map {
newValue.id == $0.id ? newValue : $0 //if the ids match, return the new value -- if not, return the old one
}
})
}
}
这三者都有相同的功能——这真的取决于你喜欢哪种编码风格。我个人可能会选择最后一个选项,因为 Combine 和 SwiftUI 往往可以很好地协同工作。它也很容易通过过滤等进行扩展。
推荐阅读
- javascript - React 嵌套路由问题(无法显示子组件)
- c - 尝试终止时使用管道的 C 程序挂起
- javascript - 使用 js 将活动类添加到当前选定的 svg
- angular - 如何在角度应用程序之外访问角度路由数据
- php - Codeigniter 2.0.2 表单验证 set_rules 自定义错误消息不起作用
- gitlab - 返回 404 的 Gitlab 片段
- php - 当前月份的日期格式问题
- 8thwall-xr - 第 8 面墙图像目标未跟踪图像
- python-3.x - python repr函数在调用时不使用引号
- java - 有没有办法在控制器类之外访问 Spring 控制器参数?