1

我目前正在编写一个实用程序,作为其中的一部分,接收一个字符串(通过 WebSockets 和 Starscream 库),然后将字符串值显示在 SwiftUI 视图(名为 ReadingsView)中。

代码的结构如下 - 有两个类,管理 WebSocket 连接的 WSManager 类和具有 ObservableObject 属性的 GetReadings 类,它管理和存储读数。

当使用 WSManager 类中的 didReceive 方法接收到字符串时,它被 WSManager 类中的 decodeText 方法解码,然后调用 GetReadings 类中的 parseReceivedStrings 方法。

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()
        }
    }
}

我想知道我是否使用了正确的声明,或者我尝试做的是否与使用多个类不兼容——这是我第一次使用 SwiftUI,所以我可能会遗漏一些细微差别。提前谢谢你的帮助。

编辑 - 添加代码

内容视图

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)
        }
    }
}
4

1 回答 1

0

如果您正在使用ObservableObject,则无需写入您的objectWillChange.send()属性。willSetPublished

这意味着您也可以删除:

let objectWillChange = ObservableObjectPublisher()

默认情况下在ObservableObject类中提供。

还要确保如果要更新@Published属性,请在队列 ( 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()
    ...
}
于 2020-06-29T19:42:49.207 回答