스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
데스크탑급 iPad 소개
iPad 앱에 데스크탑급 기능을 제공하는 방법을 알아보세요. 앱의 기능에 더 우수한 검색 가능성과 맞춤 가능성을 제공하는 UINavigationBar의 업데이트를 확인하세요. UIKit의 최신 업데이트를 통해 사용자가 내 앱에서 콘텐츠를 더 쉽고 빠르게 살펴볼 수 있도록 지원하는 방법을 알아보세요. 마지막으로, Mac Catalyst를 사용하여 iPad 앱을 데스크탑에서 사용하는 것이 얼마나 쉬워졌는지에 대한 몇 가지 소식을 공유합니다.
리소스
- Building a desktop-class iPad app
- centerItemGroups
- searchSuggestions
- Supporting desktop-class features in your iPad app
- titleMenuProvider
- UIDocumentProperties
- UINavigationItem.ItemStyle
- UINavigationItemRenameDelegate
관련 비디오
WWDC23
WWDC22
-
다운로드
안녕하세요 저는 David Duncan입니다 데스크톱 수준의 iPad를 소개해 드리죠 iOS 16은 훌륭한 앱을 만드는 도구를 더 발전시켰습니다 더 좋은 도구를 전면에 내세우는 앱으로 내장 및 외장 하드웨어를 최대한으로 활용하죠 UIKit는 앱의 목표를 달성할 도구를 추가했습니다 UINavigationBar의 업데이트로 화면 공간을 더 활용하여 모든 Apple 플랫폼에서 멋진 경험을 할 수 있죠
새로운 찾아 바꾸기 UI를 내장된 뷰에서 활용할 수 있고 커스텀 설정도 쉽게 추가할 수 있습니다 편집 메뉴도 완전히 바꿔서 인터랙션 기반의 API로 만들어 메뉴 시스템에 통합했죠 컬렉션 뷰 개선으로 여러분의 사용자가 직접 콘텐츠를 선택할 수 있는 인터페이스를 만들 수 있습니다
찾아 바꾸기와 편집 메뉴의 자세한 사항이 궁금하시면 '데스크톱 수준의 편집 인터랙션 채택하기'를 시청하세요 이 기능들이 어떻게 통합되는지 궁금하신 분들은 '데스크톱 수준의 iPad 앱 만들기'를 시청하십시오
이 영상에서는 내비게이션의 변화를 논의할 건데 iOS 16을 위한 여러분의 앱에 영향을 줍니다
찾기 쉬운 인터페이스를 구현하는 새로운 기능부터 알아보고 문서 기반의 앱에 유용한 기능을 살펴본 뒤 업데이트로 더 나아진 검색 기능을 소개하죠
UINavigationBar는 iOS에서 다양한 목적으로 사용하며 iOS 16은 그에 부응하여 다양한 사례에 최적화한 새로운 UI를 제공합니다 UINavigationItem에 스타일 속성이 추가되었는데 내비게이터, 브라우저, 에디터 중 선택할 수 있죠 각 스타일에 관해 자세히 얘기하겠습니다 기본 스타일인 내비게이터는 전통적인 UINavigationBar와 똑같이 동작하죠
제목은 중앙에 있고 좌우에 버튼 항목이 있으며 스택에 항목이 1개가 넘으면 뒤로가기 버튼이 나타납니다 브라우저 스타일은 위치만큼 이력이 중요한 인터페이스에 최적화되어 있는데 파일과 Safari가 그 예죠
이 스타일은 제목을 왼쪽에 배치했습니다
에디터 스타일은 주요 기능이 문서 편집일 때 최적화된 스타일이죠 브라우저 스타일처럼 왼쪽에 제목이 있습니다 에디터 UI는 문서 선택기로 문서를 선택했을 때 도착하는 페이지이므로 뒤로가기 버튼으로 쉽게 이용할 수 있습니다
브라우저와 에디터 스타일 모두 중앙 공간이 넓죠
iOS 16은 이 공간을 활용하여 여러분이 추가 컨트롤을 배치할 수 있게 했습니다
중앙 항목은 다른 변동 사항과 마찬가지로 화면 공간을 최대한 활용하고 UIBarButtonItemGroup에 대한 지원과 커스텀화와 오버플로도 지원하죠
오버플로 지원은 모든 모드에 제공하는데 내비게이터 스타일에서도 간접적으로 중앙 항목을 지원합니다
개별 컨트롤은 계속 UIBarButtonItems에서 지정하지만 UIBarButtonItemGroups로 정리했죠 공간이 부족해질 때 더 밀도 있게 나타냅니다 이 예시에서는 5개의 항목이 4개 그룹을 이루고 있죠
첫 번째 그룹은 단일 바 버튼 항목을 포함합니다 이 예시에서는 UIBarButtonItem의 편의성 메서드 creatingFixedGroup()으로 만들었죠
항목이 1개를 초과하는 고정 그룹이 필요하면 UIBarButtonItemGroup 메서드를 사용하십시오
고정 그룹은 항상 바의 처음에 나타나며 커스텀 설정으로 옮기거나 삭제할 수 없죠 그리기 그룹은 단일 항목을 포함하므로 편의성 API인 creatingMovableGroup (customizationIdentifier)를 사용합니다 이동 가능 그룹도 삭제할 수 없지만 움직일 수 있죠
따라서 customizationIdentifier를 사용해야 위치를 추적하고 저장할 수 있죠 항목이 1개를 초과하는 그룹이 필요하면 UIBarButtonItemGroup 메서드를 사용하십시오
도형 그룹은 여러 항목을 포함하므로 UIBarButtonItemGroup API를 사용하여 그룹을 만듭니다
이 그룹은 바 안에서 움직일 수 있으며 삭제도 가능하여 선택 그룹으로 생성되죠
이 그룹은 representativeItem을 지정하여 UIKit에서 그룹을 축소하고 공간을 확보할 수 있습니다
representativeItem은 동작을 지정하지 않아 UIKit가 그룹 내 항목을 선택하여 메뉴를 합성할 수 있죠
커스텀화 UI를 호출하면 UIKit가 자동으로 여러분이 그룹을 생성하기 위해 지정한 규칙을 적용합니다 고정 및 이동 가능 그룹이 바 안에 남아야 하지만 선택 그룹은 수에 상관없이 추가 및 삭제할 수 있죠
UIKit는 그룹을 축소하여 최대한 많은 기능이 바에 남도록 하지만 공간이 부족하면 남는 항목을 오버플로로 이동시킵니다 오버플로 메뉴는 커스텀 설정의 모든 항목을 포함하지만 바에 들어가지 못한 항목을 포함하며 바의 커스텀 설정 기능도 포함하죠
UIKit가 바 버튼 항목별로 기본 메뉴 요소를 합성하지만 원한다면 menuRepresentation을 커스텀 설정할 수도 있습니다 마지막으로 이 예시는 커스텀 설정 기능을 켜서 centerItemGroups를 추가하죠
UINavigationItem.customization Identifier로 커스텀 설정을 하죠
customizationIdentifier는 독특한 커스텀 설정을 정의하므로 다른 커스텀 설정과 충돌하지 않는 스트링을 선택하십시오
UIKit는 이 식별자를 기반으로 커스텀 설정을 저장하고 복구하죠
다음은 centerItemGroups를 제공하십시오 처음 4개 그룹은 이미 다뤘습니다
양식 그룹은 선택 그룹으로 기본 커스텀 설정에 없어서 isInDefaultCustomization 매개 변수의 기본값을 오버라이드하여 제외했습니다 UINavigationItem.customization Identifier를 설정하지 않고도 centerItemGroups를 사용할 수 있지만 커스텀 설정은 안 되죠 Mac Catalyst에서는 UINavigationBar가 콘텐츠를 NSToolbar로 자동 변환합니다 좌측 항목과 중앙 항목 우측 항목은 순서대로 추가되며 중앙 항목의 커스텀 설정 속성은 NSToolbar 커스텀 설정에서 그대로 사용하죠
NSToolbar의 모든 예상 동작을 제공하며 제목과 창 프락시 등의 다른 속성도 마찬가지입니다
Mac에 최적화할 때 모든 게 기본으로 실행되죠 다음은 강력한 인터랙션에 초점을 맞추겠습니다 특히 문서를 다룰 때 필요한 인터랙션이죠 UINavigationBar를 통해 타이틀 뷰에 메뉴를 추가하여 콘텐츠 전체에 작용하는 동작을 주요 위치에 두었습니다 또한 공유 시트와 메뉴에서의 드래그 앤 드롭 기능을 추가할 수 있죠 먼저 메뉴 항목을 살펴봅시다 실행하면 기본 타이틀 메뉴는 5개의 명령을 제공하죠 복사하기, 이동, 이름 변경 내보내기, 인쇄입니다 이 항목은 여러분의 리스폰더 체인의 특정 메서드를 기반으로 필터하죠 UINavigationBar는 이름 변경을 지원하여 renameDelegate를 적용해도 포함할 수 있습니다
타이틀 메뉴를 사용하려면 titleMenuProvider를 설정하세요 최종으로 표시할 메뉴를 반환하는 클로저죠
클로저에 추천 요소 배열을 통과시킵니다 그대로 사용해도 되고 필터하거나 직접 추가해도 되죠 이 예시에서는 단일 추가 동작을 메뉴에 추가하고 있습니다 마지막으로 구성한 UIMenu를 반환하죠
액티비티 뷰 컨트롤러를 통해 타이틀 메뉴에서 공유할 수 있고 드래그 앤 드롭도 지원합니다
이 기능을 실행하려면 여러분의 문서를 묘사하는 UIDocumentProperties를 제공하세요
UIDocumentProperties는 문서의 메타데이터와 미리 보기를 보여 줍니다 이 예시는 URL로 생성하여 UIKit가 필요한 메타데이터를 자동으로 가져오게 하죠
이러한 기능을 실행하기 위해 이 예시에서는 NSItemProvider를 생성하여 문서를 나타냅니다
dragItemsProvider를 설정하여 드래그 앤 드롭 기능을 켜세요 클로저에 UIDragSession을 넣으면 UIDragItems 배열을 반환하죠 이 예시는 문서를 나타내는 단일 항목을 반환합니다
activityViewControllerProvider를 설정하면 공유 기능이 켜지죠 UIActivityViewController를 설정하고 반환하는 클로저입니다
마지막으로 채워진 객체를 UINavigationItem.document Properties에 할당하여 타이틀을 탭 하면 UIKit에서 제목과 titleMenu 항목을 나타내죠
Mac Catalyst에서는 추천 항목을 파일 메뉴에 이미 있는 titleMenuProvider에 통과시킵니다 타이틀 메뉴에 추가하려고 했던 모든 아이템이 다른 방식을 통해 사용할 수 있게 하십시오
UIMenuBuilder API를 이용하여 이런 항목을 추가하거나 기존 항목을 필요한 대로 필터하셔도 됩니다
문서 속성을 지정하면 UIKit가 자동으로 macOS 프락시 아이콘 관리를 위해 제공한 URL을 이용하죠
windowScene에 representedURL을 수동으로 설정하면 UIKit 관리에 우선합니다
UIKit는 이름 변경을 위한 두 개의 메커니즘을 제공하죠 UINavigationItem.renameDelegate 메서드로 이름 변경을 설정하여 모든 플랫폼의 타이틀을 편집하는 전용 UI도 제공합니다
완료하면 최종 이름이 대리자에게 전달되죠
이름 변경 기능을 완벽히 제어하고 싶다면 UIResponder.rename(_:) 적용으로 원하는 UI를 제공하십시오
iOS의 UINavigationBar는 타이틀 뷰 안에서 이름 변경 UI를 제공합니다 macOS는 NSToolbar에서 내비게이션 바를 호스트할 때 이름 변경 UI를 창 제목에 제공하죠 UINavigationItemRenameDelegate 프로토콜에 맞춰 내장 이름 변경을 적용하고 renameDelegate를 설정하세요 navigationItem(_:didEndRenaming With:) 메서드만 요구되며 사용자가 결정한 타이틀을 수신합니다
파일 기반의 앱은 UIDocument BrowserViewController에서 이름 변경 API를 제공하죠
많은 사용자는 중요한 데이터를 검색 기능으로 찾습니다 iOS 16의 개선으로 최상의 검색 경험을 제공하죠 이제 검색은 더 적은 공간을 사용하는데 iPadOS의 내비게이션 바와 macOS의 도구모음 안에서 검색할 수 있기 때문이죠 iPadOS에서는 UINavigationItem .preferredSearchBarPlacement으로 이전 기능을 복원할 수 있습니다 검색 바는 버튼으로 축소할 수 있어 다른 컨트롤에 공간을 더 줄 수 있죠 검색을 활성화하면 추천 검색이 나타나는데 검색어가 바뀔 때마다 추천 검색어도 달라져서 사용자의 검색을 도울 수 있습니다 다음은 추천 검색어를 설정하는 코드를 보여 드리죠
UISearchResultsUpdating에 맞춰 추천 검색을 관리하시고 searchController의 search ResultsUpdater를 설정하세요 이를 통해 검색어가 바뀔 때 추천 검색어를 업데이트하고 선택된 추천 검색을 실행할 수 있습니다
updateSearchResults(for:)는 검색어가 바뀔 때 호출하여 추천 검색어를 업데이트할 수 있으며
어떤 걸 추천할지는 여러분에게 달렸죠 빈 배열을 설정하면 추천 UI가 사라집니다
UIKit는 UISearchSuggestionItem을 제공하여 추천 콘텐츠를 지정하죠
추천 선택에 반응하려면 updateSearchResults (for:selecting:)를 적용하세요 이 메서드는 선택한 추천 검색어를 통과시키므로 이에 적절히 반응하면 됩니다 이 예시에서는 현재 검색어를 추천 검색어로 대체하여 검색 결과를 업데이트했죠 UISearchTextField는 searchSuggestions가 있으니까 해당 클래스 자체로 사용하기를 원한다면 추천 검색어를 적용할 수 있습니다 UISearchController를 사용한다면 그 안의 속성을 사용하십시오
iOS 16에서는 UIKit가 새로운 API를 제공하여 사용자의 생산성을 높입니다 고급 기능이 더 잘 보이도록 중앙 항목과 타이틀 메뉴를 이용하세요
내비게이션 바에서 드래그 앤 드롭과 공유 기능을 직접 제공하여 문서 앱을 개선하십시오 추천 검색어를 제공하여 쉽고 빠른 검색을 지원하고 추가 작업 없이 Mac의 멋진 경험을 바로 사용하시기 바랍니다 시청해 주셔서 감사합니다 여러분이 만든 데스크톱 수준의 앱을 빨리 보고 싶군요
-
-
4:27 - Creating a fixed UIBarButtonItemGroup from a single UIBarButtonItem
let insertGroup = UIBarButtonItem(title: "Insert", image: UIImage(systemName: "photo"), primaryAction: UIAction { _ in }).creatingFixedGroup()
-
4:55 - Convenient form
// Creating the 'Draw' group // Convenient form of // UIBarButtonItemGroup.movableGroup(customizationIdentifier:representativeItem:items:) let drawGroup = UIBarButtonItem(title: "Draw", …) .creatingMovableGroup(customizationIdentifier: "Draw")
-
5:30 - Creating an optional group with multiple UIBarButtonItems using UIBarButtonItemGroup
let shapeGroup = UIBarButtonItemGroup.optionalGroup( customizationIdentifier: "Shapes", representativeItem: UIBarButtonItem(title: "Shapes", image: UIImage(systemName: "square.on.circle")), items: [ UIBarButtonItem(title: "Square", image: UIImage(systemName: "square"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Circle", image: UIImage(systemName: "circle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Rectangle", image: UIImage(systemName: "rectangle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Diamond", image: UIImage(systemName: "diamond"), primaryAction: UIAction { _ in }), ])
-
6:56 - Setting up customizable centerItemGroups on a UINavigationItem
navigationItem.customizationIdentifier = "com.jetpack.blueprints.maineditor" navigationItem.centerItemGroups = [ // groups in the default customization UIBarButtonItem(title: "Insert", image: UIImage(systemName: "photo"), primaryAction: UIAction { _ in }).creatingFixedGroup(), UIBarButtonItem(title: "Draw", image: UIImage(systemName: "scribble"), primaryAction: UIAction { _ in }).creatingMovableGroup(customizationIdentifier: "Draw"), .optionalGroup(customizationIdentifier: "Shapes", representativeItem: UIBarButtonItem(title: "Shapes", image: UIImage(systemName: "square.on.circle")), items: [ UIBarButtonItem(title: "Square", image: UIImage(systemName: "square"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Circle", image: UIImage(systemName: "circle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Rectangle", image: UIImage(systemName: "rectangle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Diamond", image: UIImage(systemName: "diamond"), primaryAction: UIAction { _ in }), ]), .optionalGroup(customizationIdentifier: "Text", items: [ UIBarButtonItem(title: "Label", image: UIImage(systemName: "character.textbox"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Text", image: UIImage(systemName: "text.bubble"), primaryAction: UIAction { _ in }), ]), // additional group not in the default customization .optionalGroup(customizationIdentifier: "Format", isInDefaultCustomization: false, representativeItem: UIBarButtonItem(title: "BIU", image: UIImage(systemName: "bold.italic.underline")), items:[ UIBarButtonItem(title: "Bold", image: UIImage(systemName: "bold"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Italic", image: UIImage(systemName: "italic"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Underline", image: UIImage(systemName: "underline"), primaryAction: UIAction { _ in }), ]) ]
-
9:30 - Adding a "Comments" item to the default title menu
navigationItem.titleMenuProvider = { suggestedActions in var children = suggestedActions children += [ UIAction(title: "Comments", image: UIImage(systemName: "text.bubble")) { _ in } ] return UIMenu(children: children) }
-
10:15 - Supporting Drag & Drop and Sharing from the title menu
let url = <#T##URL#> let documentProperties = UIDocumentProperties(url: url) if let itemProvider = NSItemProvider(contentsOf: url) { documentProperties.dragItemsProvider = { _ in [UIDragItem(itemProvider: itemProvider)] } documentProperties.activityViewControllerProvider = { UIActivityViewController(activityItems: [itemProvider], applicationActivities: nil) } } navigationItem.documentProperties = documentProperties
-
12:45 - Implementing inline rename
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() navigationItem.renameDelegate = self } } extension ViewController: UINavigationItemRenameDelegate { func navigationItem(_ navigationItem: UINavigationItem, didEndRenamingWith title: String) { // Try renaming our document, the completion handler will have the updated URL or return an error. documentBrowserViewController.renameDocument(at: <#T##URL#>, proposedName: title, completionHandler: <#T##(URL?, Error?) -> Void#>) } }
-
14:05 - Implementing Search Suggestions
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() searchController.searchResultsUpdater = self } } extension ViewController: UISearchResultsUpdating { func fetchQuerySuggestions(for searchController: UISearchController) -> [(String, UIImage?)] { let queryText = searchController.searchBar.text // Here you would decide how to transform the queryText into search results. This example just returns something fixed. return [("Sample Suggestion", UIImage(systemName: "rectangle.and.text.magnifyingglass"))] } func updateSearch(_ searchController: UISearchController, query: String) { // This method is used to update the search UI from our query text change // You should also update internal state related to when the query changes, as you might for when the user changes the query by typing. searchController.searchBar.text = query } func updateSearchResults(for searchController: UISearchController) { let querySuggestions = self.fetchQuerySuggestions(for: searchController) searchController.searchSuggestions = querySuggestions.map { name, icon in UISearchSuggestionItem(localizedSuggestion: name, localizedDescription: nil, iconImage: icon) } } func updateSearchResults(for searchController: UISearchController, selecting searchSuggestion: UISearchSuggestion) { if let suggestion = searchSuggestion.localizedSuggestion { updateSearch(searchController, query: suggestion) } } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.