ObservableObject 文本即使使用 objectWillChange 也不会更新 - 多个 类
ObservableObject text not updating even with objectWillChange - multiple classes
我目前正在编写一个实用程序,作为其中的一部分,接收到一个字符串(通过 WebSockets 和 Starscream 库),然后将字符串值显示在 SwiftUI 视图(名为 ReadingsView)中。
代码结构如下 - 有两个 classes,管理 WebSocket 连接的 WSManager class 和具有 ObservableObject [的 GetReadings class =36=],它管理和存储读数。
当使用 WSManager class 中的 didReceive 方法接收字符串时,WSManager class 中的 decodeText 方法对其进行解码,然后调用 GetReadings [=] 中的 parseReceivedStrings 方法31=].
class WSManager : WebSocketDelegate {
func didReceive(event: WebSocketEvent, client: WebSocket) {
case .text(let string):
// Decode the text
DispatchQueue.main.async {
self.decodeText(recvText: string)
print("Received text: \(string)")
}
recvString = string
}
func decodeText(recvText: String) {
// If the message is allowed, then pass it to getReadings
print("Decoding")
if recvText.hasPrefix("A=") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .allreadings)
print("All readings received")
} else if recvText.hasPrefix("T = ") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .temperature)
} else if recvText.hasPrefix("P = ") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .pressure)
} else if recvText.hasPrefix("H = ") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .humidity)
} else {
print("Unrecognised string.")
}
}
}
enum ReadingType {
case allreadings
case temperature
case pressure
case humidity
}
class GetReadings: ObservableObject {
let objectWillChange = ObservableObjectPublisher()
@Published var temp: Float = 0.0 {
willSet {
print("Temp new = " + String(temp))
objectWillChange.send()
}
}
@Published var pressure: Float = 0.0 {
willSet {
print("Pressure new = " + String(pressure))
objectWillChange.send()
}
}
@Published var humidity: Float = 0.0 {
willSet {
print("Humidity new = " + String(humidity))
objectWillChange.send()
}
}
func getAll() {
//print(readings.count)
//print(readings.count)
wsManager.socket.write(string: "get_all")
}
func parseReceivedStrings (recvText: String, readingType: ReadingType) {
if readingType == .allreadings {
// Drop first two characters
let tempText = recvText.dropFirst(2)
// Split the string into components
let recvTextArray = tempText.components(separatedBy: ",")
// Deal with the temperature
temp = (recvTextArray[0] as NSString).floatValue
// Pressure
pressure = (recvTextArray[1] as NSString).floatValue
// Humidity
humidity = (recvTextArray[2] as NSString).floatValue
}
}
}
解析值时,我希望 ReadingsView 中的值立即更新,因为我已将变量标记为 @Published,并使用 objectWillChange 属性 手动推送更改。 willSet 参数中的打印语句反映新值,但文本不会更新。在 ReadingsView 代码中,我通过在按下刷新按钮时手动调用 parseReceivedString 方法对此进行了补偿(这被用作发送请求的 WebSocket 协议的一部分),但这会导致读数落后于它们所在的位置应该。理想情况下,我希望读数在上一段中描述的方法中解析后立即更新。
struct ReadingsView: View {
@ObservedObject var getReadings: GetReadings
var body: some View {
VStack {
Text(String(self.getReadings.temp))
Text(String(self.getReadings.pressure))
Text(String(self.getReadings.humidity))
Button(action: {
print("Button clicked")
self.getReadings.getAll()
self.getReadings.parseReceivedStrings(recvText: wsManager.recvString, readingType: .allreadings)
}) {
Image(systemName: "arrow.clockwise.circle.fill")
.font(.system(size: 30))
}
.padding()
}
}
}
我想知道我是否使用了正确的声明,或者我正在尝试做的事情是否与使用多个 classes 不兼容 - 这是我第一次使用 SwiftUI,所以我可能会遗漏一些细微差别.提前感谢您的帮助。
已编辑 - 添加代码
ContentView
struct ContentView: View {
@State private var selection = 0
var body: some View {
TabView(selection: $selection){
ReadingsView(getReadings: GetReadings())
.tabItem {
VStack {
Image(systemName: "thermometer")
Text("Readings")
}
} .tag(0)
SetupView()
.tabItem {
VStack {
Image(systemName: "slider.horizontal.3")
Text("Setup")
}
}
.tag(1)
}
}
}
如果您正在使用 ObservableObject
,则无需在 Published
属性的 willSet
中写入 objectWillChange.send()
。
这意味着您也可以删除:
let objectWillChange = ObservableObjectPublisher()
ObservableObject
类.
默认提供
还要确保如果您要更新 @Published
属性,请在 main 队列 (DispatchQueue.main
) 中进行。异步请求通常在后台队列中执行,您可能会尝试在后台更新您的属性,但这是行不通的。
您不需要将所有代码包装在 DispatchQueue.main
中 - 只需更新 @Published
属性:
的部分
DispatchQueue.main.async {
self.humidity = ...
}
并确保您只创建 一个 GetReadings
实例并在您的视图中共享它。为此,您可以使用 @EnvironmentObject
.
在 SceneDelegate
中创建 ContentView
:
// create GetReadings only once here
let getReadings = GetReadings()
// pass it to WSManager
// ...
// pass it to your views
let contentView = ContentView().environmentObject(getReadings)
然后在您的 ReadingsView
中您可以这样访问它:
@EnvironmentObject var getReadings: GetReadings
请注意,您不再需要在 TabView 中创建它:
TabView(selection: $selection) {
ReadingsView()
...
}
我目前正在编写一个实用程序,作为其中的一部分,接收到一个字符串(通过 WebSockets 和 Starscream 库),然后将字符串值显示在 SwiftUI 视图(名为 ReadingsView)中。
代码结构如下 - 有两个 classes,管理 WebSocket 连接的 WSManager class 和具有 ObservableObject [的 GetReadings class =36=],它管理和存储读数。
当使用 WSManager class 中的 didReceive 方法接收字符串时,WSManager class 中的 decodeText 方法对其进行解码,然后调用 GetReadings [=] 中的 parseReceivedStrings 方法31=].
class WSManager : WebSocketDelegate {
func didReceive(event: WebSocketEvent, client: WebSocket) {
case .text(let string):
// Decode the text
DispatchQueue.main.async {
self.decodeText(recvText: string)
print("Received text: \(string)")
}
recvString = string
}
func decodeText(recvText: String) {
// If the message is allowed, then pass it to getReadings
print("Decoding")
if recvText.hasPrefix("A=") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .allreadings)
print("All readings received")
} else if recvText.hasPrefix("T = ") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .temperature)
} else if recvText.hasPrefix("P = ") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .pressure)
} else if recvText.hasPrefix("H = ") {
getReadings.parseReceivedStrings(recvText: recvText, readingType: .humidity)
} else {
print("Unrecognised string.")
}
}
}
enum ReadingType {
case allreadings
case temperature
case pressure
case humidity
}
class GetReadings: ObservableObject {
let objectWillChange = ObservableObjectPublisher()
@Published var temp: Float = 0.0 {
willSet {
print("Temp new = " + String(temp))
objectWillChange.send()
}
}
@Published var pressure: Float = 0.0 {
willSet {
print("Pressure new = " + String(pressure))
objectWillChange.send()
}
}
@Published var humidity: Float = 0.0 {
willSet {
print("Humidity new = " + String(humidity))
objectWillChange.send()
}
}
func getAll() {
//print(readings.count)
//print(readings.count)
wsManager.socket.write(string: "get_all")
}
func parseReceivedStrings (recvText: String, readingType: ReadingType) {
if readingType == .allreadings {
// Drop first two characters
let tempText = recvText.dropFirst(2)
// Split the string into components
let recvTextArray = tempText.components(separatedBy: ",")
// Deal with the temperature
temp = (recvTextArray[0] as NSString).floatValue
// Pressure
pressure = (recvTextArray[1] as NSString).floatValue
// Humidity
humidity = (recvTextArray[2] as NSString).floatValue
}
}
}
解析值时,我希望 ReadingsView 中的值立即更新,因为我已将变量标记为 @Published,并使用 objectWillChange 属性 手动推送更改。 willSet 参数中的打印语句反映新值,但文本不会更新。在 ReadingsView 代码中,我通过在按下刷新按钮时手动调用 parseReceivedString 方法对此进行了补偿(这被用作发送请求的 WebSocket 协议的一部分),但这会导致读数落后于它们所在的位置应该。理想情况下,我希望读数在上一段中描述的方法中解析后立即更新。
struct ReadingsView: View {
@ObservedObject var getReadings: GetReadings
var body: some View {
VStack {
Text(String(self.getReadings.temp))
Text(String(self.getReadings.pressure))
Text(String(self.getReadings.humidity))
Button(action: {
print("Button clicked")
self.getReadings.getAll()
self.getReadings.parseReceivedStrings(recvText: wsManager.recvString, readingType: .allreadings)
}) {
Image(systemName: "arrow.clockwise.circle.fill")
.font(.system(size: 30))
}
.padding()
}
}
}
我想知道我是否使用了正确的声明,或者我正在尝试做的事情是否与使用多个 classes 不兼容 - 这是我第一次使用 SwiftUI,所以我可能会遗漏一些细微差别.提前感谢您的帮助。
已编辑 - 添加代码
ContentView
struct ContentView: View {
@State private var selection = 0
var body: some View {
TabView(selection: $selection){
ReadingsView(getReadings: GetReadings())
.tabItem {
VStack {
Image(systemName: "thermometer")
Text("Readings")
}
} .tag(0)
SetupView()
.tabItem {
VStack {
Image(systemName: "slider.horizontal.3")
Text("Setup")
}
}
.tag(1)
}
}
}
如果您正在使用 ObservableObject
,则无需在 Published
属性的 willSet
中写入 objectWillChange.send()
。
这意味着您也可以删除:
let objectWillChange = ObservableObjectPublisher()
ObservableObject
类.
还要确保如果您要更新 @Published
属性,请在 main 队列 (DispatchQueue.main
) 中进行。异步请求通常在后台队列中执行,您可能会尝试在后台更新您的属性,但这是行不通的。
您不需要将所有代码包装在 DispatchQueue.main
中 - 只需更新 @Published
属性:
DispatchQueue.main.async {
self.humidity = ...
}
并确保您只创建 一个 GetReadings
实例并在您的视图中共享它。为此,您可以使用 @EnvironmentObject
.
在 SceneDelegate
中创建 ContentView
:
// create GetReadings only once here
let getReadings = GetReadings()
// pass it to WSManager
// ...
// pass it to your views
let contentView = ContentView().environmentObject(getReadings)
然后在您的 ReadingsView
中您可以这样访问它:
@EnvironmentObject var getReadings: GetReadings
请注意,您不再需要在 TabView 中创建它:
TabView(selection: $selection) {
ReadingsView()
...
}