iOS 27.1 커스텀 탭바를 당장 버릴 필요는 없다

이경규·2026년 9월 12일

iOS 27.1 커스텀 탭바를 당장 버릴 필요는 없다

iOS 27.1과 iPhone Duo 시대, 오래된 Navigation을 어떻게 가져갈 것인가

iPhone Duo가 공개되면서 오래 운영한 iOS 프로젝트라면 한 번쯤 확인해야 할 코드가 생겼습니다.

대표적인 것이 이런 UI입니다.

Custom Tab Bar
Custom Navigation Bar
Custom Toolbar
UIScreen.main 기반 레이아웃
Orientation 기반 분기
고정 Width / Height
Safe Area 직접 계산

최근 SwiftUI 프로젝트라면 TabView, NavigationStack, NavigationSplitView를 사용하는 경우가 많아 상대적으로 대응이 쉽습니다.

반면 몇 년 이상 운영한 UIKit 프로젝트나 디자인 요구가 강했던 앱에서는 Tab Bar와 Navigation Bar를 직접 만든 경우가 적지 않습니다.

예를 들면 이런 구조입니다.

final class MainViewController: UIViewController {

    private let contentView = UIView()
    private let customTabBar = CustomTabBar()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(contentView)
        view.addSubview(customTabBar)
    }
}

SwiftUI에서도 마찬가지입니다.

ZStack(alignment: .bottom) {
    currentContent

    CustomTabBar()
        .frame(height: 64)
}

이 코드들이 iOS 27.1에서 갑자기 사용할 수 없게 되는 것은 아닙니다.

커스텀 Tab Bar와 Navigation은 여전히 사용할 수 있습니다.

다만 iPhone Duo가 들어오면서 한 가지 차이가 커졌습니다.

시스템 Navigation을 사용하면 Apple이 새로운 화면 형태에 맞춰 상당 부분을 처리해주지만,

Custom Navigation은 그 책임이 앱으로 넘어옵니다.


먼저 결론부터 보면

앞으로의 대응은 크게 세 가지로 나눌 수 있습니다.

현재

Custom Tab / Navigation
        ↓

1단계
Custom UI 유지
+ Safe Area
+ Reserved Region
+ Adaptive Layout
        ↓

2단계
Navigation State와
UI 표현 분리
        ↓

3단계
UITabBarController / TabView
NavigationSplitView
System Toolbar
        ↓

Duo / Sidebar / Vertical Bar
자동 대응 범위 확대

즉 오래된 프로젝트를 한 번에 전부 다시 만들 필요는 없습니다.

Custom UI를 먼저 안전하게 만든 뒤, Navigation Container부터 단계적으로 시스템 쪽으로 이동하는 방식이 현실적입니다.


왜 이번에는 System Navigation이 더 중요해졌을까

기존 iPhone은 화면 형태가 비교적 예측 가능했습니다.

물론 기기 크기는 계속 달라졌지만 기본적인 구조는 비슷했습니다.

┌──────────────────┐
│ Navigation Bar   │
│                  │
│                  │
│     Content      │
│                  │
│                  │
│ Tab Bar          │
└──────────────────┘

그래서 앱에서 Tab Bar를 직접 그려도 큰 문제가 없었습니다.

하지만 Duo에서는 화면이 펼쳐지면 이야기가 달라집니다.

Closed

┌──────────────┐
│              │
│   Content    │
│              │
│              │
│   Tab Bar    │
└──────────────┘

펼친 내부 화면에서는 사용 가능한 공간 자체가 크게 달라집니다.

Opened

┌───────────┬────────────────────┐
│           │                    │
│ Sidebar   │      Content       │
│           │                    │
│           │                    │
└───────────┴────────────────────┘

여기에 화면의 pose와 방향에 따라 System Bar가 아래쪽이 아니라 옆으로 이동할 수도 있습니다.

