Swiftui 如何从Swift类更改@State变量

Swiftui 如何从Swift类更改@State变量,swiftui,Swiftui,我有SwiftUI ContentView结构,从中调用标准Swift类中的函数。此函数可能会抛出一个错误,我希望通过ContentView中的Alert显示该错误。警报的外观由ContentView中声明的Bool@State var控制。 我试图在函数中使用@Binding属性包装器,但显然不正确。我应该使用ObservieObject还是最好的方法? 谢谢 带有警报的ContentView片段 HStack { Button("Load data...", ac

我有SwiftUI ContentView结构,从中调用标准Swift类中的函数。此函数可能会抛出一个错误,我希望通过ContentView中的Alert显示该错误。警报的外观由ContentView中声明的Bool@State var控制。 我试图在函数中使用@Binding属性包装器,但显然不正确。我应该使用ObservieObject还是最好的方法? 谢谢

带有警报的ContentView片段

HStack {
    Button("Load data...", action: {
        let panel = NSOpenPanel()
        panel.title = "Select CSV formatted data file"
        panel.canChooseFiles = true
        panel.allowedFileTypes = ["csv"]
        panel.allowsMultipleSelection = false
        panel.begin(completionHandler: {result in
            if result == .OK {
                getDataset(fromFileURL: panel.url!, withHeaderLine: headerLine)
            }
        })
    })
    .padding()
    .alert(isPresented: $isError, content: {
        Alert(title: Text("Error"), message: Text(errorText), dismissButton: .default(Text("OK")))
    })
    Toggle("With header line", isOn: $headerLine)
    }.toggleStyle(SwitchToggleStyle())
}
可能引发错误的被调用函数的片段

do {
            var fromRow = 0
            let fileContent = try String(contentsOf: fromFileURL)
            let rows = fileContent.components(separatedBy: "\n")
            if withHeaderLine { fromRow = 1 }
            for i in fromRow...rows.count - 1 {
                let columns = rows[i].components(separatedBy: ",")
                guard let xValue = Double(columns[0]) else {
                    throw myError.conversionFailed
                }
                guard let yValue = Double(columns[1]) else {
                    throw myError.conversionFailed
                }
                myDataset.append(Dataset(x: xValue, y: yValue))
            }
        } catch myError.conversionFailed {
            errorText = "Value conversion to Double failed."
            isError.toggle()
        } catch let error {
            errorText = error.localizedDescription
            isError.toggle()
        }
}

我建议为该视图创建一个ViewModel。在该ViewModel中,您为errorText和iError创建了两个PublishedValue。然后,可以在ViewModel中使用该函数并更新发布的值。ViewModel将如下所示,然后相应地更新其他视图

class ContentViewModel : ObservableObject {
    @Published var isError : Bool = false
    @Published var errorText : String = ""
    
    func getDataset() {
        //Here you call your function and return the result or call it directly inside here
        errorText = "Value conversion to Double failed." //<< here you can change published values
        isError.toggle()
    }
}

如果仍然将函数外包到另一个视图中,只需从该函数返回错误字符串或使用闭包即可。

我建议为该视图创建一个ViewModel。在该ViewModel中,您为errorText和iError创建了两个PublishedValue。然后,可以在ViewModel中使用该函数并更新发布的值。ViewModel将如下所示,然后相应地更新其他视图

class ContentViewModel : ObservableObject {
    @Published var isError : Bool = false
    @Published var errorText : String = ""
    
    func getDataset() {
        //Here you call your function and return the result or call it directly inside here
        errorText = "Value conversion to Double failed." //<< here you can change published values
        isError.toggle()
    }
}

如果您仍然将函数外包到另一个视图中,只需从该函数返回错误字符串或使用闭包即可。

下面是一个可能方法的演示(如果有必要,可以模拟异步调用)

使用Xcode 12.1/iOS 14.1进行测试

class DemoClass {
    func simulate(isError: Binding<Bool>) {
        DispatchQueue.global(qos: .background).async {
            sleep(1)
            DispatchQueue.main.async {
                isError.wrappedValue.toggle()
            }
        }
    }
}

struct ContentView: View {
    let demo = DemoClass()

    @State private var isError = false

    var body: some View {
        VStack {
            Button("Demo") { demo.simulate(isError: $isError) }
        }
        .alert(isPresented: $isError, content: {
            Alert(title: Text("Error"), message: Text("errorText"), dismissButton: .default(Text("OK")))
        })
    }
}
class演示类{
func模拟(iError:Binding){
DispatchQueue.global(qos:.background).async{
睡眠(1)
DispatchQueue.main.async{
isError.wrappedValue.toggle()文件
}
}
}
}
结构ContentView:View{
让demo=DemoClass()
@国家私有变量isError=false
var body:一些观点{
VStack{
按钮(“Demo”){Demo.simulate(isError:$isError)}
}
.alert(显示:$isError,内容:{
警报(标题:文本(“错误”)、消息:文本(“错误文本”)、解除按钮:。默认值(文本(“确定”))
})
}
}

这里是一个可能的方法的演示(如果有必要,可以模拟异步调用)

使用Xcode 12.1/iOS 14.1进行测试

class DemoClass {
    func simulate(isError: Binding<Bool>) {
        DispatchQueue.global(qos: .background).async {
            sleep(1)
            DispatchQueue.main.async {
                isError.wrappedValue.toggle()
            }
        }
    }
}

struct ContentView: View {
    let demo = DemoClass()

    @State private var isError = false

    var body: some View {
        VStack {
            Button("Demo") { demo.simulate(isError: $isError) }
        }
        .alert(isPresented: $isError, content: {
            Alert(title: Text("Error"), message: Text("errorText"), dismissButton: .default(Text("OK")))
        })
    }
}
class演示类{
func模拟(iError:Binding){
DispatchQueue.global(qos:.background).async{
睡眠(1)
DispatchQueue.main.async{
isError.wrappedValue.toggle()文件
}
}
}
}
结构ContentView:View{
让demo=DemoClass()
@国家私有变量isError=false
var body:一些观点{
VStack{
按钮(“Demo”){Demo.simulate(isError:$isError)}
}
.alert(显示:$isError,内容:{
警报(标题:文本(“错误”)、消息:文本(“错误文本”)、解除按钮:。默认值(文本(“确定”))
})
}
}

你会提供你的代码吗?我添加了合理的代码片段。你会提供你的代码吗?我添加了合理的代码片段。