iOS 개발을 하다보면 화면 상단이나 하단에 영역이 있잖아요? 그 부분을 Safe Area라고 하는데 이번에는 Safe Area에 대해 간단하게 알아볼게요
iOS에서 화면을 만들 때 가장 자주 겪는 문제 중 하나는 “내 UI가 왜 위아래에 가려지지?”입니다. 이걸 해결하기 위해 Apple이 도입한 개념이 Safe Area에요. Safe Area는 노치, 상태바, 홈 인디케이터, 탭바/네비게이션바 같은 시스템 UI를 피해서 콘텐츠가 안전하게 보일 수 있는 영역을 의미해요
iOS 10 이하에서는 topLayoutGuide, bottomLayoutGuide 같은 방식으로 직접 대응해야 했습니다. 하지만 기기 화면 형태가 다양해지고(특히 iPhone X 이후), 노치/라운드 코너/홈 인디케이터가 등장하면서 기기마다 다르게 잘리는 문제가 커졌습니다
그래서 iOS 11부터 Safe Area가 공식 표준이 되었어요
import UIKit
final class SafeAreaViewController: UIViewController {
// 화면 상단에 보여줄 제목 라벨
private let titleLabel = UILabel()
// 화면 하단에 고정할 버튼
private let bottomButton = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
// 기본 배경색
view.backgroundColor = .systemBackground
// 라벨 텍스트/스타일 설정
titleLabel.text = "Safe Area"
titleLabel.font = .boldSystemFont(ofSize: 24)
// Auto Layout을 쓰기 위해 autoresizing mask 변환 비활성화
titleLabel.translatesAutoresizingMaskIntoConstraints = false
// 버튼 텍스트 설정
bottomButton.setTitle("계속", for: .normal)
bottomButton.translatesAutoresizingMaskIntoConstraints = false
// 뷰 계층에 추가
view.addSubview(titleLabel)
view.addSubview(bottomButton)
// 제약 설정 시작
NSLayoutConstraint.activate([
// titleLabel의 top을 Safe Area top에 붙임
// -> 노치/상태바 영역과 겹치지 않게 안전하게 배치
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
// 왼쪽 여백 20
titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
// 버튼 좌우 여백 20
bottomButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
bottomButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
// 버튼 bottom을 Safe Area bottom에 붙임
// -> 홈 인디케이터(하단 바)와 겹치지 않게 배치
bottomButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16),
// 버튼 높이 고정
bottomButton.heightAnchor.constraint(equalToConstant: 48)
])
}
// Safe Area가 바뀔 때마다 호출됨
// (예: 회전, 통화/핫스팟 상태바 변화 등)
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
// 현재 Safe Area 여백(top/left/bottom/right) 확인 가능
// 디버깅할 때 유용
print("SafeArea Insets:", view.safeAreaInsets)
}
}
핵심만 말하면
4-1. 배경만 Safe Area 무시
struct SafeAreaExampleView: View {
var body: some View {
ZStack {
LinearGradient(
colors: [.blue, .mint],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea() // 배경만 무시
VStack(spacing: 16) {
Text("콘텐츠는 Safe Area를 지킵니다")
.font(.title2.bold())
Button("확인") {}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
}
4-2. 하단 고정 버튼을 안전하게 배치
struct SafeAreaInsetView: View {
var body: some View {
ScrollView {
Text("긴 본문...")
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
.safeAreaInset(edge: .bottom) {
Button("다음") {}
.frame(maxWidth: .infinity)
.padding()
.background(.ultraThinMaterial)
}
}
}
핵심 원칙은 ignoresSafeArea()는 화면 전체에 무조건 적용하지 말고, 배경 레이어처럼 필요한 뷰에만 국소적으로 적용하는 거에요