如何让展开后超出屏幕的DisclosureGroup自动上滚?
解决SwiftUI中DisclosureGroup展开时自动滚动到内容的问题
你遇到的核心问题是Form基于List实现,自带滚动容器,直接在外层套ScrollView会破坏原有格式,且无法关联内部滚动行为。正确做法是将ScrollViewReader直接包裹Form,监听内层DisclosureGroup的展开状态触发滚动。
修改后的完整代码
struct TestView: View { @State private var sheetIsPresented: Bool = false @State private var outerDisclosureIsExpanded: Bool = false @State private var innerDisclosureIsExpanded: Bool = false var body: some View { Button { self.sheetIsPresented = true } label: { Label("Add Item", systemImage: "plus") } .sheet(isPresented: self.$sheetIsPresented) { VStack { // 用ScrollViewReader包裹Form,关联内部滚动容器 ScrollViewReader { proxy in Form { Section(header: Text("Section")) { DisclosureGroup("Outer Disclosure Group", isExpanded: self.$outerDisclosureIsExpanded) { ForEach(1...20, id: \.self) { index in Text("Value \(index)") } DisclosureGroup("Inner Disclosure Group", isExpanded: self.$innerDisclosureIsExpanded) { VStack { Text("Text") Button("Button") { print("Button Pressed") } .id("innerButton") // 给目标元素设置唯一ID } } } } } .onChange(of: innerDisclosureIsExpanded) { newValue in // 内层展开时触发滚动 if newValue { withAnimation { proxy.scrollTo("innerButton", anchor: .bottom) } } } } } } } }
关键修改说明
- ScrollViewReader的位置:直接包裹Form,确保代理能正确控制Form内部的List滚动容器,不会破坏原生样式。
- 监听展开状态:通过
onChange仅在内层DisclosureGroup从折叠变为展开时触发滚动,避免无效操作。 - 锚点选择:使用
.bottom锚点可确保内层内容的底部(按钮)完全显示在屏幕内,避免被安全区遮挡;若需滚动到内层顶部,可将ID设置在内层VStack上,锚点改为.top。 - 目标ID设置:给需要滚动到的元素设置唯一ID,保证滚动目标精准。
内容的提问来源于stack exchange,提问作者Ser Pounce
相关产品推荐
相关产品推荐