Apple은 Duo에서 Navigation, Toolbar, Tab Bar를 Vertical Axis로 배치할 수 있도록 시스템 UI를 바꿨습니다.

이제 화면 아래에 있던 컨트롤이 옆으로 이동할 수 있습니다.

기존

Navigation
────────────

Content

────────────
Tab Bar

에서

Duo

┌────┬───────────────────────┐
│Nav │                       │
│    │                       │
│Tool│       Content         │
│    │                       │
│Tab │                       │
└────┴───────────────────────┘

같은 형태가 가능해집니다.

시스템 Navigation을 사용한다면 이 이동을 Framework가 처리합니다.

하지만 직접 만든 Tab Bar라면 시스템이 그 내부 구조를 알지 못합니다.


Custom UITabBar를 만들었다고 시스템 Tab Bar가 되는 것은 아니다

UIKit에서 특히 주의할 부분입니다.

예를 들어 직접

let tabBar = UITabBar()

를 만들었다고 해서 Duo의 새로운 Adaptive Navigation이 자동 적용되는 것은 아닙니다.

Apple은 Duo의 Vertical Bar 대응에서 UINavigationControllerUITabBarController 같은 Navigation Container가 관리하는 Bar를 사용할 것을 권장하고 있습니다.

UITabBar
UINavigationBar
UIToolbar

인스턴스를 직접 배치한 것과

UITabBarController
UINavigationController

가 관리하는 Bar는 다르게 봐야 합니다.

전자는 Custom UI에 가깝습니다.

후자는 시스템 Navigation 구조입니다.


그렇다고 Custom Tab Bar를 바로 없앨 필요는 없다

오래된 앱에서 Tab Bar를 시스템 Tab Bar로 교체하는 것은 생각보다 큰 작업일 수 있습니다.

커스텀 Tab Bar 안에

Animation
Badge
Floating Button
Custom Transition
Analytics
Deep Link
Selection State
Authentication Check
Special Gesture

같은 로직이 들어가 있기 때문입니다.

그래서 첫 단계는 교체가 아니라 Custom UI를 Adaptive하게 만드는 것입니다.

iOS 27.1에서는 이 작업을 위한 새로운 개념이 추가됐습니다.

Reserved Region

SwiftUI에서는 ReservedRegion,

UIKit에서는 UIViewReservedRegion을 사용할 수 있습니다.

Reserved Region은 쉽게 말하면

이 영역에는 앱의 Custom UI가 있으니 시스템 UI와 충돌하지 않도록 서로 공간을 조정하자.

라는 정보를 Layout System에 전달하는 방식입니다.

특히

Custom Tab Bar
Custom Floating Controls
Edge-to-edge UI
Custom Navigation

같은 화면에서 유용합니다.

예전에는 직접

Safe Area
+
Magic Number
+
Tab Bar Height
+
Device별 보정값

을 계산했다면,

iOS 27.1에서는 시스템이 알고 있는 Reserved Region 정보를 이용할 수 있습니다.

Custom UI를 당장 제거하기 어려운 앱이라면 첫 번째 마이그레이션 포인트가 여기입니다.


Safe Area 계산부터 다시 확인해야 한다

Duo에서 가장 쉽게 문제가 생길 수 있는 코드 중 하나가 이겁니다.

let width =
    view.bounds.width
    - view.safeAreaInsets.left * 2

이 코드는 암묵적으로

left == right

라고 가정하고 있습니다.

기존 iPhone에서는 우연히 잘 동작했을 수 있습니다.

Duo에서는 Safe Area와 Layout Margin이 좌우 대칭이라는 보장이 없습니다.

따라서 이렇게 계산하는 편이 안전합니다.

let availableBounds =
    view.bounds.inset(by: view.safeAreaInsets)

let width = availableBounds.width

이 차이는 작아 보이지만 앞으로 중요합니다.

❌ left inset × 2

⭕ left / right 각각 사용

