Reputation: 457
I have a List View with 1) a header view, 2) dynamic ForEach views, and 3) a footer view. My issue is that the first row, in which the header view lies, won't resise to fit its contents. The code for the main view is below:
var body: some View {
List {
GeometryReader { geometry in
self.headerView(size: geometry.size)
}
ForEach(self.user.posts, id: \.self) { post in
Text(post.title)
}
Text("No more posts...")
.font(.footnote)
}
.edgesIgnoringSafeArea(.all)
}
This is the view which I am trying to achieve:
This is what I have so far...:
If it's any consolidation, the header view displays fine if it's outside of the list, however, that's not the view I'm looking for.
Thanks in advance.
P.S: Apologies for the huge images, I'm not sure how to make them appear as thumbnails...
Upvotes: 4
Views: 4456
Reputation: 1475
I created a simple list view that change it's height automatically with the help of "SwiftUIIntrospect" pod.
The pod:
pod 'SwiftUIIntrospect'
ListDynamicHeight code:
import SwiftUI
import SwiftUIIntrospect
struct ListDynamicHeight<Content>:View where Content:View {
@State private var listContentHeight:CGFloat = .zero
/// minHeight must be >= 1
var minHeight:CGFloat = 1
@ViewBuilder var content:Content
var body: some View {
List {
content
}
.scrollView { scroll in
DispatchQueue.main.async {
listContentHeight = scroll.contentSize.height
}
}
.frame(minHeight: minHeight, idealHeight: listContentHeight)
}
}
private extension View {
func scrollView(_ scrollView: @escaping (UIScrollView) -> ()) -> some View {
introspect(.scrollView, on: .iOS(.v15, .v16, .v17)) { scroll in
scrollView(scroll)
}
}
}
#Preview {
ListDynamicHeight(minHeight: 10) {
Text("Hello")
}
}
And there is also a Dynamic TextView I created (with placeholder if you like) that works perfectly with the list:
struct TextView: View {
@FocusState private var isInFocus:Bool
@Binding var text: String
var placeholder = ""
var shouldShowPlaceholder:Bool { text.isEmpty && !isInFocus }
var body: some View {
ZStack(alignment: .topLeading) {
if shouldShowPlaceholder {
Text(placeholder)
.padding(.top, 10)
.padding(.leading, 6)
.onTapGesture {
isInFocus = true
}
}
TextEditor(text: $text)
.colorMultiply(shouldShowPlaceholder ? .clear : .white)
.focused($isInFocus)
}
}
}
Put everything together:
struct ListAndTextView:View {
@State private var text = ""
var placeholder:String = "Your Comment here..."
var body: some View {
ListDynamicHeight {
TextView(text: $text, placeholder: placeholder)
}
}
}
#Preview {
ListAndTextView()
}
Upvotes: 0
Reputation: 257493
I prefer to use Sections
for similar purposes (sections allow to have different configuration of each), like
var body: some View {
List {
Section {
// << header view is here with own size
}
.listRowInsets(EdgeInsets()) // << to zero padding
Section { // << dynamic view is here with own settings
ForEach(self.user.posts, id: \.self) { post in
Text(post.title)
}
}
Section { // footer view is here with own size
Text("No more posts...")
.font(.footnote)
}
}
.edgesIgnoringSafeArea(.all)
}
Upvotes: 3
Reputation: 17534
Using GeometryReader is fine, but it should be used the proper way!
import SwiftUI
struct ContentView: View {
var body: some View {
// to get the size of view, we are going to use the width later
GeometryReader { p in
List {
GeometryReader { _p in
// put your image or whatever ...
// and set the frame width
Color.red.frame(width: p.size.width, alignment: .center)
}
// and finally fix the height !! to work as expected
.frame(height: 100)
Text("By by, World!")
}
}
}
}
Upvotes: 2