【问题标题】:SwiftUI Why does context.coordinator.value.wrappedValue return only initial value?SwiftUI 为什么 context.coordinator.value.wrappedValue 只返回初始值?
【发布时间】:2020-10-21 16:38:28
【问题描述】:

我正在尝试在我的 SwiftUI 应用中实现一些 UIKit 组件。我已成功将 UISlider 添加到我的 SwiftUI 视图中。但有一件事困扰着我。当我移动滑块时,值更新正常,但我很好奇为什么 context.coordinator.value.wrappedValue 保持不变?这是我的实现:

import SwiftUI

struct UISliderView: UIViewRepresentable {
typealias UIViewType = UISlider

@Binding var value: Double
var minValue: Double
var maxValue: Double

func makeUIView(context: Context) -> UISlider {
    let slider = UISlider(frame: .zero)
    slider.maximumValue = Float(minValue)
    slider.maximumValue = Float(maxValue)
    slider.value = Float(value)
    slider.addTarget(context.coordinator,
                     action: #selector(Coordinator.valueChanged(_:)),
                     for: .valueChanged)
    return slider
}

func updateUIView(_ uiView: UISlider, context: Context) {
    print("Value: \(value)")
    print("Wrapped value: \(context.coordinator.value.wrappedValue)")
    uiView.value = Float(value)
}

func makeCoordinator() -> Coordinator {
    let coordinator = Coordinator(value: $value)
    coordinator.id = 5
    return coordinator
}

class Coordinator: NSObject {
    var value: Binding<Double>
    var id: Int = 0
    
    init(value: Binding<Double>) {
        self.value = value
    }
    
    @objc func valueChanged(_ sender: UISlider) {
        value.wrappedValue = Double(sender.value)
    }
}
}

现在这是困扰我的部分:

print("Value: \(value)")
print("Wrapped value: \(context.coordinator.value.wrappedValue)")

这些值应该相同,或者至少我希望它们相同,但实际上它们不是?

【问题讨论】:

  • 这只是更新顺序的问题...不要在那里检查。只需使用 - 它可以正常工作。使用 Xcode 12 / iOS 14 测试。
  • 如果这是更新顺序的情况,那么 context.coordinator.value.wrappedValue 将具有先前的值...在这种情况下,如果您调用 UISlider init 并使用 30 作为值,context.coordinator.value。 WrappedValue 将始终打印 30。此外,应首先在 Coordinator 绑定上调用移动滑块。
  • 正如 Asperi 所说,这是一个时间问题。 context.coordinator 尚未更新。如果您将coordinator 设为实例变量并进行比较,您会发现它 更新。只是 Context 是一个结构体,还没有更新。你不应该关心它。
  • updateUIView 中的代码在您的示例中也无关紧要,因为视图 ​​已经 具有正确的值。 updateUIView 代码用于当您从外部更改绑定时,即,如果您有一个按钮可以从 ContentView 等更改绑定。

标签: ios swift swiftui


【解决方案1】:

应归功于@Asperi 和@Claus Jørgensen

如果你异步打印context.coordinator的包装值,如下图,它的值是正确的。

我不知道为什么会这样,我也很好奇。理论上,由于 context.coordinator.value 应该是 UISliderView 和 Coordinator 的相同绑定。因此它们应该在同一时间更新。

import SwiftUI

struct ContentView: View {
    @State var value: Double = 0
    
    var body: some View {
        UISliderView(value: $value, minValue: 0, maxValue: 10)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct UISliderView: UIViewRepresentable {
    typealias UIViewType = UISlider

    @Binding var value: Double
    var minValue: Double
    var maxValue: Double

    init(value: Binding<Double>, minValue: Double, maxValue: Double) {
        print("UISliderView", #function)

        self._value = value
        self.minValue = minValue
        self.maxValue = maxValue
    }

    func makeUIView(context: Context) -> UISlider {
        print("UISliderView", #function)

        let slider = UISlider(frame: .zero)
        slider.maximumValue = Float(minValue)
        slider.maximumValue = Float(maxValue)
//        slider.value = Float(value)
        slider.addTarget(context.coordinator,
                         action: #selector(Coordinator.valueChanged(_:)),
                         for: .valueChanged)
        return slider
    }

    func updateUIView(_ uiView: UISlider, context: Context) {
        print("UISliderView", #function, "coordinator", ObjectIdentifier(context.coordinator))

        print("UISliderView", "Value: \(value)")
        DispatchQueue.main.async {
            print("UISliderView", "Wrapped value: \(context.coordinator.value.wrappedValue)")
        }
        uiView.value = Float(value)
    }

    func makeCoordinator() -> Coordinator {
        print("UISliderView", #function)

        let coordinator = Coordinator(value: $value)
        coordinator.id = 5
        return coordinator
    }

    class Coordinator: NSObject {
        var value: Binding<Double>
        var id: Int = 0

        init(value: Binding<Double>) {
            self.value = value
            super.init()
            print("Coordinator", #function, ObjectIdentifier(self))
        }

        @objc func valueChanged(_ sender: UISlider) {
            print("Coordinator", #function, "newValue:", value.wrappedValue)
            value.wrappedValue = Double(sender.value)
        }
    }
}
UISliderView init(value:minValue:maxValue:)
UISliderView makeCoordinator()
Coordinator init(value:) ObjectIdentifier(0x00006000018b4d50)
UISliderView makeUIView(context:)
UISliderView updateUIView(_:context:) coordinator ObjectIdentifier(0x00006000018b4d50)
UISliderView Value: 0.0
UISliderView Wrapped value: 0.0
UISliderView updateUIView(_:context:) coordinator ObjectIdentifier(0x00006000018b4d50)
UISliderView Value: 0.0
UISliderView Wrapped value: 0.0
Coordinator valueChanged(_:) newValue: 0.0
Coordinator valueChanged(_:) newValue: 0.0
Coordinator valueChanged(_:) newValue: 0.0
UISliderView updateUIView(_:context:) coordinator ObjectIdentifier(0x00006000018b4d50)
UISliderView Value: 0.03875968977808952
UISliderView Wrapped value: 0.03875968977808952
Coordinator valueChanged(_:) newValue: 0.03875968977808952
UISliderView updateUIView(_:context:) coordinator ObjectIdentifier(0x00006000018b4d50)
UISliderView Value: 0.1550387591123581
UISliderView Wrapped value: 0.1550387591123581
Coordinator valueChanged(_:) newValue: 0.1550387591123581
UISliderView updateUIView(_:context:) coordinator ObjectIdentifier(0x00006000018b4d50)
UISliderView Value: 0.23255814611911774
UISliderView Wrapped value: 0.23255814611911774
Coordinator valueChanged(_:) newValue: 0.23255814611911774

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-30
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多