在 SwiftUI 中创建一个包含自定义 类 数组的列表
Create a List with an array of custom classes in SwiftUI
我尝试在 SwiftUI 中创建一个自定义 classes 列表,但在 UI 更新时仍然遇到问题。我让我的 class 符合 BindableObject
并在 属性 更改时正确调用 didChange.send()
函数,但更改似乎没有传递到数组,由于视图没有更新,当我在数组中更改自定义 class 的 属性 时。
这是我的意思的一个基本示例:
class Media: BindableObject, Identifiable {
typealias PublisherType = PassthroughSubject<Void, Never>
var didChange = PublisherType()
var id: Int {
didSet {
didChange.send()
}
}
var name: String {
didSet {
print("Name changed from \(oldValue) to \(self.name)")
didChange.send()
}
}
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
struct ContentView : View {
@State private var media = [
Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3"),
]
var body: some View {
VStack {
List(media) { media in
Text(media.name)
}
HStack {
Button(action: {
self.media.first!.name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.media.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}
按下 "Change" 按钮时,它只会更改数组中第一个媒体对象的名称,不会导致重新渲染视图。当我按下 "Add" 按钮时,他将一个对象添加到数组中,因此重新渲染 UI 并显示第一个对象的更改名称(从按下 "Change" 开始)。
现在我的问题是,如果有办法 link Media
的发布者到 Array<Media>
的发布者,那么当 Media
发布者触发时,Array<Media>
Publisher 也会触发,因此导致视图重新呈现。
当我在单独的视图中移动 Text(media.name)
时(将媒体保存为 @State
属性,它按预期工作,因为子视图本身请求重新渲染.
但是假设我不想使用自定义视图而只想使用简单的 Text
视图,有什么方法可以实现这一点吗?
你正在混合两种不同的东西。 @State 旨在用于视图内部的任何内容。而 @BindableObject 是用来保存视图外部的东西(即你的数据模型)。当您定义一个@BindableObject 时,您不使用@State 引用它,而是使用@ObjectBinding。
我不知道是怎么回事,但如果你打算使用@State,实现应该是这样的:
struct Media {
var id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
struct ContentView : View {
@State var media = [
Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3"),
]
var body: some View {
VStack {
List(media.identified(by: \.id)) { media in
Text(media.name)
}
HStack {
Button(action: {
self.media[0].name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.media.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}
您主要将@State 用于视图的某些内部状态,但在制作原型时也会使用。您对 Media 对象的意图是什么?
更新
使用@ObjectBinding 实现它:
假设您在 SceneDelegate 中实例化 CustomView,您需要将库传递给它:
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let library = MediaLibrary(store: [Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3")])
window.rootViewController = UIHostingController(rootView: ContentView(library: library))
self.window = window
window.makeKeyAndVisible()
}
以及实施:
struct Media {
let id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
class MediaLibrary: BindableObject {
typealias PublisherType = PassthroughSubject<Void, Never>
var didChange = PublisherType()
var store: [Media] {
didSet {
didChange.send()
}
}
init(store: [Media]) {
self.store = store
}
}
struct ContentView : View {
@ObjectBinding var library: MediaLibrary
var body: some View {
VStack {
List(self.library.store.identified(by: \.id)) { media in
Text(media.name)
}
HStack {
Button(action: {
self.library.store[0].name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.library.store.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}
我尝试在 SwiftUI 中创建一个自定义 classes 列表,但在 UI 更新时仍然遇到问题。我让我的 class 符合 BindableObject
并在 属性 更改时正确调用 didChange.send()
函数,但更改似乎没有传递到数组,由于视图没有更新,当我在数组中更改自定义 class 的 属性 时。
这是我的意思的一个基本示例:
class Media: BindableObject, Identifiable {
typealias PublisherType = PassthroughSubject<Void, Never>
var didChange = PublisherType()
var id: Int {
didSet {
didChange.send()
}
}
var name: String {
didSet {
print("Name changed from \(oldValue) to \(self.name)")
didChange.send()
}
}
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
struct ContentView : View {
@State private var media = [
Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3"),
]
var body: some View {
VStack {
List(media) { media in
Text(media.name)
}
HStack {
Button(action: {
self.media.first!.name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.media.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}
按下 "Change" 按钮时,它只会更改数组中第一个媒体对象的名称,不会导致重新渲染视图。当我按下 "Add" 按钮时,他将一个对象添加到数组中,因此重新渲染 UI 并显示第一个对象的更改名称(从按下 "Change" 开始)。
现在我的问题是,如果有办法 link Media
的发布者到 Array<Media>
的发布者,那么当 Media
发布者触发时,Array<Media>
Publisher 也会触发,因此导致视图重新呈现。
当我在单独的视图中移动 Text(media.name)
时(将媒体保存为 @State
属性,它按预期工作,因为子视图本身请求重新渲染.
但是假设我不想使用自定义视图而只想使用简单的 Text
视图,有什么方法可以实现这一点吗?
你正在混合两种不同的东西。 @State 旨在用于视图内部的任何内容。而 @BindableObject 是用来保存视图外部的东西(即你的数据模型)。当您定义一个@BindableObject 时,您不使用@State 引用它,而是使用@ObjectBinding。
我不知道是怎么回事,但如果你打算使用@State,实现应该是这样的:
struct Media {
var id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
struct ContentView : View {
@State var media = [
Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3"),
]
var body: some View {
VStack {
List(media.identified(by: \.id)) { media in
Text(media.name)
}
HStack {
Button(action: {
self.media[0].name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.media.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}
您主要将@State 用于视图的某些内部状态,但在制作原型时也会使用。您对 Media 对象的意图是什么?
更新
使用@ObjectBinding 实现它:
假设您在 SceneDelegate 中实例化 CustomView,您需要将库传递给它:
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let library = MediaLibrary(store: [Media(id: 0, name: "Name 0"),
Media(id: 1, name: "Name 1"),
Media(id: 2, name: "Name 2"),
Media(id: 3, name: "Name 3")])
window.rootViewController = UIHostingController(rootView: ContentView(library: library))
self.window = window
window.makeKeyAndVisible()
}
以及实施:
struct Media {
let id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
class MediaLibrary: BindableObject {
typealias PublisherType = PassthroughSubject<Void, Never>
var didChange = PublisherType()
var store: [Media] {
didSet {
didChange.send()
}
}
init(store: [Media]) {
self.store = store
}
}
struct ContentView : View {
@ObjectBinding var library: MediaLibrary
var body: some View {
VStack {
List(self.library.store.identified(by: \.id)) { media in
Text(media.name)
}
HStack {
Button(action: {
self.library.store[0].name = "Name \(Int.random(in: 100...199))"
}) {
Text("Change")
}
Button(action: {
self.library.store.append(Media(id: 4, name: "Name 4"))
}) {
Text("Add")
}
}
}
}
}