Community생산성 & 협업github.com

apple-design-duo

iPhone Duo(Apple의 접이식 듀얼 디스플레이 iPhone)용 iOS 앱, 게임 또는 목업을 디자인, 구축 또는 검토할 때나 외부/내부 디스플레이, 힌지, 접힘, 기기 포즈, 예약 영역, 배치 뷰, 세로 축(사이드바)의 툴바 및 탭 바, 접기 및 크기 조정 전환을 유연하고 정교하게 처리해야 할 때 사용합니다.

apple-design-duo란 무엇인가요?

이 스킬은 Apple의 차세대 폴더블 기기인 iPhone Duo를 위한 앱 디자인 가이드라인을 제공합니다. Apple Human Interface Guidelines의 'Designing for iPhone Duo' 페이지를 기반으로 하며, iOS 27.1 SDK에 도입된 `ArrangementView`, 수직 막대 API, 예약 영역 API 등 Duo 관련 핵심 기술을 상세히 설명합니다. 주요 원칙은 외부 디스플레이(컴팩트 너비)와 내부 디스플레이(레귤러 너비) 간에 유연하게 크기가 조절되는 단일 레이아웃을 설계하는 것입니다. 스킬은 SwiftUI와 UIKit 모두를 지원하며, 레이아웃 체크리스트, 게임 최적화, 접힘 애니메이션, 접근성 설정 등 실무에 필요한 모든 내용을 다룹니다. 특히 Emil Kowalski의 `apple-design` 스킬과 함께 사용하면 더욱 효과적입니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Andrewi89/Stride-skills/tree/HEAD/plugins/design/skills/apple-design-duo

Installed? Explore more 생산성 & 협업 skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

iPhone Duo 디자인하기

개요

출처: Apple Human Interface Guidelines, "Designing for iPhone Duo" (신규 페이지, 2026년 9월 9일). https://developer.apple.com/design/human-interface-guidelines/designing-for-iphone-duo

핵심 원칙: 두 개의 디스플레이와 다양한 포즈에서 유연하게 크기가 조절되는 하나의 앱. 포즈별로 레이아웃을 디자인하지 말고, 크기 클래스(컴팩트 너비 = 외부 디스플레이, 레귤러 너비 = 내부 디스플레이)를 사용하고 표준 시스템 컴포넌트가 자동으로 적응하도록 하세요. 모든 일반 iOS HIG는 여전히 적용됩니다.

정확한 문구나 규칙에 대한 개발자 API가 필요할 때는 위 페이지를 참조하세요.

하드웨어 한 문장 요약

외부 디스플레이(닫힌 상태): 다른 iPhone보다 더 넓고 짧으며, 카메라는 모서리에 고정되어 항상 보입니다. 내부 디스플레이(열린 상태): 카메라는 활성화되기 전까지 디스플레이 뒤에 숨겨져 있습니다. 중앙 힌지는 책 스타일, 평평하게, 가장자리에 세우는 포즈를 지원합니다. 툴바, 탭 바, 상태 막대 및 Dynamic Island는 세로 방향의 내부 디스플레이를 제외한 모든 곳에서 측면(세로 축)에 위치합니다.

빠른 참조 체크리스트

레이아웃

  • 크기 조절에 맞게 구축: 크기 클래스, 레이아웃 여백, 안전 영역 인셋. 고정 너비 없음, 하나의 디스플레이에 종속되지 않음.
  • 두 디스플레이와 모든 포즈에서 동일한 기능 및 상태 유지. 내부 디스플레이는 계층 구조를 한 단계 더 추가할 수 있음(Mail: 닫혔을 때 목록 또는 메시지, 열렸을 때 나란히 표시).
  • 예약 영역 준수: 외부 카메라(항상, Dynamic Island로 확장), 내부 카메라(활성화된 경우에만), 접힘 영역(부분적으로 열린 경우에만). 사용자 정의 컴포넌트에는 ReservedRegion(SwiftUI) / UIView.ReservedRegion(UIKit)을 사용하세요.
  • 접힐 때: 자체 적응 컨테이너 선호(분할 보기는 창 너비를 균등하게 함), 짝수 개의 그리드 열 사용, 작은 조정, 급격한 재배치 금지.
  • 분할 보기: 내부에서 확장, 외부에서 하나의 창으로 축소(자동).
  • 배치 뷰(ArrangementView / UIArrangementViewController): 기본 + 보조의 두 뷰. 분할(나란히 또는 쌓기, HStack/VStack 유사 레이아웃에서) 또는 오버레이(계층형, ZStack 유사 레이아웃에서). 네비게이션 컨테이너는 외부에 유지.

