Home > Net >  Make sheet the exact size of the content inside
Make sheet the exact size of the content inside

Time:11-19

Let's say I have a custom view inside of a sheet, something like this

VStack {
   Text("Title")
   Text("Some very long text ...")
}
.padding()
.presentationDetents([.height(250)])

How can I get the exact height of the VStack and pass it to the presentationDetents modifier so that the height of the sheet is exactly the height of the content inside?

CodePudding user response:

You can use a GeometryReader and PreferenceKey to read the size and then write it to a state variable. In my example, I store the entire size, but you could adjust it to store just the height, since it's likely that that is the only parameter you need.

struct ContentView: View {
    @State private var showSheet = false
    @State private var size: CGSize = .zero
    
    var body: some View {
        Button("View sheet") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            VStack {
                Text("Title")
                Text("Some very long text ...")
            }
            .padding()
            .background(
                GeometryReader { geometryProxy in
                    Color.clear
                        .preference(key: SizePreferenceKey.self, value: geometryProxy.size)
                }
            )
            .onPreferenceChange(SizePreferenceKey.self) { newSize in
                size.height = newSize.height
            }
            .presentationDetents([.height(size.height)])
        }
    }
}

struct SizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() }
}

CodePudding user response:

struct ContentView: View {
@State private var showingSheet = false

let heights = stride(from: 0.1, through: 1.0, by: 0.1).map { PresentationDetent.fraction($0) }

var body: some View {
    Button("Show Sheet") {
        showingSheet.toggle()
    }
    .sheet(isPresented: $showingSheet) {
        Text("Random text ")
            .presentationDetents(Set(heights))
    }
}

}

CodePudding user response:

Using the general idea made by @jnpdx including some updates such as reading the size of the overlay instead of the background, here is what works for me:

struct ContentView: View {
    @State private var showSheet = false
    @State private var sheetHeight: CGFloat = .zero

    var body: some View {
        Button("Open sheet") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            VStack {
                Text("Title")
                Text("Some very long text ...")
            }
            .padding()
            .overlay {
                GeometryReader { geometry in
                    Color.clear.preference(key: InnerHeightPreferenceKey.self, value: geometry.size.height)
                }
            }
            .onPreferenceChange(InnerHeightPreferenceKey.self) { newHeight in
                sheetHeight = newHeight
            }
            .presentationDetents([.height(sheetHeight)])
        }
    }
}

struct InnerHeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = .zero
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}
  • Related