如何正确使用 SwiftUI 已发布的可选属性

Chr*_*s G 4 swiftui swift-optionals observedobject

为了提供一些背景信息,我正在编写我们应用程序的订单跟踪部分,该部分会经常从服务器重新加载订单状态。屏幕上的 UI 是用 SwiftUI 开发的。我需要屏幕上有一个可选图像,该图像随着订单的进展而变化。

当我尝试以下操作时,一切正常......

我的 viewModel 是一个 ObservableObject: internal class MyAccountOrderViewModel: ObservableObject {

这有一个已发布的属性: @Published internal var graphicURL: URL = Bundle.main.url(forResource: "tracking_STAGEONE", withExtension: "gif")!

在 SwiftUI 中使用该属性如下: GIFViewer(imageURL: $viewModel.graphicURL)


我的问题是该graphicURL属性的占位符值可能不正确,而我的要求是它是可选的。将已发布的属性更改为:@Published internal var graphicURL: URL?会导致我的 GIFViewer 出现问题,它不接受可选 URL:

Cannot convert value of type 'Binding<URL?>' to expected argument type 'Binding<URL>'

尝试明显展开 会graphicURL产生此错误:

Cannot force unwrap value of non-optional type 'Binding<URL?>'


使这项工作有效的正确方法是什么?我不想在属性中添加一个值,并检查该属性是否等于占位符值(即将其视为 nil),或者假设该属性始终为非 nil 并以某种方式不安全地强制解开它。

Geo*_*e_E 7

Binding下面是您可以用来将类型转换Binding<Int?>为的扩展Binding<Int>?。在您的情况下,它将URL代替Int,但此扩展是通用的,因此可以与任何Binding:

extension Binding {
    func optionalBinding<T>() -> Binding<T>? where T? == Value {
        if let wrappedValue = wrappedValue {
            return Binding<T>(
                get: { wrappedValue },
                set: { self.wrappedValue = $0 }
            )
        } else {
            return nil
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以示例视图:

struct ContentView: View {
    @StateObject private var model = MyModel()

    var body: some View {
        VStack(spacing: 30) {
            Button("Toggle if nil") {
                if model.counter == nil {
                    model.counter = 0
                } else {
                    model.counter = nil
                }
            }

            if let binding = $model.counter.optionalBinding() {
                Stepper(String(binding.wrappedValue), value: binding)
            } else {
                Text("Counter is nil")
            }
        }
    }
}

class MyModel: ObservableObject {
    @Published var counter: Int?
}
Run Code Online (Sandbox Code Playgroud)

结果:

结果