web-dev-qa-db-fra.com

SwiftUI: Comment obtenir des mises à jour continues de Slider

J'expérimente avec SwiftUI et le contrôle Slider comme ceci:

struct MyView: View {

    @State private var value = 0.5

    var body: some View {
        Slider(value: $value) { pressed in
        }
    }
}

J'essaie d'obtenir des mises à jour continues de Slider au fur et à mesure que l'utilisateur le fait glisser, mais il semble qu'il ne met à jour la valeur qu'à la fin du changement de valeur.

Quelqu'un a joué avec ça? savoir comment obtenir un curseur SwiftUI pour émettre un flux de modifications de valeur? Combinez peut-être?

9
drekka

Dans SwiftUI, vous pouvez lier des éléments d'interface utilisateur tels que le curseur aux propriétés de votre modèle de données et y implémenter votre logique métier.

Par exemple, pour obtenir des mises à jour continues du curseur:

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

Notez que pour que votre scène utilise l'objet de modèle de données, vous devez mettre à jour votre window.rootViewController à quelque chose comme ci-dessous dans la classe SceneDelegate, sinon l'application se bloque.

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

enter image description here

9

Après avoir beaucoup joué, je me suis retrouvé avec le code suivant. C'est un peu réduit pour garder la réponse courte, mais voilà. Il y avait quelques choses dont j'avais besoin:

  • Pour lire les modifications de valeur du curseur et les arrondir à l'entier le plus proche avant de définir une liaison externe.
  • Pour définir une valeur d'indice localisé en fonction de l'entier.
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
    }
}
3
drekka

Je ne parviens pas à reproduire ce problème sur iOS 13 Bêta 2. Quel système d'exploitation ciblez-vous?

À l'aide d'une reliure personnalisée, la valeur est imprimée pour chaque petite modification, non seulement une fois l'édition terminée.

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

Notez que la fermeture ({ pressed in }) signale uniquement lorsque la modification de fin commence et se termine, le flux de valeurs est uniquement transmis à la liaison.

1
Palle

iOS 13.4, Swift 5.x

Une réponse basée sur Mohammid excellente solution, seulement je ne voulais pas utiliser de variables environnementales.

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

Avec une petite modification de ContentView_Preview et la même chose dans SceneDelegate.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
      ContentView(sliderData: SliderData.init())
    }
}
0
user3069232