Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 352 Vote(s) - 3.58 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How can I get data from ObservedObject with onReceive in SwiftUI?

#1
In my SwiftUI app, I need to get data from ObservedObject each time the value change. I understood that we could do that with .onReceive? I don't understand well the documentation of Apple about it. I don't know how I can do this.

My code:

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

In my LocationManager class, I have a heading Published variable, this is the variable I want check.

I need to get data each time the value of heading change to create an animation when my arrow move. For some raisons I need to use CGAffineTransform.
Reply

#2
## iOS 14+

Starting from iOS 14 we can now use the `onChange` modifier that performs an action every time a value is *changed*:

Here is an example:
```
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
}
}
}
}
```
Reply

#3
First in your view you need to request the HeadingProvider to start updating heading. You need to listen to objectWillChange notification, the closure has one argument which is the new value that is being set on ObservableObject.

I have changed your Compass a bit:



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

I have written an example 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)
}
}
}

Remember you need to handle asking for permission to read user's location and you need to call stopUpdatingHeading() at some point.
Reply

#4
You can consider using @Published in your ObservableObject. Then your onreceive can get a call by using location.$heading.

For observable object it can be

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

For the receive you can use

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

The above is useful if you want to perform additional functions on object changes. In many cases you can directly use the location.heading as your state changer. And then give it an animation below it. So

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





Reply

#5
You can do as @LuLuGaGa suggests but it's a bit of a kludge. objectWillChange is defined as an `ObservableObjectPublisher` and while defining it as a `PassthroughSubject<CGFloat,Never>` is going to work today there is no guarantee that it will work in the future.

An object is not restricted to having a single publisher so you can define a second or a third for other purposes than SwiftUI. e.g.:

``` swift
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 }
}
```

Subclassing ObservableObject will correctly define objectWillChange for you so you don't have to do it yourself.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through