Reputation: 8995
Swift 5.2 iOS 14
Trying to understand the Combine Framework in SwiftUI and put this code together using an example I found on the web. But ... the example wasn't complete.
Now when I change the orientation of my device, it does write to orientation, but how do I use this in my main loop? I cannot seem to find anything to subscribe too so I tried just using a onChange. Sadly that doesn't work.
class SizeClassViewV: ObservableObject {
@Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
enum Orientation {
case portrait
case landscape
}
@Published var orientation: Orientation = .portrait
private var listener: AnyCancellable?
init() {
if horizontalSizeClass == .compact && verticalSizeClass == .regular {
orientation = .portrait
} else if horizontalSizeClass == .regular && verticalSizeClass == .compact {
orientation = .landscape
}
listener = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
.compactMap { ($0.object as? UIDevice)?.orientation }
.compactMap { deviceOrientation -> Orientation? in
if deviceOrientation.isPortrait {
return .portrait
} else if deviceOrientation.isLandscape {
return .landscape
} else {
return nil
}
}
.assign(to: \.orientation, on: self)
}
deinit {
listener?.cancel()
}
}
In my main loop looks like this right now?
struct ContentView: View {
@State var orient = SizeClassViewX()
var body: some View {
Text("foo")
.onChange(of: orient.orientation) { ( _ ) in
print("changed")
}
}
}
Changed is never printed when I change the orientation?
Upvotes: 1
Views: 698
Reputation: 54611
I cannot seem to find anything to subscribe
If you take a look at the onReceive
declaration, you can see that it accepts a Combine Publisher
:
@inlinable public func onReceive<P>(_ publisher: P, perform action: @escaping (P.Output) -> Void) -> some View where P : Publisher, P.Failure == Never
Which means that if you want to subscribe to the orientation
changes the Combine way, you can just use onReceive
and pass the relevant Publisher
:
struct ContentView: View {
@State var orient = SizeClassViewV()
var body: some View {
Text("foo")
.onReceive(orient.$orientation) { _ in
print("changed")
}
}
}
Upvotes: 1
Reputation: 394
final class SizeClassViewV: ObservableObject {
enum Orientation: Equatable {
case portrait
case landscape
}
@Published var orientation: Orientation = .portrait
private var listener: AnyCancellable?
init() {
orientation = UIDevice.current.orientation.isPortrait ? Orientation.portrait : .landscape
listener = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
.compactMap { ($0.object as? UIDevice)?.orientation }
.map { $0.isPortrait ? Orientation.portrait : .landscape }
.assign(to: \.orientation, on: self)
}
deinit {
listener?.cancel()
}
}
struct ContentView: View {
@ObservedObject var orient = SizeClassViewV()
var body: some View {
Text("Hello, world!")
.padding()
.onReceive(orient.$orientation) { value in
print(value)
}
}
}
Upvotes: 1
Reputation: 258345
It works actually in different way - Environment
wrapper is for context of SwiftUI view, so use as in following demo:
struct ContentView: View {
@Environment(\.verticalSizeClass) var verticalSizeClass
@Environment(\.horizontalSizeClass) var horizontalSizeClass
var body: some View {
Text("foo")
.onChange(of: horizontalSizeClass) { _ in
print("horizontal class changed")
}
.onChange(of: verticalSizeClass) { _ in
print("vertical class changed")
}
}
}
Upvotes: 0