세로 컨트롤

  • 시스템의 측면 배치 유지, 일반적으로 재정의하지 않음.
  • 콘텐츠 영역은 비대칭: 안전 영역을 사용하여 측면 컨트롤(분할 보기의 다른 앱 포함)이 콘텐츠를 가리지 않도록 함. RTL 언어에서도 컨트롤은 동일한 측면에 유지.
  • 툴바 순서(위에서 아래로): 기본 탐색(Back, Close), 그 다음 주요 작업(Done), 나머지 그룹은 그대로.
  • 오버플로는 기본적으로 아래에서 위로 진행됨; 가시성 우선 순위 설정(ToolbarItemVisibilityPriority / UIBarButtonItemVisibilityPriority)으로 자주 사용하는 작업(Compose, New Note)과 배지 항목이 계속 표시되도록 함.
  • 항목을 ToolbarItemGroup / UIBarButtonItemGroup으로 그룹화; 수동 고정 간격을 추가하지 마세요.
  • 모든 비텍스트 툴바 항목에는 제목과 심볼이 모두 있어야 합니다. 텍스트 전용 버튼(가로 막대에 유지)은 최소화하세요.
  • 공간이 부족할 때: 탐색 중심 뷰는 툴바를 오버플로로 압축(기본값); 작업 중심 뷰는 대신 탭 바를 최소화(ToolbarVerticalCompressionBehavior / UIVerticalBarCompressionBehavior).
  • 시스템 오버플로 메뉴 사용; 줄임표는 오버플로 전용.
  • 끝이 아닌 창에 속한 컨트롤은 해당 창 옆에 유지(Mail 목록 컨트롤은 목록 위에 유지).
  • 몰입형, 비스크롤 UI는 Dynamic Island 또는 상태 막대와 충돌하지 않는 경우 전체 너비를 사용할 수 있음(계산기는 5열 x 4행).

게임

  • 모든 포즈에서 플레이 가능. 방향 잠금은 괜찮지만, 화면을 채우세요. 텍스트와 컨트롤 크기는 일관되게 유지. 레터박스/필러박스보다는 화면비 변경 선호; 불가피할 경우 패딩을 아트워크로 채우세요.

Apple 기술 강연에서 발췌

HIG 페이지에 명시되지 않은 세 가지 Duo 기술 강연의 세부 내용 (111466 Design for iPhone Duo, 111462 Raise the bar with iPhone Duo, 111463 Strike a pose with adaptive layouts).

디자인 시 고려해야 할 시스템 동작

  • 외부 디스플레이: 컨트롤은 오른손 엄지 도달을 위해 오른쪽에 위치. Dynamic Island는 Live Activity가 도착하면 세로로 확장되며 앱 바와 사이드 스트립을 공유. 공간이 부족하면 앱 항목이 자동으로 오버플로로 축소.
  • 부분 접힘(책): 텍스트, 이미지 및 대화형 요소가 곡선에서 멀어짐. 세로 접힘에서 대화형 요소는 아래쪽 절반으로 이동.
  • 분할 보기 멀티태스킹: 홈 제스처를 사용하여 앱을 측면으로 드래그하면 50/50 균등 분할. 각 앱의 컨트롤은 외부 가장자리에 위치.
  • PiP(Picture in Picture)는 상단에 고정 가능; 아래 앱은 세로로 크기가 조절되고, 접히면 비디오가 화면 절반을 채움.
  • 시트: 외부 디스플레이에서 시트 툴바는 세로로 표시. 내부 디스플레이에서 시트는 가로 막대와 함께 중앙에 위치. 접힐 때 시트는 접힘 부분에서 미끄러져 나옴. preferredPlacement를 사용하면 왼쪽에 배치된 시트에는 세로 막대가 없고 오른쪽에 배치된 시트에는 세로 막대가 생김.
  • 내장 접힘 회피(시트, 알림, 메뉴, 툴바 버튼 등)는 대화형 요소를 접힘선에서 밀어냄. 스크롤 가능한 콘텐츠는 접힘을 피할 필요가 없습니다.

