SwiftUI 视图不更新
SwiftUI View doesn't update
当我更改 ObservableObject
Class 中数组中的 属性 时,我的视图没有更新。我什至声明了一个 objectWillChange
属性 并手动调用它,但视图只是随机更新或不是我想要的。我不明白这个。
import Foundation
import SocketIO
import Combine
class SocketService: ObservableObject {
static let instance = SocketService()
let manager = SocketManager(socketURL: URL(RequestURL.base_url)!)
let socket: SocketIOClient
// let objectWillChange = ObservableObjectPublisher()
@Published var allMessages: [Message] = []
// {
// willSet {
// self.objectWillChange.send()
// }
// }
@Published var writtenUsers: [PreviewMessage] = []
// {
// willSet {
// self.objectWillChange.send()
// }
// }
init() {
socket = manager.defaultSocket
setSocketEvents()
}
// This method is called
func recieve_read_all_messages() {
socket.on("recieve-read-all-messages") { (data, ack) in
guard let arr = data as? [[String: Any]] else { return }
guard let userID = arr[0]["userID"] as? Int else {
print("no userID, \(#function), line: \(#line)"); return
}
// self.objectWillChange.send()
for msg in self.allMessages {
// Here im trying to change the property in the array
msg.content.readStatus = .read
}
}
}
即使我直接更改此 属性 视图也不会更新
@EnvironmentObject private var socketService: SocketService
var body: some View {
VStack {
List(filteredMessages, id: \.content.uuid, rowContent: chatSpeechBubbleView)
sendView
}
}
private func chatSpeechBubbleView(forMessage message: Message) -> some View {
ChatSpeechBubble(message: message)
}
private var sendView: some View {
Button(action: sendMessage) {
SFSymbol(.paperplane_fill)
.fontSize(20)
.foregroundColor(.white)
.rotate(.degrees(45))
.padding(10)
.padding(.trailing, 5)
.backgroundColor(.blue)
.clipCircle()
}
.padding(.bottom, 2)
}
func sendMessage() {
for msg in socketService.allMessages {
msg.content.readStatus = .read
}
}
我应该更新的其他观点:
struct DoubleCheckmark: View {
var messageContent: MessageContent
var body: some View {
HStack(spacing: 0) {
SFSymbol(.checkmark)
.resizable()
.scaledToFit()
SFSymbol(.checkmark)
.resizable()
.scaledToFit()
.padding(.leading, -9)
}
.height(13)
.foregroundColor(self.messageContent.readStatus == .read ? .blue : .gray)
}
}
struct ChatSpeechBubble: View {
// MARK: - init variables
var message: Message
// MARK: - normal variables
var ownSendMessage: Bool {
message.fromUser.id == UDService.shared.user.id
}
// MARK: - Body
var body: some View {
messageContent
}
private var messageContent: some View {
HStack(alignment: .bottom) {
if message.content.text != nil {
Text(message.content.text!)
.foregroundColor(.black)
}
if message.content.imageURL != nil {
Spacer(minLength: 0)
}
Text(message.content.timeHourMinute)
.font(.caption)
.foregroundColor(.gray)
if ownSendMessage {
DoubleCheckmark(messageContent: self.message.content)
}
}
}
}
使用struct
,而不是class
问题是 Message
及其子类型应该声明为 struct
,而不是 class
。这就是为什么。
这是我用来证明差异的基本示例:
代码的主要部分
class SomeObject: ObservableObject {
@Published var users = [User(username: "George", password: "password")]
}
struct ContentView: View {
@StateObject var object = SomeObject()
var body: some View {
Text("Hello world!")
let _ = print("Body")
}
}
let content = ContentView()
print(content.object)
content.object.users[0].password = "password123"
User
作为 struct
struct User {
var username: String
var password: String
}
User
作为 class
class User {
var username: String
var password: String
init(username: String, password: String) {
self.username = username
self.password = password
}
}
struct
class
查看底部的打印日志。使用 struct
,“正文”再次打印,但为什么呢?
这是因为classes是按引用传递的,而结构是按值传递的。这意味着您可以改变 class 实例的属性,如下所示:
import SpriteKit
let sprite = SKSpriteNode()
sprite.position = CGPoint(x: 50, y: 50)
sprite.color = .red
如果 SKSpriteNode
本来是 struct
,具有 let
属性,您将在尝试更改 let
常量时收到错误消息:
Cannot assign to property: '*' is a 'let' constant
结论
因为 Message
是一个 class
,所以 实例 没有改变。但是随着 struct
,整个事情 正在 改变。您需要更改整个内容(通过使用 struct
),以便 @Published
& StateObject
/ObservableObject
可以观察到更改。
当我更改 ObservableObject
Class 中数组中的 属性 时,我的视图没有更新。我什至声明了一个 objectWillChange
属性 并手动调用它,但视图只是随机更新或不是我想要的。我不明白这个。
import Foundation
import SocketIO
import Combine
class SocketService: ObservableObject {
static let instance = SocketService()
let manager = SocketManager(socketURL: URL(RequestURL.base_url)!)
let socket: SocketIOClient
// let objectWillChange = ObservableObjectPublisher()
@Published var allMessages: [Message] = []
// {
// willSet {
// self.objectWillChange.send()
// }
// }
@Published var writtenUsers: [PreviewMessage] = []
// {
// willSet {
// self.objectWillChange.send()
// }
// }
init() {
socket = manager.defaultSocket
setSocketEvents()
}
// This method is called
func recieve_read_all_messages() {
socket.on("recieve-read-all-messages") { (data, ack) in
guard let arr = data as? [[String: Any]] else { return }
guard let userID = arr[0]["userID"] as? Int else {
print("no userID, \(#function), line: \(#line)"); return
}
// self.objectWillChange.send()
for msg in self.allMessages {
// Here im trying to change the property in the array
msg.content.readStatus = .read
}
}
}
即使我直接更改此 属性 视图也不会更新
@EnvironmentObject private var socketService: SocketService
var body: some View {
VStack {
List(filteredMessages, id: \.content.uuid, rowContent: chatSpeechBubbleView)
sendView
}
}
private func chatSpeechBubbleView(forMessage message: Message) -> some View {
ChatSpeechBubble(message: message)
}
private var sendView: some View {
Button(action: sendMessage) {
SFSymbol(.paperplane_fill)
.fontSize(20)
.foregroundColor(.white)
.rotate(.degrees(45))
.padding(10)
.padding(.trailing, 5)
.backgroundColor(.blue)
.clipCircle()
}
.padding(.bottom, 2)
}
func sendMessage() {
for msg in socketService.allMessages {
msg.content.readStatus = .read
}
}
我应该更新的其他观点:
struct DoubleCheckmark: View {
var messageContent: MessageContent
var body: some View {
HStack(spacing: 0) {
SFSymbol(.checkmark)
.resizable()
.scaledToFit()
SFSymbol(.checkmark)
.resizable()
.scaledToFit()
.padding(.leading, -9)
}
.height(13)
.foregroundColor(self.messageContent.readStatus == .read ? .blue : .gray)
}
}
struct ChatSpeechBubble: View {
// MARK: - init variables
var message: Message
// MARK: - normal variables
var ownSendMessage: Bool {
message.fromUser.id == UDService.shared.user.id
}
// MARK: - Body
var body: some View {
messageContent
}
private var messageContent: some View {
HStack(alignment: .bottom) {
if message.content.text != nil {
Text(message.content.text!)
.foregroundColor(.black)
}
if message.content.imageURL != nil {
Spacer(minLength: 0)
}
Text(message.content.timeHourMinute)
.font(.caption)
.foregroundColor(.gray)
if ownSendMessage {
DoubleCheckmark(messageContent: self.message.content)
}
}
}
}
使用struct
,而不是class
问题是 Message
及其子类型应该声明为 struct
,而不是 class
。这就是为什么。
这是我用来证明差异的基本示例:
代码的主要部分
class SomeObject: ObservableObject {
@Published var users = [User(username: "George", password: "password")]
}
struct ContentView: View {
@StateObject var object = SomeObject()
var body: some View {
Text("Hello world!")
let _ = print("Body")
}
}
let content = ContentView()
print(content.object)
content.object.users[0].password = "password123"
User
作为 struct
struct User {
var username: String
var password: String
}
User
作为 class
class User {
var username: String
var password: String
init(username: String, password: String) {
self.username = username
self.password = password
}
}
struct |
class |
---|---|
查看底部的打印日志。使用 struct
,“正文”再次打印,但为什么呢?
这是因为classes是按引用传递的,而结构是按值传递的。这意味着您可以改变 class 实例的属性,如下所示:
import SpriteKit
let sprite = SKSpriteNode()
sprite.position = CGPoint(x: 50, y: 50)
sprite.color = .red
如果 SKSpriteNode
本来是 struct
,具有 let
属性,您将在尝试更改 let
常量时收到错误消息:
Cannot assign to property: '*' is a 'let' constant
结论
因为 Message
是一个 class
,所以 实例 没有改变。但是随着 struct
,整个事情 正在 改变。您需要更改整个内容(通过使用 struct
),以便 @Published
& StateObject
/ObservableObject
可以观察到更改。