How to detect whether targetEnvironment is iPadOS in SwiftUI?
Asked Answered
Y

4

32

I'd like to display different views when building for iOS and iPadOS. Currently, I know I can do

import SwiftUI

struct ContentView: View {
    #if targetEnvironment(macCatalyst)
    var body: some View {
        Text("Hello")
    }
    #else
    var body: some View {
        Text("Hello")
    }
    #endif
}

to display different views between macOS and iPadOS/iOS (introduced in Swift 4/5). But how do I differentiate between the latter? I can't seem to use targetEnvironment...

Ypres answered 26/8, 2019 at 5:31 Comment(0)
G
32

I use this in my code:

    private var idiom : UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
    private var isPortrait : Bool { UIDevice.current.orientation.isPortrait }

Then you can do this:

    var body: some View {
        NavigationView {
            masterView()

            if isPortrait {
                portraitDetailView()
            } else {
                landscapeDetailView()
            }
        }
    }

    private func portraitDetailView() -> some View {
        if idiom == .pad {
            return Text("iPadOS")
        } else {
            return Text("iOS")
        }
    }
Goerke answered 27/8, 2019 at 6:50 Comment(1)
Yeap, ` private var idiom : UIUserInterfaceIdiom {UIDevice.current.userInterfaceIdiom }` is simply the best solution I found. Thanks.Bethea
Z
29

I add the following code as an extension of UIDevice.

extension UIDevice {
    static var isIPad: Bool {
        UIDevice.current.userInterfaceIdiom == .pad
    }
    
    static var isIPhone: Bool {
        UIDevice.current.userInterfaceIdiom == .phone
    }
}

Now anywhere I can call UIDevice.isIPad or UIDevice.isIPhone to know which device is it.

Zaremski answered 28/6, 2021 at 14:54 Comment(1)
I prefer this solution but I believe it should be used as UIDevice.current.isIpad and UIDevice.current.isIphone. At least that's how I had to write it to work.Katabatic
P
6

To return different view types you can use AnyView eraser type:

if UIDevice.current.userInterfaceIdiom == .pad {
    return AnyView(Text("Hello, World!"))
} else {
    return AnyView(Rectangle().background(Color.green))
}
Perfumery answered 26/5, 2020 at 22:25 Comment(0)
G
4

While you can check the device type directly, as others answers suggest, it's best practice to use horizontal and/or vertical size classes. The constraints defining a view layout are typically the size of the view and not the device per se, so these APIs let us declare our intent more semantically.

Doing so also gives your app natural support for features like split view.

struct ContentView: View {

    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    
    var body: some View {
        // or use verticalSizeClass if you want to consider view height
        // or use both!
        if horizontalSizeClass == .compact {
            compactView
        } else {
            regularView
        }
    }

    private var regularView: some View {
        Text("Regular")
    }

    private var compactView: some View {
        Text("Compact")
    }
}
Gwendolin answered 15/9, 2023 at 16:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.