首页 > 解决方案 > 如何使 SwiftUI 滚动视图缩小到内容?

问题描述

我有一个 SwiftUI GUI,其中有两个堆叠在一起的列表。每个列表都有可变数量的项目。我希望两个列表都缩小以适应它们的内容。

如果我在 ForEach 周围使用 VStack,那么简单的情况就可以工作(参见下面的示例)。底部附近有一个垫片,因此列表会像我预期的那样移动到窗口的顶部。

现在,有时列表很大,我想要滚动视图和最大高度,但我仍然希望列表在项目较少时缩小。但是一旦我添加了滚动视图,滚动视图就会开始占用它所能占用的所有空间。如果我给它分配一个最大值,那么它就不会缩小以适应它的内容了。

在下面的示例中(如所写),我得到了我想要的调整大小行为(没有最大大小)。如果我取消注释滚动视图,那么它会占用所有空间,如果我取消注释框架修饰符,它可以工作,但大小是固定的。

struct ContentView: View {
    @State var List1: [String] = [  ]
    @State var List2: [String] = [  ]

    var body: some View {
        VStack {
            Button("1-5") {
                List1=[ "1" ]
                List2=[ "a", "b", "c", "d", "e" ]
            }
            Button("3-3") {
                List1=[ "1", "2", "3" ]
                List2=[ "a", "b", "c" ]
            }
            Button("5-1") {
                List1=[ "1", "2", "3", "4", "5" ]
                List2=[ "a" ]
            }
            //ScrollView {
                VStack {
                    ForEach(List1.indices, id: \.self) { idx in
                        Text(List1[idx])
                    }
                }
            //}
            //.frame(maxHeight: 40)
            Text("middle")
            VStack {
                ForEach(List2.indices, id: \.self) { idx in
                    Text(List2[idx])
                }
            }
            Spacer()
            Text("last")
        }
    }
}

标签: swiftui

解决方案


您需要PreferenceKey计算ScrollView内容的大小。这里有一个getSize可以帮助你的功能:

struct SizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero

    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

struct SizeModifier: ViewModifier {
    private var sizeView: some View {
        GeometryReader { geometry in
            Color.clear.preference(key: SizePreferenceKey.self, value: geometry.size)
        }
    }

    func body(content: Content) -> some View {
        content.overlay(sizeView)
    }
}

extension View {
    func getSize(perform: @escaping (CGSize) -> ()) -> some View {
        self
            .modifier(SizeModifier())
            .onPreferenceChange(SizePreferenceKey.self) {
                perform($0)
            }
    }
}

您必须比较内容的高度(with getSize)和高度ScrollView(with GeometryReader),并相应地设置框架:

struct SwiftUIView12: View {
    @State private var items: [String] = ["One", "Two", "Three"]
    @State private var scrollViewSize: CGSize = .zero
    var body: some View {
        GeometryReader { proxy in
            ScrollView {
                ForEach(items, id: \.self) { item in
                    Text(item)
                        .padding()
                }
                .frame(maxWidth: .infinity)
                .getSize {scrollViewSize = $0}
            }
            .frame(height: scrollViewSize.height < proxy.size.height ? scrollViewSize.height : .none )
            .background(Color.blue.opacity(0.2))
        }
        .navigationTitle("Test")
        .toolbar {
            Button("Many items") {
                items = (1 ... 30).map { _ in String.random(length: 10) }
            }
        }
    }
}

在此处输入图像描述


推荐阅读