You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
    }
}

关键修改说明

  1. GeometryReader 获取屏幕尺寸:替代硬编码的Constants.width,确保布局适配所有屏幕尺寸,包括小屏设备。
  2. ScrollView 包裹内容:将整个VStack放入ScrollView,同时设置:
    • .scrollDismissesKeyboard(.never):禁止滚动视图时自动收起键盘
    • .ignoresSafeArea(.keyboard):让ScrollView完全忽略键盘弹出带来的安全区域变化,避免视图上移
  3. 固定内容区域高度:给VStack添加.frame(minHeight: geo.size.height),确保内容区域高度始终等于屏幕高度,不会因键盘弹出而重新调整布局。
  4. 修复原布局错误:移除了嵌套错误的ZStack(导致测试文本叠加),修正了Google按钮的宽度设置,避免按钮显示异常。

这样修改后,无论屏幕大小、内容多少,键盘弹出时视图都能保持原有状态,顶部内容不会被截断。


内容的提问来源于stack exchange,提问作者Iftikhar Hussain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 23:54:54