如何使用SwiftUI中的onReceive从ObservedObject获取数据?
在我的SwiftUI应用程序中,每次值更改时,我都需要从ObservedObject获取数据。我知道我们可以用。onReceive?我不太了解苹果关于它的文档。我不知道该怎么做 我的代码:如何使用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
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来让一个运行的计时器继续将状态更改发布到我的视图中。