Duo에서는 Camera와 Vertical System Controls, Split View 상태 등에 따라 사용 가능한 영역이 달라질 수 있기 때문입니다.


UIScreen.main도 이제 정말 줄여야 한다

오래된 프로젝트를 검색하면 이런 코드가 상당히 많이 나옵니다.

UIScreen.main.bounds.width
UIScreen.main.bounds.height
UIScreen.main.scale

Duo는 Display가 하나라는 가정을 깨는 기기입니다.

Apple 역시 Two-display Device에서는 main screen이라는 개념이 모호해지기 때문에 화면 정보를 Layout 판단에 직접 사용하는 방식을 피하도록 안내하고 있습니다.

예를 들어 Scale만 필요하다면

let scale = traitCollection.displayScale

처럼 현재 환경에서 가져오는 편이 낫습니다.

Layout은

UIScreen

보다

Environment
Trait Collection
Scene Bounds
Window

를 기준으로 판단해야 합니다.


Orientation 분기도 다시 봐야 한다

다음 코드 역시 오래된 프로젝트에서 흔합니다.

if orientation.isLandscape {
    showWideLayout()
} else {
    showCompactLayout()
}

Duo 내부 화면에서는 이런 접근이 더 이상 좋은 기준이 아닙니다.

Apple은 Duo 내부 Display가 기존 supportedInterfaceOrientations의 가정을 그대로 따르지 않기 때문에 Layout 결정에는 Size Class를 사용하라고 안내합니다.

SwiftUI라면

@Environment(\.horizontalSizeClass)
private var horizontalSizeClass

UIKit이라면

traitCollection.horizontalSizeClass

를 사용합니다.

Portrait / Landscape

보다

Compact / Regular

그리고 궁극적으로는

현재 사용할 수 있는 공간

이 중요해집니다.


Duo 내부 화면은 Sidebar를 쓰기 좋은 환경이다

iPhone Duo 내부 화면은 horizontal과 vertical 모두 Regular Size Class를 제공합니다.

그래서 iPhone에서도 Sidebar Navigation을 본격적으로 사용할 수 있습니다.

SwiftUI에서는

TabView {
    Tab("Home", systemImage: "house") {
        HomeView()
    }

    Tab("Search", systemImage: "magnifyingglass") {
        SearchView()
    }

    Tab("Settings", systemImage: "gear") {
        SettingsView()
    }
}
.defaultTabBarPlacement(.sidebar)

처럼 Sidebar 배치를 선택할 수 있습니다.

UIKit에서는

tabBarController
    .sidebar
    .preferredPlacement = .sidebar

를 사용할 수 있습니다.

다만 Sidebar를 요청했다고 항상 Sidebar가 나타나는 것은 아닙니다.

현재 환경에서 Sidebar를 보여줄 공간이 있는지는 시스템이 판단합니다.

UIKit에서는

let available =
    tabBarController.sidebar.isAvailable

로 확인할 수 있습니다.


결국 같은 Navigation이 화면에 따라 달라질 수 있다

이제 하나의 앱에서

Compact

┌──────────────────┐
│                  │
│     Content      │
│                  │
├──────────────────┤
│ Home Search Me   │
└──────────────────┘

Regular

┌─────────┬─────────────────────┐
│ Home    │                     │
│ Search  │      Content        │
│ Profile │                     │
│ Setting │                     │
└─────────┴─────────────────────┘

로 바뀔 수 있습니다.

중요한 것은 Tab의 역할이 달라지는 것이 아니라 표현 방식만 달라진다는 점입니다.

그래서 앞으로 Navigation Architecture에서 중요한 것은

Tab Bar View

자체가 아니라

Navigation State

입니다.


커스텀 탭바 마이그레이션은 State부터 분리하면 쉽다

기존 Custom Tab Bar가 이런 식으로 돼 있다고 해보겠습니다.

struct CustomTabBar: View {

    @State
    private var selectedIndex = 0

