Xcode13.2中SwiftUI3.0的ScrollViewReader.scrollTo方法滚动至TextField时无法设置底部内边距问题
看起来你遇到的核心问题是**List的行布局被系统默认管理**,你在单元格内部添加的padding或者额外视图并不会改变List行本身的边界,而ScrollViewReader的scrollTo方法是基于你标记.id的视图的布局边界来计算滚动位置的——这就是为什么你加的底部内边距完全没效果。
下面给你几个针对性的解决方案,根据你的需求选择:
方案1:改用ScrollView + LazyVStack(最推荐)
List的行有很多系统自带的布局约束,如果你不需要List的额外特性(比如滑动删除、分组等),换成ScrollView配合LazyVStack可以完全自定义视图布局,你添加的底部内边距会被正确识别:
ScrollViewReader { value in ScrollView { LazyVStack { ForEach(0..<things.count, id: \.self) { index in VStack { HStack { Image(systemName: "circle") TextField( "Text Field", text: $things[index] ) .focused($focusField, equals: index) .submitLabel(.next) .onSubmit { funcThatScrollsToLastInput() } } } .padding(.bottom, 20) // 这个padding现在会完全生效 .id(index) } } } }
这样每个带.id的VStack的底部内边距会被包含在视图的整体frame里,scrollTo时会自动考虑这个间距,滚动后TextField下方自然会有20的空间。
方案2:给List行添加自定义内边距
如果你必须保留List,可以通过.listRowInsets直接调整行的内边距,让整个行的底部留出空间:
ScrollViewReader { value in VStack { List(0..<things.count, id: \.self) { index in VStack { HStack { Image(systemName: "circle") TextField( "Text Field", text: $things[index] ) .focused($focusField, equals: index) .submitLabel(.next) .onSubmit { funcThatScrollsToLastInput() } } } .id(index) .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 20, trailing: 0)) // 给行加底部内边距 } } }
这个方法会给所有List行统一添加底部内边距,如果你需要针对特定行调整,可以把listRowInsets的参数改成动态值(比如根据当前focus的index来设置)。
方案3:给标记id的视图添加底部占位
如果不想修改容器类型,也可以在你标记.id的VStack里添加一个透明的底部占位视图,让整个视图的高度包含这个占位空间:
ScrollViewReader { value in VStack { List(0..<things.count, id: \.self) { index in VStack { HStack { Image(systemName: "circle") TextField( "Text Field", text: $things[index] ) .focused($focusField, equals: index) .submitLabel(.next) .onSubmit { funcThatScrollsToLastInput() } } // 添加透明占位视图,增加整体高度 Color.clear.frame(height: 20) } .id(index) } } }
这样ScrollViewReader滚动到.id(index)的视图时,会把这个包含占位的VStack作为目标,TextField下方自然会有20的空白空间。
另外补充一点:如果你是担心键盘弹出挡住TextField,还可以配合ScrollView的.scrollDismissesKeyboard(.interactively)或者使用UIKit的键盘监听逻辑来动态调整滚动偏移,但根据你的描述,核心问题还是布局边界的问题,上面的方案应该能解决你的需求。
内容的提问来源于stack exchange,提问作者Sam Chahine

