스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
더 나은 문서 기반 앱 빌드하기
iPadOS의 최신 기능을 사용하여 문서 기반 앱을 개선하는 방법을 알아보세요. 기존 데스크톱급 iPad 및 문서 기반 API뿐만 아니라 UIDocument를 활용하여 앱에 새로운 기능을 추가하는 방법을 보여드립니다. 데이터 모델을 UIDocument로 변환하는 방법, UIDocumentViewController로 문서를 표시하는 방법, 앱을 최신 API로 마이그레이션하는 방법을 알아보고 모범 사례를 살펴보세요.
챕터
- 2:10 - Creating a document
- 5:46 - Presenting a document
- 9:38 - Migrating your app
리소스
관련 비디오
WWDC23
WWDC22
WWDC20
Tech Talks
-
다운로드
♪ ♪
안녕하세요 저는 마이클 옥스입니다 이 영상에서는 더 나은 문서 중심 앱 제작법을 이야기해 보겠습니다 문서 중심 앱은 생산성 도구에서 그 중요도가 높습니다 특히 iPad에서요 문서 중심 앱에는 세 가지 유형이 있는데요 파일 앱과 같이 문서를 탐색할 수 있는 브라우저 앱 Quick Look과 같이 콘텐츠를 볼 수 있는 뷰어 앱 그리고 콘텐츠를 편집하거나 생성할 수 있는 에디터 앱입니다 Pages, Keynote, Numbers처럼요 이 영상은 뷰어 및 에디터 앱의 개선 사항에 중점을 두지만 논의될 내용 중 일부는 브라우저 앱에도 적용됩니다 iPadOS 17에서는 새로운 뷰 컨트롤러를 도입하여 앱의 많은 기능을 자동으로 활성화하게 되었습니다 이는 iPadOS 16에 도입된 데스크톱급 iPad API들과도 잘 작동할 뿐만 아니라 기존의 문서 중심 API와도 잘 작동합니다 이 새로운 뷰 컨트롤러는 모듈 방식으로 구축되었습니다 훌륭한 시스템 기본 설정이 제공되지만 개별 동작을 사용자화할 수 있죠
데스크톱급 iPad API를 더 알아보려면 WWDC22의 '데스크톱급 iPad 알아보기' 및 '데스크톱급 iPad 앱 빌드하기'를 확인하세요
SwiftUI 개발의 경우 DocumentGroup은 이제 추가 코드 없이 이러한 모든 기능을 지원합니다 이에 대한 자세한 내용은 다음과 같은 영상에서 확인하세요 WWDC20의 'SwiftUI에서 문서 기반 앱 빌드하기'와 WWDC22의 'iPad의 SwiftUI: 도구 막대와 제목 추가하기'입니다 UIKit에서 기능은 옵트인입니다 UIDocumentViewController는 콘텐츠 뷰 컨트롤러를 위한 새로운 기본 클래스이며 UIDocument와 함께 작동하여 탐색 막대를 자동으로 설정합니다 이로써 다음과 같은 기능을 쓸 수 있죠 문서의 공유 및 드래그와 실행 취소 및 실행 복귀 지원 같은 것들요 또한 자동 이름 변경을 지원합니다 이 영상에서는 UIDocument를 사용하는 방법과 UIDocumentViewController로 문서를 표시하는 방법을 배우고 기본으로 제공되는 기능과 그것을 사용자화하는 방법을 설명하겠습니다 마지막으로 기존 앱을 마이그레이션하여 UIDocument를 활용하는 모범 사례를 살펴보겠습니다 먼저, 문서 생성하기입니다
모든 문서 기반 앱의 핵심은 UIDocument이며 이것은 추상 기본 클래스로서 앱이 지원하는 파일 유형에 대해 하위 클래스로 사용할 수 있습니다 모든 UIDocument는 URL로 뒷받침됩니다 디스크의 파일이 가장 일반적이지만 문서를 저장 및 로드할 때 데이터베이스와 맞춤형 URL 체계를 사용할 수도 있죠 UIDocument의 로드 및 저장 작업은 비동기적이므로 필요한 경우, 오래 이어지는 읽기 및 쓰기 작업이 가능하며 이러한 이유로 UIDocument는 스레드 안전이며 록과 큐를 통해 액세스를 조정할 수 있습니다
UIDocument 하위 클래스를 구현할 때 처리해야 할 두 가지 주요 사항은 문서 로드 및 저장과 문서의 콘텐츠에 대한 액세스 제공입니다 로드 및 저장은 모든 문서에서 매우 유사하지만 콘텐츠 액세스는 좀 더 특색 있죠 문서의 유형과 앱에서 사용되는 방식에 따라서요 예를 들어 Markdown 에디터용 문서 모델은 단일 텍스트 속성만 있거나 더 복잡한 인터페이스가 보일 수도 있습니다 그것으로 문서의 개별 부분을 업데이트할 수 있죠 콘텐츠 액세스를 더 알아보기 전에 로드 및 저장에 대해 설명하겠습니다 간단한 파일 기반 문서의 경우 재정의할 수 있는 두 가지 편리한 메서드가 있죠 문서가 열리면 'loadFromContents:ofType:'이 파일 내용과 함께 호출됩니다 문서가 저장 중일 때는 'contentsForType:'이 호출되어 문서의 현재 콘텐츠를 가져오죠 문서의 콘텐츠는 일반 파일의 경우 데이터 객체이고 그 외 모든 파일의 경우 FileWrapper입니다 파일 유형과 작동 방식을 더 자세히 알아보려면 Tech Talk 영상 '통합 유형 식별자 - 재소개'를 참고하세요 이 예제에서는 일반 Markdown 파일을 다루고 있으므로 데이터 객체가 되겠죠 이제 완전한 제어를 원한다면 'savetoURL:forSaveOperation:'과 'readfromURL:'을 재정의합니다 URL에 대한 완전한 액세스 권한이 주어지고 모든 읽기 및 쓰기가 가능해지죠 이것은 문서를 데이터베이스에 저장하거나 문서를 읽고 쓰는 데 특별한 요구 사항이 있을 때 유용합니다 저장 작업은 비동기적이지만 메서드가 반환될 때까지 읽기는 완료될 것입니다 이렇게 문서 로드 및 저장을 모두 알아보았습니다 이제 문서의 콘텐츠에 액세스하는 방법을 살펴보죠 문서의 콘텐츠에 대한 액세스를 제공하는 편리한 방법은 해당 콘텐츠에 대한 프로퍼티를 추가하는 것입니다 이 예제에서는 단일 텍스트 프로퍼티를 추가합니다 여기에는 전체 Markdown 문자열이 포함돼 있죠 이 프로퍼티는 처음 문서를 로드할 때 설정되며 이전 슬라이드에서 설명한 대로 앱은 사용자가 이 문서를 편집할 때마다 이 텍스트를 업데이트할 수 있습니다 UIDocument가 저장해야 하는 시점을 알 수 있도록 프로퍼티가 업데이트될 때마다 'updateChangeCount:'를 호출하죠 updateChangeCount:를 호출하면 UIDocument가 문서를 저장할 필요가 있다고 표시하고 적절한 시점에 자동으로 저장할 수 있습니다
다음은 새 UIDocumentViewController로 문서를 표시하는 방법입니다
UIDocument와 마찬가지로 UIDocumentViewController도 추상 기본 클래스이며 하위 클래스가 될 수 있습니다 문서 열기, 저장 및 닫기를 관리하고 관련 문서의 정보로 탐색 항목을 채우죠 여기에는 제목과 탐색 항목의 제목 메뉴 UIDocumentProperties 객체 renameDelegate가 포함됩니다 UIDocumentViewController는 실행 취소 및 실행 복귀와 같은 일반적인 작업에 대한 키 명령도 제공합니다 UIDocumentViewController 하위 클래스를 구현하는 방법을 알아보죠
하위 클래스에서 재정의하도록 설계된 두 가지 메서드가 있습니다 뷰 컨트롤러와 관련된 문서가 열리거나 이미 열린 문서가 뷰 컨트롤러에 할당되면 'documentDidOpen'이 호출됩니다 이 메서드에서 문서의 콘텐츠를 표시하기 위해 뷰 컨트롤러의 뷰를 채웁니다 documentDidOpen이 호출되는 시점과 뷰 컨트롤러의 뷰가 로드되는 시점 사이에는 타이밍 보장이 보장되지 않습니다 탄탄한 코드를 작성하는 좋은 방법은 뷰 구성을 자체 메서드로 이동시키고 양쪽 모두로부터 호출하는 겁니다 documentDidOpen과 'viewDidLoad'로부터요 뷰를 구성하기 전에 뷰가 로드되고 문서가 열려 있는지 확인하세요
재정의할 두 번째 메서드는 'navigationItemDidUpdate'입니다 UIDocumentViewController가 탐색에 변경 사항이 생기면 이 메서드를 호출하는데요 여기에 사용자 맞춤형 탐색 항목을 추가합니다 UIDocumentViewController는 변경 사항을 최소한으로 하기 위해 최선을 다할 것입니다 UIDocumentViewController는 또한 'undoRedoItemGroup'을 제공하죠 이 그룹을 탐색 막대에 넣으세요 실행 취소 및 실행 복귀 버튼을 표시하려면요 그리고 문서에 실행 취소 관리자가 할당되어 있는지 확인합니다 UIDocumentViewController는 실행 취소 관리자의 사용 가능 여부에 따라 이 그룹의 '숨겨진' 속성을 변경하며 필요에 따라 그룹 내부의 버튼을 활성화 또는 비활성화합니다
UIDocumentViewController는 문서를 자동으로 열고 닫습니다 그러나 뷰 컨트롤러 외부에서 문서에 액세스해야 하는 경우라면 'openDocumentWithCompletion Handler'를 호출하세요 UIDocumentViewController가 필요한 콜백을 다 할 겁니다 documentDidOpen을 호출하고 준비가 되면 완료 핸들러를 호출하는 식으로요
마지막으로 UIDocumentViewController는 document 프로퍼티를 제공합니다 이 프로퍼티는 항상 뷰 컨트롤러와 연결된 문서를 참조하죠 초기화 중에 문서를 제공할 수 있지만 이는 전적으로 선택 사항이며 뷰 컨트롤러와 연결된 문서가 없으면 자동으로 빈 상태를 표시합니다 빈 상태 구성을 더 자세히 알아보려면 'UIKit의 새 기능'을 확인하세요 또한 UIDocumentViewController는 앱의 루트 뷰 컨트롤러로 사용할 수 있는데요 계층 구조에 브라우저 뷰 컨트롤러가 없는 경우 UIDocumentViewController는 탐색 막대에 문서 버튼을 둡니다 이 버튼이 문서 피커를 열죠 이를 위해 'UIDocumentClass'를 선언해야 합니다 앱의 info.plist의 관련 파일 유형에 대해서요 또한 해당 파일 유형과 일치하는 UIDocument 하위 클래스로 설정해야 합니다
iPadOS 17에서 UIDocument는 'UINavigationItemRename Delegate'를 준수하며 기본 파일 변경은 자체적으로 처리하게 됩니다 사용자가 제목 메뉴에서 이름 변경을 호출하는 경우에요 UIDocumentViewController를 사용한다면 자동으로 이름 변경을 구성하고 그렇지 않은 경우 문서를 탐색 항목의 이름 변경 대리자로 수동 설정 할 수 있습니다
지금까지 iPadOS 17에서 훌륭한 문서 중심 앱을 만드는 데 필요한 모든 요소를 살펴봤습니다 다음은 기존 앱을 마이그레이션하는 방법입니다
새로운 UIDocument ViewController를 사용하도록 앱을 마이그레이션하는 것은 간단하며 세 단계만 거치면 됩니다 먼저, 콘텐츠 뷰 컨트롤러의 기본 클래스를 업데이트합니다 둘째, 기존 코드를 새 콜백으로 이동시킵니다 셋째, 더 이상 필요하지 않은 코드를 삭제합니다 Markdown 에디터 예제를 변환하는 방법을 확인해 보겠습니다 이 에디터는 데스크톱급 iPad 앱 영상에서 사용하죠 익숙하지 않더라도 걱정하지 마시고 기존 코드의 관련 내용을 먼저 안내해 드리겠습니다
상단을 보면 뷰 컨트롤러의 정의와 그것이 정의하는 문서 프로퍼티 초기 문서를 설정하는 init 메서드가 있으며 문서에 콜백을 추가합니다
먼저, 기본 클래스를 UIDocumentViewController로 변경합니다
이제 이 클래스는 UIDocumentViewController를 상속하므로 컴파일러 오류가 발생합니다 'document' 프로퍼티가 이미 다른 유형으로 상위 클래스에 존재하기 때문이죠 해당 프로퍼티의 이름을 더 구체적인 이름으로 변경합니다 'markdownDocument'처럼요 그런 다음 이것을 연산 프로퍼티로 만들어 일반 document 프로퍼티를 이 뷰 컨트롤러에서 사용되는 특정 문서 클래스로 캐스팅합니다 이 코드에서 처리해야 할 마지막 비트는 이니셜라이저입니다 여전히 필요한 유일한 코드는 문서에 콜백을 할당하는 것입니다 이 뷰 컨트롤러의 수명 주기 동안 문서가 변경될 수 있으므로 문서가 변경될 때마다 실행되도록 이 코드를 이동시킵니다
이를 수행하는 쉬운 방법은 document 프로퍼티를 재정의하고 didSet 콜백을 추가하는 거죠 좋습니다, 이제 기본 클래스가 최신 상태이므로 새 콜백을 처리해야 합니다 viewDidLoad에서 탐색 막대에 버튼을 추가한 후 탐색 막대를 사용자화할 수 있게 설정합니다 UIDocumentViewController의 경우 새 콜백 'navigationItem DidUpdate'로 이동합니다
다음으로 우리 클래스에는 이미 'didOpenDocument' 메서드가 있죠 이것은 UIDocumentView Controller에도 있는데요 메서드의 이름을 바꾸고 이제 document가 선택 사항이라는 사실을 조정하기만 하면 됩니다
좋습니다, 다음은 우리 모두가 가장 좋아하는 코드 삭제입니다 에디터 뷰 컨트롤러는 UINavigationItemRename Delegate를 준수하지만 더 이상 필요하지 않습니다 UIDocument는 모든 이름 변경을 자동으로 수행하죠 따라서 대리자 정의와 모든 코드가 있는 대리자 메서드 그리고 renameDelegate 할당을 삭제합니다
다음으로 몇 가지 사용자화한 navigationItem을 삭제할 수 있죠 'style'과 'backAction'은 모두 문서 뷰 컨트롤러에 의해 자동으로 구성되므로 완전히 삭제할 수 있습니다
UIDocumentProperties 객체를 만드는 데 사용되는 'updateDocumentProperties' 메서드도 있으며 이 메서드는 다양한 위치에서 호출됩니다 그러나 더 이상 필요하지 않습니다 UIDocumentViewController가 이 모든 작업을 수행하므로 이 메서드와 모든 호출 사이트를 삭제할 수 있습니다 이것으로 모든 것이 끝났습니다 이제 에디터 뷰 컨트롤러는 앱 고유의 기능만 처리하게 됩니다 더 이상 문서 관리의 기본 작업이나 탐색 막대의 기본 설정을 관리할 필요가 없으며 대신 앱의 고유하고 핵심적인 요소에 집중할 수 있죠 이렇게만 하시면 문서 중심 앱을 한 단계 더 발전시켜 사용자에게 훌륭한 경험을 제공할 수 있습니다
데이터 모델을 UIDocument를 사용하도록 변환한 다음 콘텐츠 뷰 컨트롤러를 변환하여 새로운 UIDocumentViewController 기본 클래스를 사용하고 그런 다음, 뷰 컨트롤러를 살펴 더 이상 필요하지 않은 모든 코드를 삭제하세요 시청해 주셔서 감사합니다 ♪ ♪
-
-
3:54 - Loading a document
override func load(fromContents contents: Any, ofType typeName: String?) throws { // Load your document from contents guard let data = contents as? Data, let text = String(data: data, encoding: .utf8) else { throw DocumentError.readError } self.text = text }
-
4:08 - Saving a document
override func contents(forType typeName: String) throws -> Any { // Encode your document with an instance of NSData or NSFileWrapper guard let data = self.text?.data(using: .utf8) else { throw DocumentError.writeError } return data }
-
4:34 - Manually saving and loading a document
override func save(to url: URL, for saveOperation: UIDocument.SaveOperation, completionHandler: ((Bool) -> Void)? = nil) { self.performAsynchronousFileAccess { // Set up file coordination and write file to URL } } override func read(from url: URL) throws { // Set up file coordination and read file from URL }
-
5:08 - Defining document that require saving
class Document: UIDocument { var text: String? { didSet { if oldValue != nil && oldValue != text { self.updateChangeCount(.done) } } } }
-
6:30 - Updating the view hierarchy for a document
override func documentDidOpen() { configureViewForCurrentDocument() } override func viewDidLoad() { super.viewDidLoad() configureViewForCurrentDocument() } func configureViewForCurrentDocument() { guard let document = markdownDocument, !document.documentState.contains(.closed) && isViewLoaded else { return } // Configure views for document }
-
7:17 - Updating navigation items for a document
override func navigationItemDidUpdate() { // Customize navigation item }
-
8:01 - Manually opening a document
documentController.openDocument { success in if success { self.present(documentController, animated: true) } }
-
9:20 - Renaming a UIDocument without UIDocumentViewController
navigationItem.renameDelegate = document
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.