iosswiftuipopoverswiftui-sheet

SwiftUI .popover frame size incorrect


In iOS 16.4 we can now use .presentationCompactAdaptation(.none) in our .popover to achieve a true popover on iOS (compact screen sizes).

SomeView()
    .popover(isPresented: $isPopoverOpen) {
        Text("Hello world!")
            .fixedSize(horizontal: false, vertical: true)
            .padding()
            .presentationCompactAdaptation(.none)
    }

This will give us something like:

enter image description here

Great, it works as expected!

The issue arises when the Text() in the popover spans multiple lines. For some reason, the popover height will only grow up to a certain height (~3 lines with non-dynamic .body font). Here is an illustration of the issue using some Lorem Ipsum text. Notice how the end gets clipped off because the popover height is too short:

enter image description here

How can I make the popover fit the Text() content? I can statically define the height but I would like the popover to perfectly fit the content.


Solution

  • You can get the height of the Text element using this approach.

    For this, you get the height of the Text and set the height you received. This is how the code would look like

    struct ContentLengthPreference: PreferenceKey {
        static var defaultValue: CGFloat { 0 }
        
        static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
            value = nextValue()
        }
    }
    
    struct ContentView: View {
        @State private var isPopoverOpen = true
        @State var textHeight: CGFloat = 0 // <-- this
        
        var body: some View {
            Text("Hello, World!")
                .popover(isPresented: $isPopoverOpen) {
                    Text("""
                            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
                            """
                    )
                    .overlay(
                        GeometryReader { proxy in
                            Color
                                .clear
                                .preference(key: ContentLengthPreference.self,
                                            value: proxy.size.height) // <-- this
                        }
                    )
                    .onPreferenceChange(ContentLengthPreference.self) { value in // <-- this
                        DispatchQueue.main.async {
                            print (value)
                            self.textHeight = value
                        }
                    }
                    .fixedSize(horizontal: false, vertical: true)
                    .frame(height: textHeight)
                    .padding()
                    .presentationCompactAdaptation(.none)
                    
                }
        }
    }
    

    Popover with Long Text Long text

    Popover with Short Text Short text

    Happy Coding!