stardust4891
stardust4891

Reputation: 2550

SwiftUI set navigationViewStyle based on device

I'm trying to get my navigation view style to be stacked on iPad but default on iPhone.

Code:

.navigationViewStyle(UIDevice.current.userInterfaceIdiom == .pad ? StackNavigationViewStyle() : DefaultNavigationViewStyle())

Giving me the error:

Result values in '? :' expression have mismatching types 'StackNavigationViewStyle' and 'DefaultNavigationViewStyle'

Are these not both NavigationViewStyle subclasses?

Upvotes: 8

Views: 2486

Answers (3)

Nav Brar
Nav Brar

Reputation: 384

The modifier navigationViewStyle need a value of one type conforming to NavigationViewStyle. We can't use directly it will ask to confirm to any type NavigationViewStyle and cannot use the ternary operator which may return two different types StackNavigationViewStyle or DefaultNavigationViewStyle. it can be used as :

struct ContentView: View {
    let showStackView: Bool = false
    var body: some View {
        ZStack {
            NavigationView {
                Button("showStackView") {
                    showStackView.toggle()
                }
            }.navigationTitle("Options")
        }.modifier(MyNavigationViewStyle(showStackView)) // conditional modifier for navigation view style
    }
}

struct MyNavigationViewStyle: ViewModifier {
    let showStackView: Bool
    func body(content: Content) -> some View {
        if showStackView {
            content.navigationViewStyle(StackNavigationViewStyle())
        } else {
            content.navigationViewStyle(DefaultNavigationViewStyle())
        }
    }
}

https://developer.apple.com/forums/thread/683704

Upvotes: 0

Asperi
Asperi

Reputation: 257711

I recommend to extract it into simple wrapper modifier and use it in place where needed. Here is modifier:

Update:

extension View {
    @ViewBuilder
    public func currentDeviceNavigationViewStyle() -> some View {
        if UIDevice.current.userInterfaceIdiom == .pad {
            self.navigationViewStyle(StackNavigationViewStyle())
        } else {
            self.navigationViewStyle(DefaultNavigationViewStyle())
        }
    }
}

SwiftUI 1.0 (backward-compatible)

extension View {
    public func currentDeviceNavigationViewStyle() -> AnyView {
        if UIDevice.current.userInterfaceIdiom == .pad {
            return AnyView(self.navigationViewStyle(StackNavigationViewStyle()))
        } else {
            return AnyView(self.navigationViewStyle(DefaultNavigationViewStyle()))
        }
    }
}

Upvotes: 7

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119302

Thats because ? : should always return values with same type.

You can implement a custom conditional modifier extension like this:

extension View {
    public func modify<T, U>(if condition: Bool, then modifierT: T, else modifierU: U) -> some View where T: ViewModifier, U: ViewModifier {
        Group {
            if condition {
                modifier(modifierT)
            } else {
                modifier(modifierU)
            }
        }
    }
}

Implement custom modifiers like this:

struct IPadNavigationViewStyle: ViewModifier {
    func body(content: Content) -> some View { content.navigationViewStyle(StackNavigationViewStyle()) }
}

struct IPhoneNavigationViewStyle: ViewModifier {
    func body(content: Content) -> some View { content.navigationViewStyle(DefaultNavigationViewStyle()) }
}

and then use it like:

    .modify(if: UIDevice.current.userInterfaceIdiom == .pad, then: IPadNavigationViewStyle(), else: IPhoneNavigationViewStyle() )

Upvotes: 3

Related Questions