세로 막대 선택하기

  1. 최신 SDK로 다시 빌드.
  2. 네비게이션 컨테이너가 소유한 막대 사용: SwiftUI NavigationStack, NavigationSplitView 또는 TabView의 .toolbar; UIKit 항목은 UINavigationController/UITabBarController 내의 뷰 컨트롤러에 설정. 사용자 정의 UIToolbar/UINavigationBar/UITabBar 콘텐츠는 이동되지 않습니다.
  • 디스플레이 가장자리에 있는 컨테이너만 세로로 이동. 분할 보기에서는 세부 열만 이동; 검사기에는 자체 세로 막대가 없음.
  • 매핑: 상단 툴바 항목은 스트립 상단으로, 하단 항목은 하단으로 이동하며, 탭 바는 하단 정렬 유지. 유연한 스페이서는 높이가 0; 고정 스페이서는 크기 유지.
  • Back/Close: 네비게이션 컨트롤러로 자동 처리. 사용자 정의 항목은 .cancellationAction(SwiftUI) 또는 leftItemSupplementsBackButton = false(UIKit)가 있는 선행 항목을 사용. 주요 작업은 topBarPinnedTrailing(SwiftUI) 또는 pinnedTrailingGroup(UIKit)을 사용.

세로로 가는 것, 가로로 남는 것

  • 세로 막대는 고정 너비와 유연한 높이를 가지므로 심볼 전용 항목에 적합. 아이콘이 있는 항목은 세로로 이동; 텍스트 전용 항목(Edit, 세그먼트 컨트롤, 넓은 텍스트 버튼)은 가로 탐색 막대에 유지.
  • 새로운 AxisBehavior API로 재정의: 심볼과 텍스트 사이를 전환하는 항목(사용자 정의 Select, 시스템 Edit 버튼은 자체 처리)에는 가로 전용 사용, 세로 형태를 가진 사용자 정의 뷰(예: 나침반)에는 세로 선호 사용.
  • 텍스트 및 심볼 항목을 배지(iOS 26 배지 API)가 있는 심볼 전용 항목으로 전환(예: 받은 편지함 개수). 실제 정보를 전달하는 경우 텍스트 유지(예: 장바구니 총액).
  • 키보드 액세서리 막대는 키보드에 고정된 상태 유지.
  • 사용자 정의 뷰: 고정 너비에 맞추거나 세로 레이아웃 제공. toolbarVerticalEdge(환경 값 또는 특성) 읽기, 막대를 세로로 배치할 수 없으면 nil.
  • 세로 막대는 기본적으로 스크롤 가장자리 효과가 없습니다. 투명도 감소가 켜져 있을 때만 배경을 얻으므로 사용자 정의 콘텐츠는 어떤 경우든 읽기 쉬워야 함.
  • 선택 해제(Calculator와 같은 단일 페이지, 하단 중심 앱 또는 Close 버튼만 있는 시트)하려면 toolbarVerticalBehavior / preferredVerticalBarBehavior를 사용. 세로 막대가 없는 시트는 카메라 바로 앞에서 멈춤.
  • 뷰별 압축: toolbarCompressionBehavior. 가시성 우선 순위는 높음, 낮음 또는 사용자 정의 가능; 그룹별로 먼저 설정한 다음 항목별로 설정.

