Reputation: 145
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:
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:
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.
Upvotes: 5
Views: 2822
Reputation: 384
I had the same issue. Multiline text inside a popover wasn't being sized correctly. The above solution worked for me, so I turned Md. Ibrahim Hassan's answer into a SwiftUI extension
private struct PopoverMultilineHeightFix: ViewModifier {
@State var textHeight: CGFloat = 0
func body(content: Content) -> some View {
content
.fixedSize(horizontal: false, vertical: true)
.overlay(
GeometryReader { proxy in
Color
.clear
.preference(key: ContentLengthPreference.self,
value: proxy.size.height)
}
)
.onPreferenceChange(ContentLengthPreference.self) { value in
DispatchQueue.main.async {
self.textHeight = value
}
}
.frame(height: self.textHeight)
}
}
private extension PopoverMultilineHeightFix {
struct ContentLengthPreference: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
}
extension View {
func popoverMultilineHeightFix() -> some View {
modifier(PopoverMultilineHeightFix())
}
}
Just call it like this:
Text("This is some really long text to test the line wrapping to make suire the popover handles it properly")
.multilineTextAlignment(.leading)
.popoverMultilineHeightFix()
Upvotes: 1
Reputation: 704
How do you like this?
You can negotiate size using Layout protocol.
import SwiftUI
struct PopoverContainer: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
guard subviews.count == 1 else {
fatalError("You need to implement your layout!")
}
var p = proposal
p.width = p.width ?? UIScreen.main.bounds.width
p.height = p.height ?? UIScreen.main.bounds.height
return subviews[0].sizeThatFits(p) // negotiates possible size
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
// entrusts default
}
}
struct ContentView: View {
@State private var isPopoverOpen = true
var body: some View {
Text("Hello, World!")
.popover(isPresented: $isPopoverOpen) {
PopoverContainer {
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.
"""
)
.fixedSize(horizontal: false, vertical: true)
.padding()
}
.presentationCompactAdaptation(.none)
}
}
}
Upvotes: 5
Reputation: 5477
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)
}
}
}
Happy Coding!
Upvotes: 5