SwiftUI列表行不会自动调整大小以适应内容

4

我有一个列表视图,其中包含 1) 页眉视图,2) 动态 ForEach 视图和 3) 页脚视图。我的问题是第一行(也就是包含页眉视图的那一行)无法自适应其内容大小。以下是主视图的代码:

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)
}

这是我想要实现的视图:

mockup

目前我已经做到了这个程度...

actual hierarchy

如果有安慰的话,页眉视图在列表之外显示正常,但那不是我要找的视图。

提前致谢。

P.S:很抱歉图片太大了,我不知道如何将它们显示为缩略图...


你为什么忽略了安全区域? - Julian Silvestri
@JulianSilvestri 我希望列表能够延伸到屏幕的边缘,就像模型一样。 - JacobCXDev
我并没有太多使用SwiftUI的经验,但我认为你不应该忽略安全区域...除非你另有规定,否则列表将始终扩展到屏幕边缘。 - Julian Silvestri
@JulianSilvestri 忽略安全区域是必需的,以便列表视图矩形扩展到屏幕边缘——从状态栏上方到底部栏下方。标题必须包含在列表中的原因是它可以与其他内容一起滚动。如果我要在UIKit中实现这个功能,我会简单地使用一个UITableView,并将第一个单元格的内容设置为标题视图。 - JacobCXDev
@JulianSilvestri 没错。想想 Twitter 或 Facebook... - JacobCXDev
显示剩余2条评论
3个回答

3
我更喜欢使用“Sections”来实现类似的目的(每个部分都可以有不同的配置),例如:enter image description here
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)
}

问题是,行中仍然会有一些左填充...这就是为什么我在List行内使用第二个GeomertryReader的原因,即使它的代理值根本没有被使用。请查看我的答案并自行测试。 - user3441734
感谢.listRowInsets(EdgeInsets())!!! 我以前从未使用过... - user3441734
谢谢Asperi,这太棒了。我尝试了许多方法来自动缩小列表高度,当行高是动态的(未知)时,没有一个方法有效。这个方法很好地解决了这个问题。 - kwiknik

2
使用GeometryReader是可以的,但必须使用正确的方式!
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!")
            }
        }
    }
}

是的,我曾经遇到过类似的问题,只需要像你在这里做的那样将GeometryReader移出List即可。之后,我使用geometry.size作为列表中视图的输入,这些视图使用var size: CGSize进行初始化。 - lmh

0
我创建了一个简单的列表视图,借助于"SwiftUIIntrospect"库,它可以自动改变高度。
这个库:
  pod 'SwiftUIIntrospect'

ListDynamicHeight 代码:
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")
    }
}

还有一个我创建的动态TextView(如果你喜欢的话,可以带有占位符),它与列表完美配合。
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)
        }
    }
}

把所有东西放在一起:
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()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接