web-dev-qa-db-fra.com

Comment puis-je obtenir des données d'ObservedObject avec onReceive dans SwiftUI?

Dans mon application SwiftUI, j'ai besoin d'obtenir des données d'ObservedObject chaque fois que la valeur change. J'ai compris que nous pouvions le faire avec .onReceive? Je ne comprends pas bien la documentation de Apple à ce sujet. Je ne sais pas comment je peux le faire.

Mon code:

import SwiftUI
import CoreLocation

struct Compass: View {

  var locationManager = CLLocationManager()
  @ObservedObject var location: LocationManager = LocationManager()
  @State private var angle: CGFloat = 0

  var body: some View {
    VStack {
      Image("arrow")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(width: 300, height: 300)
        .modifier(RotationEffect(angle: -CGFloat(self.angle.degreesToRadians)))
        .onReceive(location, perform: {
          withAnimation(.easeInOut(duration: 1.0)) {
            self.angle = self.location.heading
          }
        })

      Text(String(self.location.heading.degreesToRadians))
        .font(.system(size: 20))
        .fontWeight(.light)
        .padding(.top, 15)
    }
  }
}

struct RotationEffect: GeometryEffect {
  var angle: CGFloat

  var animatableData: CGFloat {
    get { angle }
    set { angle = newValue }
  }

  func effectValue(size: CGSize) -> ProjectionTransform {
    return ProjectionTransform(
      CGAffineTransform(translationX: -150, y: -150)
        .concatenating(CGAffineTransform(rotationAngle: angle))
        .concatenating(CGAffineTransform(translationX: 150, y: 150))
    )
  }
}

Dans ma classe LocationManager, j'ai un en-tête Variable publiée, c'est la variable que je veux vérifier.

J'ai besoin d'obtenir des données à chaque fois que la valeur du cap change pour créer une animation lorsque ma flèche se déplace. Pour certaines raisons, j'ai besoin d'utiliser CGAffineTransform.

7
Guillaume

Vous pouvez envisager d'utiliser @Published dans votre ObservableObject. Ensuite, votre destinataire peut recevoir un appel en utilisant la position. $ $.

Pour un objet observable, il peut être

class LocationManager: ObservableObject {
@Published var heading:Angle = Angle(degrees: 20)
}

Pour la réception, vous pouvez utiliser

struct Compass: View {

  @ObservedObject var location: LocationManager = LocationManager()
  @State private var angle: Angle = Angle(degrees:0)

  var body: some View {
    VStack {
      Image("arrow")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(width: 300, height: 300)
        .modifier(RotationEffect(angle: angle))
        .onReceive(location.$heading, perform: { heading in
          withAnimation(.easeInOut(duration: 1.0)) {
            self.angle = heading
          }
        })
  }
}
}

Ce qui précède est utile si vous souhaitez effectuer des fonctions supplémentaires sur les modifications d'objet. Dans de nombreux cas, vous pouvez utiliser directement location.heading comme changeur d'état. Et puis donnez-lui une animation en dessous. Alors

            .modifier(RotationEffect(angle: location.heading))
            .animation(.easeInOut)
0
Pbk

Vous pouvez faire comme @LuLuGaGa le suggère, mais c'est un peu compliqué. objectWillChange est défini comme un ObservableObjectPublisher et tout en le définissant comme un PassthroughSubject<CGFloat,Never> va fonctionner aujourd'hui, il n'y a aucune garantie que cela fonctionnera à l'avenir.

Un objet n'est pas limité à un seul éditeur, vous pouvez donc en définir un deuxième ou un troisième à d'autres fins que SwiftUI. par exemple.:

class Observable<T>: ObservableObject, Identifiable {
    let id = UUID()
    let publisher = PassthroughSubject<T, Never>()
    var value: T {
        willSet { objectWillChange.send() }
        didSet { publisher.send(value) }
    }

    init(_ initValue: T) { self.value = initValue }
}

Le sous-classement ObservableObject définira correctement objectWillChange pour vous afin que vous n'ayez pas à le faire vous-même.

0
Michael Salmon