SwiftUI中如何阻止打开键盘时视图内容上移?
SwiftUI 键盘弹出时阻止视图上移的解决方案
问题描述
我希望打开键盘时,SwiftUI视图的内容不会上移。我的视图包含较多内容,键盘弹出时会将顶部内容推出屏幕,需要键盘开合时视图保持原有状态。
我尝试过忽略键盘安全区域的方案,但在小屏幕设备上无效,仅当视图内容较少时才起作用。
我的代码
struct ContentView34: View { @State var phoneNumber = "" var body: some View { ZStack{ VStack{ Image("homeIcon") .resizable() .frame(width:Constants.width*0.6,height: Constants.width*0.5) .padding() Text("Enter your phone number") .font(.custom("Inter-Medium", size: 20)) .fontWeight(.medium) Text("Sign in with Apple id or Gmail") .foregroundColor(.gray) .font(.custom("Inter-Regular", size: 15)) .font(.caption) .padding(.top,0.2) ZStack{ RoundedRectangle(cornerRadius: 5) .stroke(Color("blue")) .frame(width: 90,height:50) Text("Mobile") .foregroundColor(.blue) .font(.caption2) .padding(.horizontal,10) .background(Color.white) .frame(width: 90,height:70,alignment: .topLeading) HStack{ Group{ Image("flag") Text("+44") .font(.custom("Inter-Medium", size: 15)) Image(systemName: "arrowtriangle.down.fill") .resizable() .foregroundColor(.blue) .frame(width: 10, height: 6) .padding(0) } Divider() .frame(width:2 ,height: 25) .background(Color.gray.opacity(0.5)) TextField("Enter phone number", text: $phoneNumber) .font(.custom("Inter-Medium", size: 15)) .padding() .foregroundColor(Color("dayNightText")) .keyboardType(.numberPad) Spacer() } .frame(width: Constants.width*0.9,height: 50) .padding(.leading) } .padding(.vertical) ZStack{ VStack{ Button { } label: { HStack{ Spacer() Text("Sign in with Apple") .font(.custom("Inter-Medium", size: 14)) Spacer() } .frame(width: Constants.width * 0.6, height: 45) .foregroundColor( .white) .background(Color.black) .cornerRadius(5) .overlay( Image("apple") .resizable() .renderingMode(.template) .foregroundColor(.white) .aspectRatio(contentMode: .fit) .frame(width:25,height:25) .frame(width: 50,height:45,alignment: .leading) ) } .padding(.vertical) Button { } label: { HStack{ Spacer() Text("Sign in with Google") .font(.custom("Inter-Medium", size: 14)) Spacer() } .frame(width: 60,height:45) .foregroundColor(.white) .background(Color("blue")) .cornerRadius(5) .overlay( Image("google") .resizable() .aspectRatio(contentMode: .fit) .frame(width:25,height:25) .frame(width: 50,height:45,alignment: .leading) ) } } Text("Testing1") Text("Testing1") Text("Testing1") Spacer() } .navigationBarHidden(true) .padding() .background(Color.white) } .ignoresSafeArea(.keyboard, edges: .bottom) } } } struct ContentView34_Previews: PreviewProvider { static var previews: some View { ContentView34() } }
键盘关闭时
键盘打开时顶部图片被截断
解决方案
要彻底解决小屏幕设备上键盘弹出导致顶部内容截断的问题,核心是阻止SwiftUI因键盘弹出而调整视图布局,同时确保内容区域高度固定。以下是修改后的代码及关键说明:
修改后的代码
struct ContentView34: View { @State var phoneNumber = "" var body: some View { ZStack{ GeometryReader { geo in ScrollView { VStack{ Image("homeIcon") .resizable() .frame(width: geo.size.width*0.6, height: geo.size.width*0.5) .padding() Text("Enter your phone number") .font(.custom("Inter-Medium", size: 20)) .fontWeight(.medium) Text("Sign in with Apple id or Gmail") .foregroundColor(.gray) .font(.custom("Inter-Regular", size: 15)) .font(.caption) .padding(.top, 0.2) // 手机号输入区域 ZStack{ RoundedRectangle(cornerRadius: 5) .stroke(Color("blue")) .frame(width: 90, height: 50) Text("Mobile") .foregroundColor(.blue) .font(.caption2) .padding(.horizontal, 10) .background(Color.white) .frame(width: 90, height: 70, alignment: .topLeading) HStack{ Group{ Image("flag") Text("+44") .font(.custom("Inter-Medium", size: 15)) Image(systemName: "arrowtriangle.down.fill") .resizable() .foregroundColor(.blue) .frame(width: 10, height: 6) } Divider() .frame(width: 2, height: 25) .background(Color.gray.opacity(0.5)) TextField("Enter phone number", text: $phoneNumber) .font(.custom("Inter-Medium", size: 15)) .padding() .foregroundColor(Color("dayNightText")) .keyboardType(.numberPad) Spacer() } .frame(width: geo.size.width*0.9, height: 50) .padding(.leading) } .padding(.vertical) // 登录按钮及测试文本区域 VStack{ Button { // 登录逻辑 } label: { HStack{ Spacer() Text("Sign in with Apple") .font(.custom("Inter-Medium", size: 14)) Spacer() } .frame(width: geo.size.width * 0.6, height: 45) .foregroundColor(.white) .background(Color.black) .cornerRadius(5) .overlay( Image("apple") .resizable() .renderingMode(.template) .foregroundColor(.white) .aspectRatio(contentMode: .fit) .frame(width:25, height:25) .frame(width: 50, height:45, alignment: .leading) ) } .padding(.vertical) Button { // 登录逻辑 } label: { HStack{ Spacer() Text("Sign in with Google") .font(.custom("Inter-Medium", size: 14)) Spacer() } .frame(width: geo.size.width * 0.6, height: 45) .foregroundColor(.white) .background(Color("blue")) .cornerRadius(5) .overlay( Image("google") .resizable() .aspectRatio(contentMode: .fit) .frame(width:25, height:25) .frame(width: 50, height:45, alignment: .leading) ) } Text("Testing1") Text("Testing1") Text("Testing1") Spacer() } .padding() .background(Color.white) } .frame(minHeight: geo.size.height) } .scrollDismissesKeyboard(.never) .ignoresSafeArea(.keyboard) } } .navigationBarHidden(true) } }
关键修改说明
- GeometryReader 获取屏幕尺寸:替代硬编码的
Constants.width,确保布局适配所有屏幕尺寸,包括小屏设备。 - ScrollView 包裹内容:将整个VStack放入ScrollView,同时设置:
.scrollDismissesKeyboard(.never):禁止滚动视图时自动收起键盘.ignoresSafeArea(.keyboard):让ScrollView完全忽略键盘弹出带来的安全区域变化,避免视图上移
- 固定内容区域高度:给VStack添加
.frame(minHeight: geo.size.height),确保内容区域高度始终等于屏幕高度,不会因键盘弹出而重新调整布局。 - 修复原布局错误:移除了嵌套错误的ZStack(导致测试文本叠加),修正了Google按钮的宽度设置,避免按钮显示异常。
这样修改后,无论屏幕大小、内容多少,键盘弹出时视图都能保持原有状态,顶部内容不会被截断。
内容的提问来源于stack exchange,提问作者Iftikhar Hussain
相关产品推荐
相关产品推荐