    // UI + Navigation Logic
}

이 구조에서는 Tab Bar UI와 Navigation State가 붙어 있습니다.

시스템 TabView로 바꾸려면 전체 구조를 같이 건드려야 합니다.

먼저 Selection을 외부로 빼는 것이 좋습니다.

enum MainTab: Hashable {
    case home
    case search
    case profile
    case settings
}

그리고 상태를 별도로 관리합니다.

@Observable
final class MainNavigationState {

    var selectedTab: MainTab = .home
}

Custom Tab Bar에서는

MainNavigationState
        ↓
CustomTabBar

를 사용하고,

나중에는 같은 State를

MainNavigationState
        ↓
TabView

가 사용하도록 바꿉니다.

Before

Custom Tab
   │
   ├─ UI
   └─ Navigation State

After

Navigation State
      │
 ┌────┴─────┐
 │          │
Custom    System
Tab       TabView

로 바꾸는 것입니다.

이렇게 하면 한 번에 UI 전체를 교체하지 않아도 됩니다.


첫 번째 마이그레이션은 Navigation Container부터

오래된 UIKit 프로젝트라면 가장 현실적인 순서는 이렇습니다.

현재 구조가

RootViewController

├─ Content
└─ CustomTabBar

라면,

먼저

UITabBarController
        ↓
각 화면의 UINavigationController

구조로 Navigation Container를 바꿉니다.

디자인 때문에 Custom UI가 필요하다면 처음에는 일부 Appearance나 Content를 유지해도 됩니다.

핵심은 Navigation Ownership을 시스템에 돌려주는 것입니다.

이렇게 해두면 이후 Duo의

Vertical Bar
Sidebar
Overflow
Adaptive Navigation

같은 기능을 단계적으로 사용할 수 있습니다.


Navigation Bar도 View가 아니라 Item 중심으로 옮긴다

커스텀 Navigation Bar가 이런 구조라면

HStack {
    BackButton()
    Spacer()
    Text(title)
    Spacer()
    MoreButton()
}

장기적으로는 Action을 시스템 Toolbar Item으로 옮기는 것이 좋습니다.

SwiftUI에서는

.toolbar {
    ToolbarItem(placement: .cancellationAction) {
        Button("Back") {
            dismiss()
        }
    }

    ToolbarItem(placement: .topBarTrailing) {
        Button("More", systemImage: "ellipsis") {
            showMenu()
        }
    }
}

같은 구조입니다.

UIKit에서도

navigationItem
UIBarButtonItem

쪽으로 Action을 이동합니다.

이렇게 해야 시스템이

Horizontal Bar

↓

Vertical Bar

로 이동할 때 각 Action의 의미를 알 수 있습니다.


Duo에서는 Toolbar가 정말 세로가 된다

이 부분은 단순 Sidebar보다 더 중요한 변화일 수 있습니다.

Duo의 넓은 화면에서는 Navigation과 Toolbar Item이 옆쪽 Shared Bar Region에 배치될 수 있습니다.

대략 이런 느낌입니다.

┌──────┬───────────────────────┐
│  ←   │                       │
│      │                       │
│ Done │                       │
│      │       Content         │
│  +   │                       │
│      │                       │
│ ...  │                       │
└──────┴───────────────────────┘

그래서 Toolbar Item도 세로 배치를 고려해야 합니다.

Apple은 가능한 경우 Symbol 중심의 Toolbar Item을 권장합니다.

긴 텍스트 버튼은 Vertical Bar와 잘 맞지 않기 때문입니다.


Custom Toolbar Item도 버릴 필요는 없다

System Toolbar 안에 Custom View를 넣는 것도 여전히 가능합니다.

예를 들어

.toolbar {
    ToolbarItem {
        ProfileView()
            .axisBehavior(.verticalPreferred)
    }
}

처럼 Vertical Representation을 지원한다고 알려줄 수 있습니다.