레이아웃 패턴

  • 외부 디스플레이: 대부분의 콘텐츠는 컨트롤 뒤에 숨겨지지 않도록 오프셋이 필요하며, 가로 안전 영역 인셋에 정렬하면 자동으로 처리됨. 몰입형, 비스크롤 UI에서만 전체 디스플레이 중앙에 배치(막대 아래에 대화형 요소가 없는 경우). 혼합 접근 방식은 전체 너비 배경에 인셋 스크롤 콘텐츠를 사용하고 모든 대화형 요소를 인셋 영역 내에 유지하는 것.
  • 내부 디스플레이, 단순히 늘어난 폰 앱이 되지 않도록: 분할 보기, 넓을 때 2열이 되는 스택 레이아웃(Music), 또는 정보 밀도가 높은 앱의 경우 탭 바를 사이드바로 표시(Health)를 사용.
  • 선택적 테이블(노트북) 포즈 레이아웃: 미디어는 상단, 컨트롤은 안정적인 하단 절반. 모든 컨트롤과 동일한 계층 구조를 유지해야 함.
  • 변위(Displacement): 예약 영역 밖으로 요소를 목적에 맞는 영역으로 이동. 범위 선택: 단독 요소는 단독으로 이동, 관련 요소는 함께 이동, 요소를 원본에서 멀리 이동하지 마세요. 책 포즈에서 알림은 뒤쪽(닫힐 때 다시 나타날 위치 근처)으로 이동. 테이블 포즈에서 glanceable 콘텐츠는 상단, 탭 가능한 컨트롤은 하단. 검색은 검색하는 뷰 위에 유지. 그리드의 경우 외부 여백을 유지하고 힌지에서 거터를 넓힘(Fitness).
  • 시스템 분할 보기는 접힐 때 50/50으로 균등해짐(Reminders).

예약 영역 API

  • SwiftUI: reservedRegion on GeometryProxy (GeometryReader 또는 onGeometryChange에서). UIKit: reservedRegion on UIView. 레이아웃에는 .frame 사용.
  • 두 가지 종류가 있습니다. 분할 영역(접힘)은 접혔을 때만 활성화되고 평평할 때는 너비가 0입니다. 가림 영역(내부 FaceTime 카메라)은 카메라가 켜져 있을 때만 활성화됩니다.
  • 활성 영역은 기본적으로 반환됨; includeInactive를 사용하여 사전 계획(예: 분할 영역이 있을 때마다 짝수 열 개수 선택).
  • 카메라 중심 앱의 경우 핵심 콘텐츠와 컨트롤을 내부 카메라에서 멀리 유지.
  • 상호작용 입력으로서의 접힘 상태는 Leverage multiple displays and scenes on iPhone Duo 강연에서 다루며, 이 스킬에 포함되지 않음.

배치 뷰 (iOS 27.1)

  • SwiftUI: NavigationStack 내부의 ArrangementView(primary, secondary), .arrangementViewStyle(.split)(기본값) 또는 .overlay로 스타일 지정. .split.axes(...)를 사용하여 분할 방향 제한; 기본 축을 따라 분할할 수 없으면 기본 뷰만 표시.
  • UIKit: 네비게이션 컨트롤러의 루트인 UIArrangementViewController, UISplitArrangement와 함께 updateArrangement.
  • 오버레이: 평평할 때는 쌓이고, 접힐 때는 나란히 표시. overlayArrangementZIndex(SwiftUI) 또는 state(for:) 다음 .zIndex(UIKit)를 읽어 축소 및 확장된 보조 뷰 사이를 전환.
  • 선택: 기존 패턴을 따름(HStack/VStack은 분할, ZStack은 오버레이). 그렇지 않으면 콘텐츠를 부분적으로 가려도 괜찮은 전경/배경 관계(접근성 리더)에는 오버레이를, 어느 뷰도 가려져서는 안 되는 메인/디테일(Podcasts 대본)에는 분할을 사용.
  • 네비게이션 컨테이너를 ArrangementView 내부에 배치하지 마세요. List/ScrollView 내부에 ArrangementView를 배치하지 마세요.

준비 감사(강연에서)

  1. 최신 SDK로 다시 빌드.
  2. 막대 감사: 시스템 컨테이너, 항목 순서, 제목이 있는 심볼.
  3. 사용자 정의 항목을 세로 준비.
  4. 오버플로 우선 순위 설정.
  5. 중앙 정렬 레이아웃 감사: 각각 2열이 되어야 하는지, 변위되어야 하는지, ArrangementView를 사용해야 하는지?
  6. 수동으로 배치된 고우선 순위 컨트롤에 예약 영역 API 채택.

Duo 크래프트 레이어 (apple-design 스킬에서 발췌)

