SwiftUI: How to get continuous updates from Slider
我正在尝试像这样的SwiftUI和Slider控件:
1 2 3 4 5 6 7 8 9 | struct MyView: View { @State private var value = 0.5 var body: some View { Slider(value: $value) { pressed in } } } |
我试图在用户拖动它时从
有人玩过这个吗? 知道如何让SwiftUI滑块发布价值变化流? 结合起来?
在SwiftUI中,您可以将UI元素(例如滑块)绑定到数据模型中的属性,并在那里实现业务逻辑。
例如,要获得连续的滑块更新:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import SwiftUI import Combine final class SliderData: BindableObject { let didChange = PassthroughSubject<SliderData,Never>() var sliderValue: Float = 0 { willSet { print(newValue) didChange.send(self) } } } struct ContentView : View { @EnvironmentObject var sliderData: SliderData var body: some View { Slider(value: $sliderData.sliderValue) } } |
请注意,要使场景使用数据模型对象,您需要将
1 | window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(SliderData())) |
经过大量的尝试,我最终得到了以下代码。为了使答案简短一点,它做了一些削减,但是这里有。我需要做几件事:
- 要从滑块读取值更改并将其四舍五入为最接近的整数,然后再设置外部绑定。
- 根据整数设置本地化的提示值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | struct AspectSlider: View { // The first part of the hint text localization key. private let hintKey: String // An external integer binding to be set when the rounded value of the slider changes to a different integer. private let value: Binding<Int> // A local binding that is used to track the value of the slider. @State var sliderValue: Double = 0.0 init(value: Binding<Int>, hintKey: String) { self.value = value self.hintKey = hintKey } var body: some View { VStack(alignment: .trailing) { // The localized text hint built from the hint key and the rounded slider value. Text(LocalizedStringKey("\\(hintKey).\\(self.value.value)")) HStack { Text(LocalizedStringKey(self.hintKey)) Slider(value: Binding<Double>( getValue: { self.$sliderValue.value }, setValue: { self.sliderChanged(toValue: $0) } ), through: 4.0) { if !$0 { self.slideEnded() } } } } } private func slideEnded() { print("Moving slider to nearest whole value") self.sliderValue = self.sliderValue.rounded() } private func sliderChanged(toValue value: Double) { $sliderValue.value = value let roundedValue = Int(value.rounded()) if roundedValue == self.value.value { return } print("Updating value") self.value.value = roundedValue } } |
在版本11.4.1(11E503a)和Swift 5中,我没有复制它。
通过使用Combine,我可以不断从滑块更改中进行更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class SliderData: ObservableObject { @Published var sliderValue: Double = 0 ... } struct ContentView: View { @ObservedObject var slider = SliderData() var body: some View { VStack { Slider(value: $slider.sliderValue) Text(String(slider.sliderValue)) } } } |
我无法在iOS 13 Beta 2上重现此问题。您要针对哪个操作系统?
使用自定义绑定,不仅可以在编辑结束之后,还可以为每个小的更改打印值。
1 | Slider(value: Binding<Double>(getValue: {0}, setValue: {print($0)})) |
请注意,闭包(
只需使用Slider的onEditingChanged参数即可。当用户移动滑块或仍与滑块接触时,该参数为true。当参数从true更改为false时,我会进行更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 | struct MyView: View { @State private var value = 0.5 func update(changing: Bool) -> Void { // Do whatever } var body: some View { Slider(value: $value, onEditingChanged: {changing in self.update(changing) }) { pressed in } } } |
iOS 13.4,Swift 5.x
一个基于Mohammid出色解决方案的答案,只是我不想使用环境变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class SliderData: ObservableObject { let didChange = PassthroughSubject<SliderData,Never>() @Published var sliderValue: Double = 0 { didSet { print("sliderValue \\(sliderValue)") didChange.send(self) } } } @ObservedObject var sliderData:SliderData Slider(value: $sliderData.sliderValue, in: 0...Double(self.textColors.count)) |
只需对ContentView_Preview进行少量更改,然后在SceneDelegate中进行相同的更改。
1 2 3 4 5 | struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView(sliderData: SliderData.init()) } } |