반대로 세로 UI로 만들기 어려운 컨트롤은

.axisBehavior(.horizontalOnly)

로 유지할 수 있습니다.

UIKit의 UIBarButtonItem에도 같은 방향의 axisBehavior가 제공됩니다.

즉 선택지가

System UI
or
Custom UI

두 개뿐인 것이 아닙니다.

가장 현실적인 형태는

System Container
       +
Custom Content

입니다.


이것이 기존 Custom UI의 좋은 마이그레이션 지점이다

예전에는

Custom Navigation Bar
    ├─ Back
    ├─ Title
    ├─ Profile View
    └─ Custom Action

전체를 직접 만들었다면,

앞으로는

UINavigationController
        │
        ├─ System Back
        ├─ System Toolbar Item
        ├─ Custom Profile View
        └─ Custom Action View

처럼 바꿀 수 있습니다.

디자인은 상당 부분 유지하면서도 Navigation Context를 시스템에 돌려주는 방식입니다.

이렇게 해야 Apple이 새로운 Form Factor를 만들 때 대응 비용이 줄어듭니다.


Toolbar가 세로로 갔는지도 확인할 수 있다

Custom Content가 Vertical Bar에서 Layout을 바꿔야 한다면 현재 Bar 위치를 확인할 수도 있습니다.

SwiftUI에서는

@Environment(\.toolbarVerticalEdge)
private var verticalEdge

UIKit에서는

traitCollection.verticalBarEdge

를 사용할 수 있습니다.

예를 들어 같은 Profile UI라도

Horizontal

[ Photo  Name ]

에서

Vertical

[Photo]

처럼 바꿀 수 있습니다.

시스템 Container 안에서 Custom View를 사용하면서도 새로운 Duo UI에 적응할 수 있는 방법입니다.


Overflow도 직접 만들지 않는 쪽이 좋아진다

오래된 Custom Toolbar를 보면 흔히

버튼을 직접 만들고 Menu를 띄우는 코드가 있습니다.

Duo에서는 Bar 공간이 화면 상태에 따라 계속 달라질 수 있습니다.

따라서 어떤 Action이 Overflow로 이동해야 하는지를 직접 계산하기보다 시스템에 맡기는 편이 낫습니다.

SwiftUI에는

ToolbarOverflowMenu {
    Button("Scan") {
        scan()
    }

    Button("Connect") {
        connect()
    }
}

같은 방식이 있습니다.

중요한 Action에는

.visibilityPriority(.high)

를 줄 수도 있습니다.

즉 앞으로 Toolbar 설계는

어디에 놓을 것인가

보다

무엇이 반드시 보여야 하는가

를 시스템에 알려주는 방향으로 바뀝니다.


그래도 Vertical Bar가 맞지 않는 앱도 있다

모든 앱이 Sidebar나 Vertical Toolbar를 사용해야 하는 것은 아닙니다.

예를 들어 Calculator처럼

화면 전체가 하나의 작업 공간
+
하단 Control이 중요한 앱

이라면 Vertical Bar가 오히려 Layout을 방해할 수도 있습니다.

SwiftUI에서는

.toolbarVerticalBehavior(.disabled)

로 Vertical Bar를 끌 수 있습니다.

UIKit에서도 View Controller의

override var preferredVerticalBarBehavior:
    UIVerticalBarBehavior {
    .disabled
}

처럼 Opt-out이 가능합니다.

중요한 것은 새 UI를 무조건 사용하는 것이 아닙니다.

시스템의 Adaptive Behavior를 이해한 뒤 앱에 맞게 선택하는 것입니다.


Resizable 대응은 Duo만 위한 작업이 아니다

Duo 때문에 Resizable이라는 단어가 더 눈에 띄지만 사실 이 문제는 이미 시작됐습니다.

iPhone 앱이

iPhone

iPhone Mirroring

iPad에서 실행

Duo

Split View

