0

I have the following situation: I need to display a sheet with the items user can pick. Since the amount of this items can be big, I added the ScrollView to display the items. Here's the way I implemented the sheet:

struct PickerModalView: View {
    let title: LocalizedStringKey
    let items: [String]
    let onDismiss: () -> Void
    let onSubmit: (String) -> Void
    
    init(
        title: LocalizedStringKey,
        items: [String],
        onDismiss: @escaping () -> Void = {},
        onSubmit: @escaping (String) -> Void
    ) {
        self.title = title
        self.items = items
        self.onDismiss = onDismiss
        self.onSubmit = onSubmit
    }
    
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            HStack {
                Text(title)
                    .title2()
                    .foregroundColor(MXColor.textPrimary)
                Spacer()
                Button {
                    onDismiss()
                    dismiss()
                } label: {
                    Image(MXImage.ic_modal_close)
                }
            }.padding(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16))
            ScrollView {
                VStack {
                    ForEach(items, id: \.self) { item in
                        PickerItemView(text: item) { clickedItem in
                            onSubmit(clickedItem)
                            dismiss()
                        }
                    }
                }
            }.background(.red)
        }.background(MXColor.surfacePrimary)
    }
}

struct PickerItemView: View {
    let text: String
    let onClick: (String) -> Void
    
    var body: some View {
        Button {
            onClick(text)
        } label: {
            HStack {
                Text(text).foregroundColor(MXColor.textPrimary)
                Spacer()
                Image(systemName: "checkmark").foregroundColor(MXColor.system)
            }.padding(.vertical, 11)
                .padding(.horizontal, 16)
        }
    }
}

I want to show this modal not in full height, but to make it wrap content (so, for example if I have 2 elements it should fill only quater of screen and if there are 100 items they fill the whole height). For this purpose I created the following modifier:

struct AdaptiveModalModifier<T: View>: ViewModifier {
    let sheetContent: () -> T
    
    @Binding var isPresented: Bool
    @State private var size: CGSize = .zero
    
    func body(content: Content) -> some View {
        if #available(iOS 16.0, *) {
            content.sheet(isPresented: $isPresented) {
                sheetContent().background(GeometryReader { proxy in
                    Color.clear.onAppear { size = proxy.size }
                }).presentationDetents([.height(size.height)])
            }
        } else {
            /// some logic here
        }
    }
}

So, this modifier just calculates the height of content and sets it to detents. But there's the issue with the ScrollView, that it fills the full height by default in my case (here's the screenshot) (red is the background of ScrollView)

enter image description here

I tried to add VStack into GeometryReader to check it's size and set it to ScrollView, but it returns 10.0 in my case (I don't know, how this value is calculated, but it seems to be wrong).

Can you please help me to set the ScrollView height the same as child's content? Thanks in advance for any help!

Sergei Mikhailovskii
  • 2,100
  • 2
  • 21
  • 43
  • Have a look at `ViewThatFits` (https://developer.apple.com/documentation/swiftui/viewthatfits). For clarification, you do something like: `ViewThatFits { VStack { ... } ScrollView { ... } }`. If VStack fits, it would be used, it not, ScrollView would. – Baglan Apr 16 '23 at 14:08

0 Answers0