0

I am trying to add an icon button to the leading edge of a NavigationView's toolbar - but I want that button to only be visible when the device is in landscape mode. (Kinda like how the default Notes app works)

I have the following code:

var body: some View {
        VStack {
            // ... content display
        }
        .toolbar {
            if UIDevice.current.orientation.isLandscape {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: toggleFullscreen) {
                        if isFullscreen {
                            Image(systemName: "arrow.down.right.and.arrow.up.left")
                        } else {
                            Image(systemName: "arrow.up.left.and.arrow.down.right")
                        }
                    }
                }
            }
            ToolbarItem(placement: .navigationBarTrailing) {
                Button(action: createNewNote) {
                    Image(systemName: "square.and.pencil")
                }
            }
        })
    }

If I remove the conditional if clause within the toolbar content, it works fine, but it seems to not recognize the if statement at all?

However, the if statement in the VStack works fine.

The specific error I get is:

Closure containing control flow statement cannot be used with result builder 'ToolbarContentBuilder'

Any idea how to fix this?


P.S. Consider me a complete SwiftUI noob. Although, I am proficient in React - so any React analogies to help me understand would be much appreciated.

Vidur
  • 1,442
  • 2
  • 17
  • 37

1 Answers1

1

You're detecting the device orientation in a wrong way SwiftUI One way you can achieve that is by using a custom modifier to detect device rotation:

Cfr: https://www.hackingwithswift.com/quick-start/swiftui/how-to-detect-device-rotation

The code would look like this:

1. Create custom modifier: Detecting Device Rotation

// Our custom view modifier to track rotation and call our action
struct DeviceRotationViewModifier: ViewModifier {
    let action: (UIDeviceOrientation) -> Void

    func body(content: Content) -> some View {
        content
            .onAppear()
            .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                action(UIDevice.current.orientation)
            }
    }
}

// A View wrapper to make the modifier easier to use
extension View {
    func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View {
        self.modifier(DeviceRotationViewModifier(action: action))
    }
}

2. Use the created modifier:

struct ContentView: View {
    
    @State private var isFullscreen = false
    @State private var orientation = UIDeviceOrientation.unknown

    var body: some View {
        NavigationView {
            VStack {
                // ... content display
                
            }
            .onRotate { orientation = $0 }
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    if orientation.isLandscape {
                        Button(action: {}) {
                            if isFullscreen {
                                Image(systemName: "arrow.down.right.and.arrow.up.left")
                            } else {
                                Image(systemName: "arrow.up.left.and.arrow.down.right")
                            }
                        }
                    }
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {}) {
                        Image(systemName: "square.and.pencil")
                    }
                }
                
            }
            
        }
    }
    
}
cedricbahirwe
  • 1,274
  • 4
  • 15
  • That works flawlessly. However, I changed the initializer to `private var orientation = UIDevice.current.orientation` - does that make sense? – Vidur Jan 10 '22 at 12:37
  • 1
    Also - I was mainly trying to solve the if statement - and it definitely works when placed within the ToolbarItem's content block - but why doesn't it work in the `toolbar`'s content block like i have in my original code? – Vidur Jan 10 '22 at 12:38