Ios 如何让SwiftUI动态响应用户事件?
我是一名经验丰富的iOS开发人员,但对SwiftUI来说是新手。我试图创建一个由数据模型支持的动态接口。以下说明了我正在尝试做的事情: 模型对象:Ios 如何让SwiftUI动态响应用户事件?,ios,swift,user-interface,mvvm,swiftui,Ios,Swift,User Interface,Mvvm,Swiftui,我是一名经验丰富的iOS开发人员,但对SwiftUI来说是新手。我试图创建一个由数据模型支持的动态接口。以下说明了我正在尝试做的事情: 模型对象: class Book : Hashable { static func == (lhs: Book, rhs: Book) -> Bool { return lhs.title == rhs.title && lhs.author == rhs.author && lhs.favorite
class Book : Hashable {
static func == (lhs: Book, rhs: Book) -> Bool {
return lhs.title == rhs.title && lhs.author == rhs.author && lhs.favorite == rhs.favorite
}
var title : String
var author : String
var favorite : Bool = false
init(title : String, author: String) {
self.title = title
self.author = author
}
func hash(into hasher: inout Hasher) {
hasher.combine(title)
hasher.combine(author)
hasher.combine(favorite)
}
}
内容视图:
struct ContentView: View {
@State private var library = [
Book(title: "Lord of The Rings", author: "J.R.R. Tolkien"),
Book(title: "Harry Potter", author: "J.K. Rowling"),
Book(title: "Under the Dome", author: "Steven King")
]
var body: some View {
NavigationView {
MasterView(books: $library)
.navigationBarTitle(Text("Library"))
}.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}
struct MasterView: View {
@Binding var books: [Book]
var body: some View {
List {
ForEach(books, id: \.self) { book in
HStack {
Button(action: {}, label: {
(book.favorite ?
Image(systemName: "heart.fill") :
Image(systemName: "heart"))
.imageScale(.large)
}).onTapGesture {
book.favorite.toggle()
}
Text("\(book.title) by \(book.author)")
}
}
}
}
}
结果如下:
外观是正确的。但是,我希望像按钮一样的按钮能够工作:我希望点击来更新模型对象中最喜欢的变量,然后按钮本身应该适当地更改
第一个发生了,第二个没有
我做错了什么,如何获得我想要的动态UI更新?这种方法的主要问题是
@State private var library = ...
是一个引用数组,当您在MasterView
中更改对象时,这些引用不会更改,因此库的状态不会更改,因此ContentView
不会刷新
以下是使此方法起作用所需的最低限度的更改
1) 通过更改初始值设定器使模型可复制(在类目手册中的更改
)
2) 强制更改书本会导致更改库(在struct MasterView
中更改)
这很有效。在Xcode 11.2/iOS 13.2上测试。(如果需要来自我的测试环境的完整代码,请毫不犹豫地通知我)。另一个解决方案是遵守ObserveObject协议,并使用@Published property observer,以便监视我们类的任何视图都会注意到属性已更改并重新加载。像这样
class Book : Hashable, ObservableObject {
static func == (lhs: Book, rhs: Book) -> Bool {
return lhs.title == rhs.title && lhs.author == rhs.author && lhs.favorite == rhs.favorite
}
var title : String
var author : String
@Published var favorite : Bool = false
init(title : String, author: String) {
self.title = title
self.author = author
}
func hash(into hasher: inout Hasher) {
hasher.combine(title)
hasher.combine(author)
hasher.combine(favorite)
}}
然后为每行创建单独的视图
struct Row: View {
@ObservedObject var onebook: Book
var body: some View {
HStack {
Button(action: {}, label: {
(onebook.favorite ?
Image(systemName: "heart.fill") :
Image(systemName: "heart"))
.imageScale(.large)
}).onTapGesture {
self.onebook.favorite.toggle()
}
Text("\(onebook.title) by \(onebook.author)")
}
}}
struct MasterView: View {
@Binding var books: [Book]
var body: some View {
List {
ForEach(books, id: \.self) { book in
Row(onebook: book)
}
}
}
这比另一个答案更接近于我想要的——数据模型支持的要点是有一个源直接驱动UI。显然,@Binding
对应于@State
,而@ObservedObject
对应于@observeobject
,但将两者混用是行不通的。
class Book : Hashable, ObservableObject {
static func == (lhs: Book, rhs: Book) -> Bool {
return lhs.title == rhs.title && lhs.author == rhs.author && lhs.favorite == rhs.favorite
}
var title : String
var author : String
@Published var favorite : Bool = false
init(title : String, author: String) {
self.title = title
self.author = author
}
func hash(into hasher: inout Hasher) {
hasher.combine(title)
hasher.combine(author)
hasher.combine(favorite)
}}
struct Row: View {
@ObservedObject var onebook: Book
var body: some View {
HStack {
Button(action: {}, label: {
(onebook.favorite ?
Image(systemName: "heart.fill") :
Image(systemName: "heart"))
.imageScale(.large)
}).onTapGesture {
self.onebook.favorite.toggle()
}
Text("\(onebook.title) by \(onebook.author)")
}
}}
struct MasterView: View {
@Binding var books: [Book]
var body: some View {
List {
ForEach(books, id: \.self) { book in
Row(onebook: book)
}
}
}