SwiftUI: How to get continuous updates from Slider

后端 未结 6 1749
悲哀的现实
悲哀的现实 2020-12-11 01:56

I\'m experimenting with SwiftUI and the Slider control like this:

struct MyView: View {

    @State private var value = 0.5

    var body: some View {
               


        
相关标签:
6条回答
  • 2020-12-11 02:20

    In SwiftUI, you can bind UI elements such as slider to properties in your data model and implement your business logic there.

    For example, to get continuous slider updates:

    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)
      }
    }
    

    Note that to have your scene use the data model object, you need to update your window.rootViewController to something like below inside SceneDelegate class, otherwise the app crashes.

    window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(SliderData()))
    

    0 讨论(0)
  • 2020-12-11 02:20

    iOS 13.4, Swift 5.x

    An answer based on Mohammid excellent solution, only I didn't want to use environmental variables.

    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))
    

    With a small change to ContentView_Preview and the same in SceneDelegate.

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
          ContentView(sliderData: SliderData.init())
        }
    }
    
    0 讨论(0)
  • 2020-12-11 02:30

    I am not able to reproduce this issue on iOS 13 Beta 2. Which operating system are you targeting?

    Using a custom binding, the value is printed for every small change, not only after editing ended.

    Slider(value: Binding<Double>(getValue: {0}, setValue: {print($0)}))
    

    Note, that the closure ({ pressed in }) only reports when editing end starts and ends, the value stream is only passed into the binding.

    0 讨论(0)
  • 2020-12-11 02:33

    Just use the onEditingChanged parameter of Slider. The argument is true while the user is moving the slider or still in contact with it. I do my updates when the argument changes from true to false.

    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 }
        }
    }
    
    0 讨论(0)
  • 2020-12-11 02:36

    After much playing around I ended up with the following code. It's a little cut down to keep the answer short, but here goes. There was a couple of things I needed:

    • To read value changes from the slider and round them to the nearest integer before setting an external binding.
    • To set a localized hint value based on the integer.
    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
        }
    }
    
    0 讨论(0)
  • 2020-12-11 02:43

    In Version 11.4.1 (11E503a) & Swift 5. I didn't reproduce it. By using Combine, I could get continuously update from slider changes.

    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))
        } 
      }
    }  
    
    
    0 讨论(0)
提交回复
热议问题