iOS 15/Xcode 13环境下如何让ScrollView在键盘弹出时保持当前滚动位置?
解决iOS15下ScrollView键盘弹出时保持滚动位置的问题
嘿,我刚好在iOS15/Xcode13环境下做过类似的需求,完全懂你想要的Messenger那种键盘弹出时滚动位置丝毫不乱的效果!默认情况下系统会自动上移ScrollView内容导致位置偏移,咱们手动接管这个逻辑就能实现,直接看修改后的代码和解释:
修改后的完整代码
import SwiftUI struct TestKeyboardScrollView2: View { @State var textfield: String = "" // 保存当前滚动到的行索引 @State private var currentScrollIndex: Int? = nil // 记录键盘高度,用来调整内容内边距 @State private var keyboardHeight: CGFloat = 0 var body: some View { VStack { ScrollViewReader { proxy in ScrollView { LazyVStack { ForEach(1...100, id: \.self) { index in Text("Row \(index)") .id(index) // 给每一行设置唯一标识,方便定位 .onAppear { // 当行出现在屏幕上时,更新当前滚动的索引 currentScrollIndex = index } } } // 底部内边距随键盘高度变化,避免内容被键盘遮挡 .padding(.bottom, keyboardHeight) } .onChange(of: keyboardHeight) { newHeight in // 键盘弹出时,滚动回之前记录的位置 if let index = currentScrollIndex, newHeight > 0 { withAnimation(.interactiveSpring()) { proxy.scrollTo(index, anchor: .top) } } } } TextField("Write here...", text: $textfield) .textFieldStyle(.roundedBorder) } .padding() .onAppear { // 监听键盘弹出通知 NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { notification in guard let userInfo = notification.userInfo, let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } // 计算实际键盘高度(减去底部安全区域,避免多算刘海屏的底部间距) keyboardHeight = keyboardFrame.height - (UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0) } // 监听键盘收起通知 NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in keyboardHeight = 0 } } .onDisappear { // 页面消失时移除通知监听,避免内存泄漏 NotificationCenter.default.removeObserver(self) } } }
核心逻辑解释
- ScrollViewReader的作用:它是iOS14+引入的组件,能让我们手动控制ScrollView的滚动位置,这里用来在键盘弹出时精准定位回之前的行。
- 记录当前滚动位置:通过给每一行添加
onAppear,实时更新currentScrollIndex,这样我们就能知道当前屏幕上显示的是哪一行。 - 键盘通知监听:获取键盘的高度,动态调整ScrollView底部的内边距,既避免内容被键盘挡住,又能触发滚动恢复的逻辑。
- 平滑滚动恢复:用
withAnimation配合scrollTo方法,让滚动恢复的动画和系统键盘弹出的动画保持一致,体验更自然。
测试的时候你滚动到第100行,点击TextField弹出键盘,ScrollView会稳稳停在第100行,完全符合你想要的Messenger交互效果~
内容的提问来源于stack exchange,提问作者Flincorp
相关产品推荐
相关产品推荐