등 다양한 공간에서 실행될 수 있기 때문입니다.

그래서 이제

if UIDevice.current.userInterfaceIdiom == .phone

만으로 Layout을 판단하는 것은 부족합니다.

같은 .phone 환경에서도 사용 가능한 Width는 크게 달라질 수 있습니다.

앞으로는

Device Type

이 아니라

Available Space

를 봐야 합니다.


Resizable 대응에서 가장 먼저 없앨 코드

기존 프로젝트에서 아래 코드를 검색해보면 좋습니다.

UIScreen.main
UIDevice.current.userInterfaceIdiom
interfaceOrientation

그리고

.frame(width: 390)

같은 고정 Layout도 확인해야 합니다.

물론 모두 잘못된 코드는 아닙니다.

문제는 Layout 결정의 핵심 기준으로 사용했을 때입니다.

대신

Size Class
Container Size
Safe Area
Trait Collection
Scene

를 사용하도록 조금씩 옮기는 편이 좋습니다.


NavigationSplitView도 이제 iPad 전용 API처럼 보면 안 된다

예전에는

NavigationSplitView

를 보면 사실상

iPad / macOS

용이라고 생각하기 쉬웠습니다.

Duo에서는 상황이 달라졌습니다.

내부 화면은 충분한 공간을 제공하기 때문에

Sidebar
+
Detail

구조가 자연스럽습니다.

SwiftUI라면

NavigationSplitView {
    List {
        NavigationLink(
            "Home",
            value: Destination.home
        )

        NavigationLink(
            "Projects",
            value: Destination.projects
        )
    }
} detail: {
    DetailView()
}

같은 구조를 iPhone에서도 고려할 수 있습니다.

외부 화면에서는 Single Column으로 Collapse되고,

넓은 환경에서는 Split Navigation으로 확장됩니다.


결국 UI가 아니라 Information Architecture까지 바뀐다

Duo 대응을 단순히

390pt 화면

↓

큰 화면

으로 보면 부족합니다.

기존 iPhone에서는 화면 하단에

Home
Search
Profile
Settings

정도만 보여줬다면,

Sidebar에서는

Home

Projects
 ├─ Active
 ├─ Recent
 └─ Archived

Favorites

Team
 ├─ Members
 └─ Shared

Settings

같이 더 많은 Navigation Hierarchy를 보여줄 수 있습니다.

즉 문제는 Layout Resize만이 아닙니다.

화면이 넓어졌을 때 Information Architecture 자체를 확장할 것인지를 생각해야 합니다.


기존 앱이라면 이렇게 마이그레이션하는 게 현실적이다

한 번에 SwiftUI로 다시 만드는 방식은 대부분 현실적이지 않습니다.

다음 정도의 순서가 안전합니다.

1단계 — Custom UI를 Duo에서도 깨지지 않게 만든다

UIScreen.main 제거
Orientation 의존 제거
Safe Area 좌우 독립 처리
고정 Width 제거
Reserved Region 대응

이 단계에서는 기존 디자인을 유지합니다.


2단계 — Navigation State를 UI에서 분리한다

Selected Tab
Navigation Path
Deep Link
Presentation State

를 Custom View 내부에서 빼냅니다.

Navigation State
        ↓
Custom UI

구조로 만듭니다.


3단계 — System Container로 이동한다

UIKit이라면

UITabBarController
UINavigationController
UISplitViewController

SwiftUI라면

TabView
NavigationStack
NavigationSplitView

로 Container부터 교체합니다.


4단계 — Custom Control은 Toolbar Item으로 옮긴다

전체 Custom Navigation Bar를 유지하기보다 필요한 Custom UI만 남깁니다.

System Navigation
       +
Custom ToolbarItem

구조입니다.


5단계 — Sidebar와 Vertical Bar를 활성화한다

Duo나 Regular Width 환경에서

.defaultTabBarPlacement(.sidebar)

또는

