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

如何适配SwiftUI应用UI以兼容不同iPhone机型?

解决SwiftUI登录界面在小屏幕设备上的UI适配问题

你的UI在iPhone SE上错乱的核心原因是硬编码固定尺寸(比如输入框宽度400、按钮最小宽度360、固定高度的Spacer),这些值在小屏幕设备上会超出可视范围,同时没有正确利用SwiftUI的自适应布局和安全区机制。

关键适配改动

  • 顶部元素适配安全区:移除硬编码的顶部内边距,让元素自动从安全区顶部开始,适配刘海/非刘海设备的状态栏下方
  • 移除固定尺寸约束:将输入框、按钮的固定宽度改为自适应屏幕,用maxWidth: .infinity配合水平内边距实现响应式宽度
  • 弹性垂直布局:用Spacer()替代固定高度的间隔,让系统自动分配剩余空间,将「Continue」按钮推到屏幕底部
  • 键盘适配:利用SwiftUI的安全区机制,确保按钮在键盘弹出时始终位于键盘正上方
  • 字体大小自适应:输入框字体大小根据屏幕宽度动态调整,避免小屏幕上字体过大

修改后的完整代码

import SwiftUI

struct LoginEnterName: View {
    @State private var fullname = ""
    @FocusState private var isTextFieldFocused: Bool
    @State private var navigateToDOB = false
    
    // 动态计算输入框字体大小,适配不同屏幕
    private var textFieldFontSize: CGFloat {
        min(UIScreen.main.bounds.width * 0.1, 40)
    }
    
    var body: some View {
        LinearGradient(gradient: Gradient(colors: [Color("oasispitchblack"), Color("oasischocolate")]), startPoint: .topLeading, endPoint: .bottomTrailing)
            .preferredColorScheme(.dark)
            .edgesIgnoringSafeArea(.all)
            .overlay(
                VStack(spacing: 10) {
                    // 应用图标:自动适配安全区顶部
                    Image("Appnx_Logo")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 130, height: 50)
                    
                    // 提示文本
                    Text("Let's get you set up, what’s your name?")
                        .foregroundColor(Color("oasisyellow"))
                        .padding(15)
                    
                    // 输入框:自适应宽度+动态字体
                    TextField("Your name", text: $fullname)
                        .focused($isTextFieldFocused)
                        .foregroundColor(.white)
                        .padding()
                        .bold()
                        .multilineTextAlignment(.center)
                        .font(.system(size: textFieldFontSize))
                        .frame(maxWidth: .infinity)
                        .background(Color.clear)
                        .cornerRadius(10)
                        .padding(.horizontal, 20)
                        .padding(.bottom, 25)
                        .onAppear {
                            isTextFieldFocused = true
                        }
                    
                    // 弹性间隔:自动填充剩余空间
                    Spacer()
                    
                    // Continue按钮:自适应宽度+安全区适配
                    Button(action: {
                        navigateToDOB = true
                    }) {
                        Text("Continue")
                            .foregroundColor(.black)
                            .bold()
                            .padding()
                            .frame(maxWidth: .infinity)
                            .background(fullname.count < 2 ? Color.gray : Color.white)
                            .cornerRadius(20)
                    }
                    .disabled(fullname.count < 2)
                    .padding(.horizontal, 20)
                    // 确保按钮在键盘弹出时位于安全区底部
                    .safeAreaInset(edge: .bottom) {
                        Color.clear.frame(height: 0)
                    }
                }
                .padding(20)
                .vAlign(.top)
            )
            .fullScreenCover(isPresented: $navigateToDOB) {
                LoginEnterDOB()
            }
    }
}

struct LoginEnterName_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            LoginEnterName()
                .previewDevice(PreviewDevice(rawValue: "iPhone 14"))
            LoginEnterName()
                .previewDevice(PreviewDevice(rawValue: "iPhone SE (3rd generation)"))
        }
    }
}

extension View{
    func hAlign(_ alignment: Alignment)->some View{
        self.frame(maxWidth: .infinity, alignment: alignment)
    }
    
    func vAlign(_ alignment: Alignment)->some View{
        self.frame(maxHeight: .infinity, alignment: alignment)
    }
    
    func performAction() {
        // 执行你的操作
    }
    
    func capitalizeFirstLetterOfEachWord(_ text: String) -> String {
        let words = text.components(separatedBy: " ")
        let capitalizedWords = words.map { word -> String in
            guard let firstLetter = word.first else { return word }
            let restOfString = word.dropFirst()
            return String(firstLetter).capitalized + restOfString
        }
        return capitalizedWords.joined(separator: " ")
    }
}

改动细节说明

  1. 安全区适配:移除图标上的固定顶部内边距,SwiftUI会自动识别设备的安全区,刘海设备留出状态栏空间,iPhone SE这类非刘海设备对齐时间栏下方。
  2. 响应式宽度:输入框和按钮使用maxWidth: .infinity,配合水平内边距,确保在所有屏幕宽度下都能完整显示,不会超出边界。
  3. 弹性布局:Spacer()会自动填充顶部元素和按钮之间的剩余空间,不管屏幕高度如何,都能把按钮固定在屏幕底部,键盘弹出时系统会自动调整布局,让按钮位于键盘上方。
  4. 动态字体:通过屏幕宽度的比例计算输入框字体大小,最大限制为40,避免小屏幕上字体过大导致内容被截断。
  5. 按钮状态优化:简化了按钮的背景逻辑,移除重复的.background(Color.white),直接根据输入长度切换背景色,逻辑更清晰。

内容的提问来源于stack exchange,提问作者Venkat Ravuri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 06:45:05