为了自定义 a UISlider
,我在 a 中使用它UIViewRepresentable
。它公开了 a@Binding var value: Double
以便我的视图模型 ( ObservableObject
) 视图可以观察更改并View
相应地更新 a 。
问题是当@Binding
值改变时视图没有更新。在以下示例中,我有两个滑块。一土Slider
一风俗SwiftUISlider
。
两者都将绑定值传递给应该更新视图的视图模型。本机Slider
确实会更新视图,但不会更新自定义视图。在日志中,我可以看到$sliderValue.sink { ...
已正确调用,但视图未更新。
我注意到当呈现视图具有该@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
属性时会发生这种情况。如果我将其注释掉,它会按预期工作。
重现此的完整示例代码是
import SwiftUI
import Combine
struct ContentView: View {
@State var isPresentingModal = false
// comment this out
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
VStack {
Button("Show modal") {
isPresentingModal = true
}
.padding()
}
.sheet(isPresented: $isPresentingModal) {
MyModalView(viewModel: TempViewModel())
}
}
}
class TempViewModel: ObservableObject {
@Published var sliderText = ""
@Published var sliderValue: Double = 0
private var cancellable = Set<AnyCancellable>()
init() {
$sliderValue
.print("view model")
.sink { [weak self] value in
guard let self = self else { return }
print("updating view \(value)")
self.sliderText = "\(value) C = \(String(format: "%.2f" ,value * 9 / 5 + 32)) F"
}
.store(in: &cancellable)
}
}
struct MyModalView: View {
@ObservedObject var viewModel: TempViewModel
var body: some View {
VStack(alignment: .leading) {
Text("SwiftUI Slider")
Slider(value: $viewModel.sliderValue, in: -100...100, step: 0.5)
.padding(.bottom)
Text("UIViewRepresentable Slider")
SwiftUISlider(minValue: -100, maxValue: 100, value: $viewModel.sliderValue)
Text(viewModel.sliderText)
}
.padding()
}
}
struct SwiftUISlider: UIViewRepresentable {
final class Coordinator: NSObject {
var value: Binding<Double>
init(value: Binding<Double>) {
self.value = value
}
@objc func valueChanged(_ sender: UISlider) {
let index = Int(sender.value + 0.5)
sender.value = Float(index)
print("value changed \(sender.value)")
self.value.wrappedValue = Double(sender.value)
}
}
var minValue: Int = 0
var maxValue: Int = 0
@Binding var value: Double
func makeUIView(context: Context) -> UISlider {
let slider = UISlider(frame: .zero)
slider.minimumTrackTintColor = .systemRed
slider.maximumTrackTintColor = .systemRed
slider.maximumValue = Float(maxValue)
slider.minimumValue = Float(minValue)
slider.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged(_:)),
for: .valueChanged
)
adapt(slider, context: context)
return slider
}
func updateUIView(_ uiView: UISlider, context: Context) {
adapt(uiView, context: context)
}
func makeCoordinator() -> SwiftUISlider.Coordinator {
Coordinator(value: $value)
}
private func adapt(_ slider: UISlider, context: Context) {
slider.value = Float(value)
}
}
struct PresentationMode_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}