SwiftUI中键盘弹出时ScrollViewReader未上移,消息被覆盖如何解决?
解决键盘弹出遮挡消息列表的问题
问题说明
点击输入框开始输入时,键盘带动画弹出,包含输入框和发送按钮的HStack会向上偏移,但此时键盘与上移后的HStack会遮挡底部的消息列表,需要让消息列表同步向上偏移,避免内容被遮挡。
解决步骤
- 添加键盘高度状态变量:存储当前键盘高度,用于动态调整布局
- 监听键盘通知:通过系统通知获取键盘显示/隐藏时的高度变化,配合动画更新状态
- 动态调整内边距:给包含消息列表和输入栏的容器设置动态底部内边距,值与键盘高度绑定,让整体内容随键盘弹出而上移
- 优化滚动行为:键盘高度变化时自动滚动到最新消息,保证用户能看到底部对话内容
修改后的完整代码
import SwiftUI struct ContentView: View { @State var messages: [Message] = [ Message(text: "Hello!", isMe: false), Message(text: "Hi there!", isMe: true), Message(text: "How are you?", isMe: false), Message(text: "I'm good, thanks. How about you?", isMe: true), Message(text: "I'm doing well too, thanks for asking.", isMe: false) ] @State var newMessage: String = "" @State private var keyboardHeight: CGFloat = 0 // 存储键盘高度 var body: some View { VStack { ScrollViewReader { scrollView in ScrollView { VStack(alignment: .leading, spacing: 10) { ForEach(messages) { message in MessageView(message: message) .id(message.id) } } .onChange(of: messages) { _ in withAnimation { scrollView.scrollTo(messages.last?.id) } } .onChange(of: keyboardHeight) { _ in // 键盘高度变化时滚动到最新消息 withAnimation { scrollView.scrollTo(messages.last?.id) } } } .onAppear { scrollView.scrollTo(messages.last?.id) // 注册键盘显示/隐藏通知 NotificationCenter.default.addObserver( forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main ) { notification in let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero withAnimation { keyboardHeight = frame.height } } NotificationCenter.default.addObserver( forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main ) { _ in withAnimation { keyboardHeight = 0 } } } .onDisappear { // 移除通知监听,避免内存泄漏 NotificationCenter.default.removeObserver(self) } .gesture( TapGesture() .onEnded { _ in UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } ) } HStack { TextField("Type a message...", text: $newMessage) .textFieldStyle(RoundedBorderTextFieldStyle()) Button(action: sendMessage) { Text("Send") } } .padding() } .padding(.bottom, keyboardHeight) // 动态绑定底部内边距 .edgesIgnoringSafeArea(.bottom) // 忽略底部安全区,让输入栏紧贴键盘 } func sendMessage() { guard !newMessage.trimmingCharacters(in: .whitespaces).isEmpty else { return } messages.append(Message(text: newMessage, isMe: true)) newMessage = "" // 发送后自动收起键盘 UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } } struct Message: Identifiable, Equatable { let id = UUID() let text: String let isMe: Bool } struct MessageView: View { let message: Message var body: some View { HStack { if message.isMe { Spacer() } Text(message.text) .padding(10) .background(message.isMe ? Color.blue : Color.gray) .foregroundColor(.white) .cornerRadius(10) if !message.isMe { Spacer() } } .padding(message.isMe ? .leading : .trailing, 50) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
代码说明
- 键盘监听逻辑:通过系统通知捕获键盘高度变化,用动画同步更新状态变量,保证界面过渡流畅
- 动态布局调整:容器的底部内边距与键盘高度绑定,键盘弹出时自动增加内边距,推动整个内容区上移,避免遮挡
- 滚动优化:在键盘状态变化和新消息添加时,自动滚动到最新消息,确保用户始终能看到对话底部
- 输入体验优化:发送消息时过滤空内容,同时自动收起键盘,提升交互流畅度
- 内存管理:视图消失时移除通知监听,防止内存泄漏
内容的提问来源于stack exchange,提问作者Presley
相关产品推荐
相关产品推荐

