iOS屏幕适配方案及场景案例
文档说明
本文档汇总iOS开发全场景屏幕适配方案,覆盖AutoLayout、SizeClass、百分比适配、字体/图片适配、刘海屏/灵动岛适配、多设备兼容等核心内容,搭配实战代码案例,适配iPhone全机型(SE/标准版/ProMax/Plus)、iPad全系列,解决横竖屏切换、异形屏、多分辨率适配问题,无第三方依赖,原生API实现,兼容iOS 13+主流版本。
一、iOS屏幕适配核心基础
1.1 核心概念
点(pt):iOS开发通用尺寸单位,与屏幕像素(px)无关,系统自动适配像素密度,1pt = 2px/3px(根据设备倍率)。
安全区域(Safe Area):避开刘海、状态栏、导航栏、底部指示条、键盘的可视区域,是适配异形屏的核心。
SizeClass:iOS设备屏幕尺寸分类方案,分为紧凑(Compact)、常规(Regular)两种尺寸类型,兼容iPhone/iPad横竖屏、分屏模式。
1.2 主流适配方案选型
-
AutoLayout(自动布局):苹果官方推荐,纯代码/XIB/Storyboard均可实现,适配绝大多数场景
-
SizeClass:配合AutoLayout,实现多设备、横竖屏差异化布局
-
百分比/比例适配:针对全屏布局、等比控件、轮播图等场景
-
手动计算frame:小众定制化布局、自定义UI控件场景
-
自动缩放适配:针对老项目、全屏UI快速适配
二、AutoLayout自动布局(核心方案)
2.1 纯代码实现(SnapKit/原生NSLayoutConstraint)
优先使用原生API,无第三方依赖;复杂布局推荐SnapKit简化代码,以下为两种实现方式,均贴合安全区域适配。
2.1.1 原生NSLayoutConstraint适配
import UIKit
class AutoLayoutViewController: UIViewController {
// 声明控件
private let contentView = UIView()
private let testLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.backgroundColor = .white
// 关闭AutoresizingMask
contentView.translatesAutoresizingMaskIntoConstraints = false
testLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(contentView)
contentView.addSubview(testLabel)
// 配置控件属性
testLabel.text = "iOS屏幕适配示例"
testLabel.textAlignment = .center
testLabel.backgroundColor = .lightGray
// 约束布局:贴合安全区域
NSLayoutConstraint.activate([
// contentView铺满安全区域
contentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
contentView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
// label居中,左右留白20pt,高度固定
testLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
testLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
testLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
testLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
testLabel.heightAnchor.constraint(equalToConstant: 50)
])
}
}
2.1.2 SnapKit实现(简化版)
import UIKit
import SnapKit
class SnapKitLayoutViewController: UIViewController {
private let contentView = UIView()
private let testLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.backgroundColor = .white
view.addSubview(contentView)
contentView.addSubview(testLabel)
testLabel.text = "SnapKit适配示例"
testLabel.textAlignment = .center
testLabel.backgroundColor = .lightGray
// SnapKit约束
contentView.snp.makeConstraints { make in
// 绑定安全区域
make.edges.equalTo(view.safeAreaLayoutGuide)
}
testLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
make.leading.trailing.equalToSuperview().inset(20)
make.height.equalTo(50)
}
}
}
2.2 安全区域适配要点
-
禁止控件直接贴合view边缘,必须绑定safeAreaLayoutGuide
-
刘海屏、灵动岛、底部Home指示条,均通过安全区域自动避开
-
横竖屏切换时,安全区域会自动适配,无需手动修改约束
三、SizeClass多设备差异化适配
解决iPhone竖屏、横屏,iPad全屏/分屏,不同尺寸设备的布局差异,通过监听SizeClass变化,动态调整约束。
3.1 SizeClass分类规则
-
iPhone竖屏:宽紧凑(C)、高常规(R)
-
iPhone横屏(全面屏):宽紧凑(C)、高紧凑(C)
-
iPad全场景:宽常规(R)、高常规(R)
3.2 代码实现差异化布局
import UIKit
class SizeClassViewController: UIViewController {
private let testView = UIView()
// 存储约束,方便动态修改
private var viewWidthConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
// 监听SizeClass变化
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
// 判断屏幕宽度类型
if traitCollection.horizontalSizeClass == .compact {
// 紧凑宽度:iPhone竖屏/横屏
viewWidthConstraint.constant = UIScreen.main.bounds.width - 40
} else {
// 常规宽度:iPad
viewWidthConstraint.constant = 500
}
}
private func setupUI() {
view.backgroundColor = .white
testView.translatesAutoresizingMaskIntoConstraints = false
testView.backgroundColor = .systemBlue
view.addSubview(testView)
// 初始化约束
viewWidthConstraint = testView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 40)
NSLayoutConstraint.activate([
testView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
testView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
testView.heightAnchor.constraint(equalToConstant: 200),
viewWidthConstraint
])
}
}
四、百分比/比例适配(实战场景)
针对等比例控件、全屏布局、流式布局、轮播图等场景,通过屏幕宽度/高度百分比计算尺寸,适配所有机型分辨率。
4.1 基础百分比工具类
import UIKit
// MARK: - 屏幕适配工具类
struct ScreenAdaptationUtil {
// 屏幕宽度
static let screenWidth = UIScreen.main.bounds.width
// 屏幕高度
static let screenHeight = UIScreen.main.bounds.height
// 基准宽度(以iPhone 15 Pro宽度393pt为基准)
private static let baseWidth: CGFloat = 393
/// 宽度百分比适配
static func widthPercent(_ percent: CGFloat) -> CGFloat {
return screenWidth * percent
}
/// 高度百分比适配
static func heightPercent(_ percent: CGFloat) -> CGFloat {
return screenHeight * percent
}
/// 等比缩放(基于基准宽度)
static func scale(_ value: CGFloat) -> CGFloat {
return value * (screenWidth / baseWidth)
}
}
4.2 实战场景:流式布局+百分比控件
import UIKit
class PercentLayoutViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupPercentLayout()
}
private func setupPercentLayout() {
view.backgroundColor = .white
// 一行两个控件,各占45%屏幕宽度,间距10%
let leftView = UIView(frame: CGRectMake(
ScreenAdaptationUtil.widthPercent(0.05),
ScreenAdaptationUtil.heightPercent(0.2),
ScreenAdaptationUtil.widthPercent(0.4),
ScreenAdaptationUtil.heightPercent(0.3)
))
leftView.backgroundColor = .systemRed
let rightView = UIView(frame: CGRectMake(
ScreenAdaptationUtil.widthPercent(0.55),
ScreenAdaptationUtil.heightPercent(0.2),
ScreenAdaptationUtil.widthPercent(0.4),
ScreenAdaptationUtil.heightPercent(0.3)
))
rightView.backgroundColor = .systemGreen
view.addSubview(leftView)
view.addSubview(rightView)
}
}
五、字体&图片适配
5.1 字体自动适配
支持动态字体、机型差异化字体大小,兼顾无障碍适配与多设备兼容。
import UIKit
extension UIFont {
// 自适应字体
static func adaptiveFont(size: CGFloat, weight: UIFont.Weight = .regular) -> UIFont {
// 屏幕缩放字体大小
let scaleSize = size * (UIScreen.main.bounds.width / 393)
return UIFont.systemFont(ofSize: scaleSize, weight: weight)
}
// 动态字体(支持系统字体大小设置)
static func dynamicFont(style: UIFont.TextStyle) -> UIFont {
if #available(iOS 15.0, *) {
return UIFont.preferredFont(forTextStyle: style, compatibleWith: .current)
} else {
return UIFont.preferredFont(forTextStyle: style)
}
}
}
// 使用示例
// label.font = .adaptiveFont(size: 16)
// label.font = .dynamicFont(style: .body)
5.2 图片适配
-
图片资源配置@2x/@3x图,系统自动加载对应分辨率图片
-
图片控件设置contentMode,避免拉伸变形:
// 常用图片填充模式
imageView.contentMode = .scaleAspectFit // 等比缩放,完整显示
imageView.contentMode = .scaleAspectFill // 等比缩放,填充控件,裁剪多余部分
imageView.clipsToBounds = true // 开启裁剪
六、异形屏/特殊场景适配
6.1 刘海屏/灵动岛/底部指示条适配
// 获取安全区域边距
let safeTop = UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0
let safeBottom = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
// 自定义导航栏/底部按钮,避开安全区域
customNavView.frame = CGRectMake(0, safeTop, ScreenAdaptationUtil.screenWidth, 44)
customBottomBtn.frame = CGRectMake(20, ScreenAdaptationUtil.screenHeight - safeBottom - 50, ScreenAdaptationUtil.screenWidth - 40, 50)
6.2 横竖屏切换适配
-
开启项目横竖屏权限
-
通过viewWillLayoutSubviews监听屏幕旋转,刷新约束
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// 屏幕旋转后,重新更新约束/frame
testView.snp.updateConstraints { make in
make.height.equalTo(ScreenAdaptationUtil.heightPercent(0.25))
}
}
6.3 iPad分屏/多任务适配
全程使用AutoLayout+SizeClass,避免固定frame,不写死屏幕尺寸,所有尺寸通过比例/约束实现,即可兼容iPad分屏模式。
七、常见适配避坑指南
-
✅ 优先使用安全区域,禁止直接使用view边缘约束
-
✅ 不使用固定frame,优先AutoLayout/SnapKit约束
-
✅ 字体/控件尺寸不写死数值,使用比例/自适应方法
-
✅ 横屏适配需重新计算布局,避免控件超出屏幕
-
✅ 图片避免拉伸,合理设置contentMode
-
✅ 刘海屏机型不忽略底部安全区域,防止按钮被遮挡
八、接入使用说明
-
基础布局优先使用AutoLayout+安全区域,适配绝大多数场景
-
多设备兼容引入SizeClass,实现横竖屏、iPhone/iPad差异化布局
-
流式/等比布局使用百分比适配方案,引入工具类简化代码
-
字体图片使用自适应扩展,兼顾显示效果与兼容性
-
异形屏重点处理安全区域,避开刘海、底部指示条