tabBarController
    .sidebar
    .preferredPlacement = .sidebar

를 적용합니다.

Toolbar Item도 Vertical Axis에서 정상적으로 보이는지 확인합니다.


Xcode 27.1에서는 DeviceHub로 반드시 확인하는 게 좋다

Duo는 Simulator를 한 번 띄우는 것으로 테스트가 끝나지 않습니다.

확인해야 할 상태가 많습니다.

Outer Display

Inner Display

Fully Open

Partially Folded

Portrait

Landscape

Split View

Xcode 27.1의 DeviceHub에서는 Duo를 열고 닫고, 회전하고, Fold 상태를 바꾸면서 Layout을 확인할 수 있습니다.

Apple은 기존 App Modernization Skill도 Xcode 27.1에서 App Resizability라는 이름으로 확장했습니다.

SwiftUI와 iPhone Duo까지 분석 대상으로 포함됩니다.

오래된 프로젝트라면 이런 자동 분석을 이용해

Fixed Layout
Screen Assumption
Adaptive Layout 문제

를 먼저 찾는 것도 좋은 시작점입니다.


지금 프로젝트에서 체크할 것

Duo 대응을 준비한다면 최소한 다음은 확인할 필요가 있습니다.

□ UIScreen.main으로 Layout을 계산하는가?

□ interfaceOrientation으로 UI 구조를 결정하는가?

□ Safe Area의 left/right가 같다고 가정하는가?

□ Custom Tab Bar가 화면 아래에 고정되어 있는가?

□ Custom Navigation Bar가 Safe Area를 직접 계산하는가?

□ Tab 선택 State가 Custom Tab Bar 내부에 묶여 있는가?

□ NavigationStack / UINavigationController로 옮길 수 있는가?

□ Regular Width에서 Sidebar가 더 자연스럽지 않은가?

□ Toolbar Custom View가 Vertical Axis에서도 사용할 수 있는가?

□ Overflow Menu를 직접 구현하고 있는가?

□ Duo Split View에서도 UI가 깨지지 않는가?

여기에서 많이 걸린다고 해서 앱을 전부 다시 만들어야 한다는 의미는 아닙니다.

오히려 마이그레이션 우선순위를 잡을 수 있다는 의미입니다.


커스텀 UI가 문제라기보다 시스템과 단절된 UI가 문제다

이번 변화에서 가장 중요한 부분은 이것입니다.

Custom UI 자체가 나쁜 것은 아닙니다.

브랜드 경험이나 복잡한 Interaction 때문에 Custom Control이 반드시 필요한 앱도 있습니다.

문제가 되는 것은

Custom UI
+
Device 가정
+
Magic Number
+
직접 Safe Area 계산
+
Navigation State까지 결합

된 구조입니다.

반대로

System Navigation Container
        +
Adaptive Layout
        +
Custom Content

구조라면 Custom UI를 상당히 많이 유지하면서도 새로운 플랫폼 변화에 대응할 수 있습니다.


마치며

iOS 27.1과 iPhone Duo가 나왔다고 해서 오래된 Custom Tab Bar와 Navigation을 당장 전부 버릴 필요는 없습니다.

Apple도 ReservedRegionUIViewReservedRegion을 추가하면서 Custom UI가 새로운 화면 환경에서 시스템 UI와 공존할 수 있는 방법을 제공하고 있습니다.

다만 앞으로의 방향은 분명합니다.

예전에는 앱이

화면 크기
Tab Bar 위치
Navigation 위치
Safe Area

를 상당 부분 직접 결정할 수 있었습니다.

앞으로는

System Container
       ↓
Adaptive Navigation
       ↓
현재 사용 가능한 공간
       ↓
App Content

구조가 훨씬 유리해집니다.

특히 Duo에서는

Bottom Tab Bar
        ↓
Sidebar

Horizontal Toolbar
        ↓
Vertical Toolbar

