sqwk
sqwk

Reputation: 2699

SwiftUI: Tie window aspect ratio to subview

How can I modify a SwiftUI window to respect the aspect ratio of a subview? The window should always be as high as the contained contentView, resizing its width as necessary? (Think a video player with a 16/9 contentView and a variable width sidebar.)

Using storyboards, this was as easy as setting the aspect ratio of the contentView and setting the constraints to 0 on all sides—the window would scale up as needed, keeping the aspect ratio of the the subview. How can this be done using SwiftUI?

I tried using a PreferenceKey to pass the value of the width back up the view tree to resize the window as needed. However, the value passed is always 0. When resizing the window, more values are generated, but they are also 0.

struct SizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero

    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

struct ContentView: View {
   
    var contentView: some View {
        Rectangle()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(
                GeometryReader { geometry in
                    Color.clear
                        .preference(key: SizePreferenceKey.self, value: geometry.size.width)
                }
            )
    }

    
    var body: some View {
        HSplitView {
            contentView
                .aspectRatio(16/9, contentMode: .fit)
            someTabView
                .frame(width: 300)
        }.onPreferenceChange(SizePreferenceKey.self) { value in
            print(value)
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

EDIT: I since found out that attaching the GeometryReader and PreferenceKey to the last View in the HSplitView works as expected. Attaching it to any other index in the HSplitView does not.

Upvotes: 4

Views: 1507

Answers (1)

Vyacheslav Pukhanov
Vyacheslav Pukhanov

Reputation: 1928

Use the .aspectRatio(nil) modifier. The nil parameter tells the view to maintain its aspect ratio.

Upvotes: 1

Related Questions