如何使用SwiftUI中的onReceive从ObservedObject获取数据?

如何使用SwiftUI中的onReceive从ObservedObject获取数据?,swift,swiftui,combine,Swift,Swiftui,Combine,在我的SwiftUI应用程序中,每次值更改时,我都需要从ObservedObject获取数据。我知道我们可以用。onReceive?我不太了解苹果关于它的文档。我不知道该怎么做 我的代码: import SwiftUI import CoreLocation struct Compass: View { @StateObject var location = LocationManager() @State private var angle: CGFloat = 0

在我的SwiftUI应用程序中,每次值更改时,我都需要从ObservedObject获取数据。我知道我们可以用。onReceive?我不太了解苹果关于它的文档。我不知道该怎么做

我的代码:

import SwiftUI
import CoreLocation

struct Compass: View {
  
  @StateObject var location = 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))
    )
  }
}
在LocationManager类中,我有一个标题已发布变量,这是我要检查的变量


我需要在每次标题值更改时获取数据,以便在箭头移动时创建动画。对于某些理由,我需要使用CGAffineTransform。

首先在您的视图中,您需要请求HeadingProvider开始更新heading。您需要监听objectWillChange通知,闭包有一个参数,它是在ObservableObject上设置的新值

我把你的指南针改了一点:

struct Compass: View {

  @StateObject var headingProvider = HeadingProvider()
  @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: angle))
        .onReceive(self.headingProvider.objectWillChange) { newHeading in
            withAnimation(.easeInOut(duration: 1.0)) {
                self.angle = newHeading
            }
        }

      Text(String("\(angle)"))
        .font(.system(size: 20))
        .fontWeight(.light)
        .padding(.top, 15)
    }   .onAppear(perform: {
            self.headingProvider.updateHeading()
        })
  }
}
我已经编写了一个HeadingProvider示例:

public class HeadingProvider: NSObject, ObservableObject {
    
    public let objectWillChange = PassthroughSubject<CGFloat,Never>()
    
    public private(set) var heading: CGFloat = 0 {
        willSet {
            objectWillChange.send(newValue)
        }
    }
    
    private let locationManager: CLLocationManager
    
    public override init(){
        self.locationManager = CLLocationManager()
        super.init()
        self.locationManager.delegate = self
    }
    
    public func updateHeading() {
        locationManager.startUpdatingHeading()
    }
}

extension HeadingProvider: CLLocationManagerDelegate {
    
    public func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
        DispatchQueue.main.async {
            self.heading = CGFloat(newHeading.trueHeading)
        }
    }
}
公共类标题提供程序:NSObject,ObservableObject{
public let objectWillChange=PassthroughSubject()
公私(集合)变量标题:CGFloat=0{
意志{
objectWillChange.send(newValue)
}
}
私人出租位置管理器:CLLocationManager
公共重写init(){
self.locationManager=CLLocationManager()
super.init()
self.locationManager.delegate=self
}
公共函数更新标题(){
locationManager.startUpdatingHeading()
}
}
扩展头提供程序:CLLocationManagerDelegate{
公共函数位置管理器(u管理器:CLLocationManager,didUpdateHeading新标题:CLHeading){
DispatchQueue.main.async{
self.heading=CGFloat(newHeading.trueHeading)
}
}
}

请记住,您需要处理读取用户位置的权限请求,并且需要在某个时候调用stopUpdatengHeading()。

您可以按照@LuLuGaGa的建议执行,但这有点困难。objectWillChange被定义为一个
可观察对象发布者
,虽然将其定义为一个
传递对象
将在今天起作用,但不能保证它在将来起作用

对象不限于具有单个发布服务器,因此您可以定义第二个或第三个发布服务器,用于SwiftUI以外的其他用途。e、 g:

可观察类:可观察对象,可识别{
设id=UUID()
让publisher=PassthroughSubject()
var值:T{
willSet{objectWillChange.send()}
didSet{publisher.send(value)}
}
init(initValue:T){self.value=initValue}
}

子类观察对象将为您正确定义ObjyTwitter更改,因此您不必自己做。

< P>可以考虑使用@在您的Sababable对象中发布。然后,您的onreceive可以通过使用location.$heading获得呼叫

对于可观察的物体,它可以是

class LocationManager: ObservableObject {
@Published var heading:Angle = Angle(degrees: 20)
}
对于您可以使用的接收

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
          }
        })
  }
}
}
如果您想对对象更改执行其他功能,上述内容非常有用。在许多情况下,您可以直接使用location.heading作为状态更改器。然后在下面给它一个动画。所以

            .modifier(RotationEffect(angle: location.heading))
            .animation(.easeInOut)
SwiftUI 2/iOS 14 从iOS 14开始,我们现在可以使用
onChange
修饰符,在每次更改值时执行操作:

以下是一个例子:

struct Compass: View {
    @StateObject var location = LocationManager()
    @State private var angle: CGFloat = 0

    var body: some View {
        VStack {
            // ...
        }
        .onChange(of: location.heading) { heading in
            withAnimation(.easeInOut(duration: 1.0)) {
                self.angle = heading
            }
        }
    }
}

嗨,谢谢,我现在更明白了。但我有一条消息:无法将类型为“(Double)->()”的值转换为预期的参数类型“()->Void”我需要添加其他内容?否-objectWillChange的类型由关联类型定义,所以@LuLuGaGa的建议很好:
var objectWillChange:Self.ObjectWillChangePublisher
Apple说你可以将objectWillChange重新定义为你喜欢的任何类型的发布者。SwiftUII中的WWDC2020 Data essentials发现这个答案更容易理解。非常感谢。(我认为这应该是公认的答案)谢谢!我相信这就是我问题的答案。我使用onReceive和@Published来让一个运行的计时器继续将状态更改发布到我的视图中。