HIG는 무엇이 적응하는지 알려줍니다. 이 섹션은 Apple의 유체 인터페이스 및 디자인 원칙 지침(WWDC Designing Fluid Interfaces, Principles of Great Design)을 Duo에서 어떻게 느껴져야 하는지에 적용합니다. 이는 적용된 원칙이지 HIG 규칙이 아닙니다. 전체 모션 툴킷(프로젝션, 고무줄 효과, 속도 핸드오프)에 대해서는 Emil Kowalski의 apple-design 스킬(emilkowalski/skills에서)도 로드하고 웹 예제를 SwiftUI/UIKit으로 변환하세요.

접힘 및 크기 조정 모션

  • 접힘은 사용자 주도의 연속적인 제스처입니다. 접힘을 따르는 레이아웃 변경(창 너비 균등화, 그리드 재배치, 접힘 영역에서 콘텐츠 이동)은 힌지를 부드럽게 추적하고 중단 가능해야 합니다: 사용자는 접힘 중간에 멈추거나 되돌릴 수 있으므로 항상 현재 화면 값에서 애니메이션을 시작하고 최종 상태로 스냅하지 마세요.
  • 재배치에는 임계 감쇠 스프링 사용(SwiftUI .spring(duration: 0.4, bounce: 0) 또는 .smooth; UIKit UIView.animate(springDuration:bounce:) with bounce: 0). 바운스 없음: 사용자가 아무것도 튕기지 않았으므로 오버슈트는 고장으로 읽힘.
  • 디스플레이 간 공간 일관성: 사용자가 기기를 열 때 외부 디스플레이에서 보고 있던 항목이 앵커로 유지(Mail의 열린 이메일이 새 화면이 아닌 뒤쪽 창이 됨). 스크롤 위치와 선택 유지; 추가 계층 구조는 그 옆에 나타납니다.
  • 재배치보다 작은 조정(HIG)은 "변경 방향으로 힌트"라는 동일한 규칙: 요소가 새 위치로 순간이동하지 않고 눈에 띄게 미끄러져야 함.

머티리얼 레이어로서의 측면 컨트롤

  • 측면 툴바와 탭 바는 콘텐츠 위에 떠 있으며 기본적으로 스크롤 가장자리 효과나 배경이 없습니다(기술 강연 기준). 하드 구분선이나 자체 배경을 추가하지 마세요. 안전 영역 인셋을 사용하여 콘텐츠가 막대 뒤에 숨겨지지 않도록 하세요.
  • 막대에 배치된 사용자 정의 뷰는 뒤에서 스크롤되는 콘텐츠 위에서, 그리고 투명도 감소가 켜져 있을 때 나타나는 단색 배경 위에서도 읽기 쉬워야 합니다.

Duo에 매핑된 원칙

  • 친숙함: 세로 막대 배치를 재정의하지 마세요. 이는 플랫폼의 새로운 근육 기억입니다.
  • 그룹화 및 매핑: 컨트롤은 영향을 미치는 창 옆에 유지(Mail 목록 컨트롤은 목록 위).
  • 길 찾기: 모든 포즈에서 사용자는 여전히 "내가 어디에 있고, 어떻게 돌아가는지"를 알 수 있어야 합니다. Back/Close는 항상 세로 축 상단에 있습니다.
  • 유연성: 하나의 적응형 레이아웃, Dynamic Type도 함께 조정. 측면 막대가 있는 좁고 높은 외부 디스플레이에서 가장 큰 텍스트 크기 테스트.
  • 반응: 포즈 변경은 입력을 차단해서는 안 됩니다. 접힘 중 탭도 여전히 작동해야 함.

접근성

  • 동작 줄이기가 켜져 있으면 재배치 슬라이드/스프링을 짧은 크로스페이드로 대체.
  • 투명도 감소가 켜져 있으면 측면 막대가 단색이 됨.
  • 접힘 중 이동하는 큰 표면: 모션을 짧게 유지하고 큰 밝기 점프를 피하세요.

일반적인 실수

실수수정 방법
포즈별 사용자 정의 레이아웃크기 클래스 + 크기 조절 가능 레이아웃
기능 숨기기…

[잘림]

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

관련 스킬