Skip to content

Instantly share code, notes, and snippets.

@EmilioPelaez
Created February 21, 2023 19:50
Show Gist options
  • Save EmilioPelaez/33eb5ea685f77d5ac72e0036f9cb2d84 to your computer and use it in GitHub Desktop.
Save EmilioPelaez/33eb5ea685f77d5ac72e0036f9cb2d84 to your computer and use it in GitHub Desktop.
Drop-in replacement for the `.sheet` modifier that will adjust the height of the sheet to fit the its content.
//
// Created by Emilio Peláez on 11/2/23.
//
import SwiftUI
public extension View {
func fittedSheet<Item: Identifiable, Content: View>(item binding: Binding<Item?>,
onDismiss: @escaping () -> Void = {},
content: @escaping (Item) -> Content) -> some View {
modifier(FittedSheetModifier(item: binding, onDismiss: onDismiss, itemContent: content))
}
func fittedSheet<Content: View>(isPresented binding: Binding<Bool>,
onDismiss: @escaping () -> Void = {},
content: @escaping () -> Content) -> some View {
let dummyBinding: Binding<Dummy?> = .init(
get: { binding.wrappedValue ? Dummy() : nil },
set: { binding.wrappedValue = $0 != nil ? true : false }
)
let fittedSheetModifier = FittedSheetModifier(item: dummyBinding, onDismiss: onDismiss) { _ in
content()
}
return modifier(fittedSheetModifier)
}
}
struct FittedSheetModifier<Item: Identifiable, ItemContent: View>: ViewModifier {
@Binding var item: Item?
let onDismiss: () -> Void
let itemContent: (Item) -> ItemContent
@State private var size: CGSize = .zero
func body(content: Content) -> some View {
content
.sheet(item: $item, onDismiss: onDismiss) { item in
itemContent(item)
.padding()
.padding(.top, 4)
.storeSize(in: $size)
.presentationDetents([.height(size.height)])
.presentationDragIndicator(.visible)
}
}
}
private struct Dummy: Identifiable {
var id = "Dummy"
}
struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
public extension View {
func getSize(callback: @escaping (CGSize) -> Void) -> some View {
overlay {
GeometryReader { proxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: proxy.size)
}
}
.onPreferenceChange(SizePreferenceKey.self, perform: callback)
}
func storeSize(in binding: Binding<CGSize>) -> some View {
getSize { binding.wrappedValue = $0 }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment