Attempting to reference the @Environment
objects horizontalSizeClass
and verticalSizeClass
on macOS (native, not Catalyst) results in the following errors:
'horizontalSizeClass' is unavailable in macOS
'verticalSizeClass' is unavailable in macOS
I appreciate that these properties aren't really applicable for macOS, but this presents a big barrier to creating SwiftUI Views which work universally (i.e. cross-platform across macOS, iOS, etc.).
One workaround is to wrap all the size-class-specific code inside conditional compilation, but the result is lots of duplication and redundancy (see below example).
Isn't there a more efficient way to handle this?
Example Universal View:
struct ExampleView: View {
#if !os(macOS)
@Environment(\.horizontalSizeClass) var horizontalSizeClass
#endif
private var item1: some View {
Text("Example Item 1")
}
private var item2: some View {
Text("Example Item 2")
}
private var item3: some View {
Text("Example Item 3")
}
var body: some View {
VStack {
#if !os(macOS)
if horizontalSizeClass == .compact {
VStack {
item1
item2
item3
}
} else {
HStack {
item1
item2
item3
}
}
#else
HStack {
item1
item2
item3
}
#endif
}
}
}
It's true that macOS doesn't support horizontalSizeClass
and verticalSizeClass
natively, but the good news is it's easy to add them.
You can define your own @Environment
objects by making a struct
that conforms to EnvironmentKey
, then using it to extend EnvironmentValues
.
All you need to implement size classes on macOS is the following. They just return .regular
at all times, but it's enough to function exactly the same as on iOS.
#if os(macOS)
enum UserInterfaceSizeClass {
case compact
case regular
}
struct HorizontalSizeClassEnvironmentKey: EnvironmentKey {
static let defaultValue: UserInterfaceSizeClass = .regular
}
struct VerticalSizeClassEnvironmentKey: EnvironmentKey {
static let defaultValue: UserInterfaceSizeClass = .regular
}
extension EnvironmentValues {
var horizontalSizeClass: UserInterfaceSizeClass {
get { return self[HorizontalSizeClassEnvironmentKey.self] }
set { self[HorizontalSizeClassEnvironmentKey.self] = newValue }
}
var verticalSizeClass: UserInterfaceSizeClass {
get { return self[VerticalSizeClassEnvironmentKey.self] }
set { self[VerticalSizeClassEnvironmentKey.self] = newValue }
}
}
#endif
With this in place, you don't need anything special for macOS. For example:
struct ExampleView: View {
@Environment(\.horizontalSizeClass) var horizontalSizeClass
private var item1: some View {
Text("Example Item 1")
}
private var item2: some View {
Text("Example Item 2")
}
private var item3: some View {
Text("Example Item 3")
}
var body: some View {
VStack {
if horizontalSizeClass == .compact {
VStack {
item1
item2
item3
}
} else {
HStack {
item1
item2
item3
}
}
}
}
}
You can even put these extensions into a framework for broader use, so long as you define them as public
.