Single Column
        ↓
Split View

처럼 같은 앱의 Navigation 표현 자체가 바뀔 수 있습니다.

이걸 Custom UI로 전부 직접 구현하는 것도 가능합니다.

하지만 계속 직접 따라가는 비용은 점점 커집니다.

그래서 오래된 프로젝트라면 가장 현실적인 전략은 하나입니다.

디자인을 전부 버리는 것이 아니라 Navigation의 책임부터 시스템에 조금씩 돌려주는 것.

Custom Tab Bar를 유지해야 한다면 먼저 Resizable과 Reserved Region에 대응하고,

그다음 Navigation State를 분리하고,

마지막으로 TabView, UITabBarController, NavigationSplitView 같은 System Container로 이동하면 됩니다.

iPhone Duo가 던지는 질문도 결국 폴더블 대응 하나로 끝나지 않습니다.

다음에 또 다른 화면 형태가 나와도 지금의 Navigation 구조가 그대로 살아남을 수 있는가.

이번 iOS 27.1은 오래된 Navigation 코드를 한 번 점검하기 좋은 시점입니다.

참고자료

  • Apple Developer — Prepare your app for iPhone Duo
    iPhone Duo의 Resizable Layout, Size Class, Safe Area, UIScreen.main 의존 문제, Sidebar와 표준 Navigation의 Adaptive 동작, Xcode 27.1 DeviceHub 테스트 방법 등을 확인할 수 있습니다.
    Prepare your app for iPhone Duo

  • Apple Developer — Raise the bar with iPhone Duo
    Duo의 Vertical Bar, Toolbar Item, Custom View, axisBehavior, Overflow, Visibility Priority 등 새로운 Navigation Bar 동작을 확인할 수 있습니다.
    Raise the bar with iPhone Duo

Apple 공식 자료 기준으로 보면, iOS 27.1 SDK에서는 표준 Navigation과 Toolbar가 iPhone Duo의 새로운 화면 구조에 맞춰 더 적극적으로 적응합니다.

NavigationSplitView, UISplitViewController, TabView, UITabBarController 같은 System Navigation Container를 사용하면 Duo의 화면 상태와 사용할 수 있는 공간에 따라 Navigation 표현을 변경하기가 훨씬 수월합니다.

내부 화면은 horizontal·vertical Size Class가 모두 regular이기 때문에 Sidebar를 사용할 수 있는 공간도 확보됩니다.

반대로 기존 앱에서 직접 만든

Custom Tab Bar
Custom Navigation Bar
Custom Toolbar

는 같은 Adaptive Behavior를 자동으로 받을 수 있다고 가정하면 안 됩니다.

특히 다음 코드는 다시 확인할 필요가 있습니다.

UIScreen.main
interfaceOrientation
.frame(width: 390)

그리고

left Safe Area == right Safe Area

같은 가정도 Duo에서는 안전하지 않습니다.

iOS 27.1의 ReservedRegionUIViewReservedRegion은 Custom Bar나 edge-to-edge UI를 당장 제거하기 어려운 앱에서 시스템 UI와 충돌하지 않도록 만드는 중요한 중간 대응책입니다.

또한 Apple은 Vertical Bar 내부에서도 Custom View를 사용할 수 있도록 axisBehavior, Vertical Bar 환경 정보, Overflow와 Visibility Priority 관련 기능을 제공하고 있습니다.

따라서 현실적인 Migration 방향은

Custom UI 전부 제거

가 아니라

System Container
       +
필요한 Custom Content

에 가깝습니다.

기존 Custom Tab Bar와 Navigation을 유지하면서 먼저 Resizable과 Safe Area에 대응하고, Navigation State를 UI에서 분리한 뒤 TabView, UITabBarController, NavigationSplitView 같은 시스템 Container로 단계적으로 옮기는 것이 가장 현실적인 접근입니다.

profile
iOS 앱 개발자

0개의 댓글