스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
글쓰기 도구 시작하기
앱에서 글쓰기 도구를 활용하여 텍스트를 교정하고, 다시 작성하고, 변환하는 방법을 알아봅니다. 사용자가 어느 텍스트 뷰에서나 작성한 내용을 다듬을 수 있도록 글쓰기 도구가 앱과 상호작용하는 방식을 자세히 살펴보세요. 텍스트를 가져오고 처리하는 방법과 맞춤형 텍스트 뷰에서 글쓰기 도구를 지원하는 방법을 확인할 수 있습니다.
챕터
- 0:00 - Introduction
- 3:42 - Native text views
- 7:03 - Controlling behavior
- 8:23 - Protecting ranges
- 9:03 - Custom text views
- 11:44 - Next steps
리소스
관련 비디오
WWDC23
WWDC22
WWDC21
WWDC19
-
다운로드
안녕하세요, ’글쓰기 도구 시작하기’ 비디오입니다 저는 Liu Dongyuan입니다 텍스트 입력 및 국제화 업무를 담당하고 있습니다 글쓰기 도구는 새로운 기능으로 모든 종류의 앱에 걸쳐 텍스트 뷰에서 제공됩니다 사용자가 작업 중인 텍스트를 다듬을 수 있도록 도와줍니다 이 세션에서는 글쓰기 도구를 소개한 다음 글쓰기 도구가 기본 텍스트 보기와 상호작용하는 방법 앱에 더 적합하도록 글쓰기 도구의 동작을 제어하는 방법 따옴표와 코드 블록 같은 텍스트 범위를 보호하는 방법 맞춤형 텍스트 뷰에 글쓰기 도구를 지원하는 방법을 설명합니다 이제 글쓰기 도구를 시작해 보겠습니다 글쓰기 도구로 사용자는 iOS, iPadOS, macOS의 텍스트 뷰에서 텍스트를 교정하고 고쳐 쓰고 변형할 수 있습니다 제안 사항이 바로 줄에 표시되므로 쉽게 변경 사항을 빠르고 원활하게 검토하고 통합할 수 있습니다 모든 기본 텍스트 뷰에서 텍스트를 선택하면 키보드 상단에 글쓰기 도구가 표시됩니다 콜아웃 막대에 ’오려두기’, ’복사하기’ ’붙여넣기’ 옆에도 표시됩니다
macOS에서는 빠른 메뉴와 편집 메뉴에서 사용할 수 있습니다
기본 텍스트 뷰에서 선택된 텍스트 위로 마우스를 가져가면 글쓰기 도구 패널을 여는 어포던스로 표시됩니다 이렇게 하면 텍스트에서 곧바로 글쓰기 도구를 호출할 수 있습니다 글쓰기 도구로 할 수 있는 일을 더 자세히 살펴보겠습니다
텍스트를 교정할 수 있고 맞춤법 오류와 문법 오류 같은 실수를 표시할 수 있습니다 글쓰기 도구의 제안 사항을 하나씩 검토할 수 있습니다
글쓰기 도구는 텍스트를 고쳐 쓰거나 텍스트를 더 친근하고, 전문적이고 간결하게 만들 수 있습니다 글쓰기 도구는 텍스트를 요약하고 텍스트의 요점을 간추릴 수 있으며 텍스트를 목록이나 표로 변환할 수도 있습니다 글쓰기 도구는 편집할 수 없는 텍스트에서도 작동합니다 결과는 패널에 표시되며 사용자는 해당 결과를 복사하거나 공유할 수 있습니다 이러한 기능 중 일부가 실제로 작동하는 모습을 보여 드리죠 글쓰기 도구의 출시 파티를 개최할 예정이라고 가정해 보죠 제가 작성한 대략적인 초대장이 있습니다 전송하기 전에 실수가 없는지 확인하고 싶습니다
콜아웃 막대에서 글쓰기 도구를 호출하고 ’교정’을 선택할 수 있습니다
와! 작업이 완료되면 모든 제안 사항이 줄에 표시됩니다 탭하면 개별 변경 사항을 검토할 수 있습니다
모두 괜찮아 보이니 ’완료’를 탭하여 변경 사항을 적용합니다 또한 친구들에게 파티에 대한 짧은 메시지를 보내고 싶네요 글쓰기 도구로 텍스트를 요약해 더 짧게 만들 수 있습니다
그런 다음 요약 내용을 복사하거나 공유할 수 있죠
마지막으로 출시 파티를 준비하려는데 친구 몇 명에게 어떤 물건을 가져오라고 부탁했습니다 그 내용을 멋진 표로 만들고 싶네요 텍스트를 선택하고 글쓰기 도구를 호출한 다음 ’표’를 선택합니다
짠!
볼드체 텍스트와 링크는 모두 그대로 유지됩니다 멋지지 않나요? 이처럼 글쓰기 도구는 강력한 도구입니다 텍스트 뷰에 더 많은 기능을 제공하죠 글쓰기 도구는 지원되는 환경에서 앱이 실행되는 경우 자동으로 표시됩니다 다음으로, 글쓰기 도구가 텍스트 보기에서 작동하는 방식과 앱에서 글쓰기 도구를 불러오는 방법을 살펴보겠습니다 편리하게도 UITextView, NSTextView WKWebView 사용 시 바로 작동하죠
UITextView 또는 NSTextView는 TextKit 2를 사용해야 전체 글쓰기 도구 경험을 지원할 수 있습니다 TextKit 1을 사용하는 경우 고쳐 쓴 결과만 패널에 표시하는 제한적인 경험이 제공됩니다
TextKit 2에 대한 자세한 내용은 링크로 연결된 세션을 확인하세요
글쓰기 도구를 호출하면 내부에서 어떤 일이 발생할까요?
첫째, 사용자의 선택 영역을 완전한 문장으로 확장하여 이 도구에서 더 나은 결과를 얻을 수 있습니다 선택 영역 주변의 텍스트를 더 많이 사용하여 모델이 텍스트의 맥락을 인지하게 할 수도 있습니다
둘째, 속성 지정된 문자열로 서식 있는 텍스트를 완전히 지원합니다 모델은 스타일, 링크, 첨부 파일 같은 속성을 유지합니다 해당 관련 텍스트가 고쳐 쓴 텍스트에 계속 존재한다면요
마지막으로, 목록 및 표 변환의 경우 텍스트 뷰가 목록과 표를 지원한다면 NSTextList와 NSTextTable을 텍스트 저장 공간으로 전송합니다 텍스트 뷰에서 WritingToolsAllowedInputOptions를 사용해 표를 처리하게 할지 여부를 제어할 수 있습니다 이 내용은 나중에 살펴보죠 데모에서 보셨듯이 모델이 텍스트를 처리 중인 경우 글쓰기 도구 세션이 활성화된 상태이면 애니메이션을 표시합니다 세션 중에 앱이 동기화를 일시 정지하거나 실수로 편집하는 것을 방지할 수 있습니다 글쓰기 도구에 대한 새로운 텍스트 보기 위임 메서드를 도입했습니다
textViewWritingToolsWillBegin에서 글쓰기 도구에 대한 앱 상태를 준비할 수 있습니다 예를 들어, 동기화 또는 텍스트 저장 공간을 직접 조작하는 기능을 일시 정지할 수 있습니다
textViewWritingToolsDidEnd에서는 동기화 재개 같은 앱 상태를 복원할 수 있습니다
UITextView에서 새로운 속성 isWritingToolsActive도 제공하죠 텍스트 작업을 수행할 때는 글쓰기 도구가 텍스트 뷰와도 상호작용하는지를 인지해야 합니다
새로운 위임 메서드와 속성은 UITextView와 NSTextView 모두에 대해 사용할 수 있습니다
텍스트가 긴 경우 고쳐 쓴 텍스트가 별도의 청크로 텍스트 뷰에 제공될 수 있습니다 처리 중인 텍스트에 애니메이션을 적용하여 모델이 작동 중임을 나타냅니다 모델이 처리를 완료한 후 ’원본’ 버튼을 탭해 원본과 고쳐 쓴 텍스트 간에 전환 가능합니다
교정의 경우 제안된 변경 사항이 텍스트 보기에 자동으로 적용됩니다 사용자는 개별 제안 사항을 검토하고 거부할 수 있습니다
글쓰기 도구 작업 중에 기본 텍스트 저장 공간에 무슨 일이 일어나는지 궁금하실 수 있습니다 모든 작업은 실제로 텍스트 저장 공간을 변경하므로 글쓰기 도구 활성 시, 텍스트 저장 공간을 유지하지 마세요 사용자가 변경 사항을 되돌릴 수 있도록 허용하면 글쓰기 도구의 변경 사항도 실행 취소 스택으로 푸시됩니다 앱에 더 작합하도록 글쓰기 도구 동작을 맞춤화할 수 있습니다 새로운 텍스트 입력 특성 API가 있습니다
기본적으로 시스템은 글쓰기 도구를 가능한 한 인라인 경험으로 제공하며 사용자는 아무것도 하지 않아도 됩니다 이것이 앱에 적합하지 않다면 writingToolsBehavior를 .limited로 해 패널로 제공하거나 .none으로 설정해 텍스트 뷰에서 글쓰기 도구를 완전히 해제합니다
writingToolsAllowedInputOptions를 사용해 텍스트 뷰가 서식 있는 텍스트나 표를 지원하도록 지정할 수도 있죠 이 항목을 설정하지 않으면 텍스트 뷰가 일반 텍스트와 서식 있는 텍스트는 렌더링하고 표는 렌더링하지 않을 것입니다 텍스트 뷰가 일반 텍스트만 허용하거나 표를 처리하는 경우 옵션을 명시적으로 지정할 수 있습니다
마지막으로, 이와 유사한 WKWebView용 API가 있습니다 WKWebViewConfiguration을 통해 동일한 열거형을 지정 가능합니다 WKWebViews의 기본 동작은 .limited입니다 전체 동작을 사용하려면 이를 명시적으로 설정해야 합니다
’isWritingToolsActive’ 속성을 확인하여 글쓰기 도구 세션이 활성 상태인지 파악할 수 있습니다 글쓰기 도구에서 무시할 범위를 제공하는 방법을 살펴보겠습니다
메모 작성 앱을 구현 중이라고 가정해 보죠 이 앱에서 특정 범위를 코드 블록으로 표시할 수 있습니다 인용된 콘텐츠를 포함할 수 있는 메일 앱이 있을 수도 있습니다 글쓰기 도구로 해당 범위를 고쳐 쓰고 싶지 않을 수 있겠죠 방법이 있습니다! UITextViewDelegate와 NSTextViewDelegate에 새 위임 메서드를 추가했습니다 무시할 범위를 반환하기만 하면 글쓰기 도구가 해당 범위 내에서는 변경 사항을 제안하지 않습니다
WKWebView의 경우, ’blockquote’, ’pre’ 같은 태그가 글쓰기 도구에서 자동으로 무시됩니다 마지막으로, 앱에 UITextView나 NSTextView 이외의 맞춤형 텍스트 뷰가 있다면요? 편리하게도, 기본 경험을 무료로 쉽게 이용할 수 있습니다 고쳐 쓴 텍스트가 패널에 표시되며 사용자는 이 텍스트를 텍스트 뷰에 복사, 공유, 적용할 수 있습니다
iOS와 iPadOS에서는 맞춤형 텍스트 뷰에서 UITextInteraction을 도입하는 한 콜아웃 막대나 빠른 메뉴에서 글쓰기 도구가 무료로 제공됩니다 UITextInteraction을 사용할 수 없다면 UITextSelectionDisplayInteraction을 UIEditMenuInteraction과 함께 도입할 수도 있습니다 내부적으로 글쓰기 도구는 UITextInput 프로토콜을 사용해 텍스트를 읽고 쓰며 팝오버를 고정합니다 텍스트 상호작용에 대해 더 자세히 알아보시려면 이전의 멋진 WWDC 세션들을 확인하세요
텍스트 상호작용을 사용하지 않는 텍스트 뷰의 경우 UITextInput 프로토콜에 새 선택적 속성 isEditable을 추가했습니다 이를 도입하여 텍스트 뷰가 편집을 지원하는지를 나타내세요
Mac에서는 맞춤형 텍스트 뷰에 대해 빠른 메뉴와 편집 메뉴에 글쓰기 도구가 자동으로 표시됩니다
NSServicesMenuRequestor를 텍스트 뷰에 도입하세요 이것은 시스템이 보기에서 콘텐츠를 읽고 보기에 콘텐츠를 다시 쓸 수 있게 허용하는 프로토콜입니다
NSResponder에서 validRequestor(forSendType:returnType:)를 오버라이드합니다 그러면 빠른 메뉴가 보기에 추가되는 한 글쓰기 도구 메뉴 항목이 무료로 제공됩니다 이렇게 하면 앱을 모든 서비스와 단축어에서도 사용할 수 있게 됩니다 관련 내용은 WWDC21의 ’AppKit의 새로운 기능’을 참고하세요
모든 내용을 코드로 보여 드리겠습니다 텍스트가 포함된 간단한 보기와 복사 기능을 제공하는 메뉴가 있습니다 글쓰기 도구를 제공하려면 validRequestor(forSendType:returnType:)를 오버라이드하고 NSServicesMenuRequestor에서 writeSelection을 구현합니다 보기가 편집 가능한 텍스트 뷰이면 readSelection도 구현하세요 그러면 글쓰기 도구가 고쳐 쓴 텍스트를 텍스트 뷰로 전송합니다 validRequestor 메서드에서 sendType을 확인하고 텍스트 뷰에서 서식 있는 텍스트를 처리할지를 결정합니다 이 모든 작업이 완료되면 빠른 메뉴에서 글쓰기 도구와 서비스가 모두 제공됩니다 이상으로 세션을 마무리하겠습니다 다음 단계는 무엇일까요?
여러분의 앱에서 글쓰기 도구를 사용해 보세요 대부분의 경우 아무것도 하지 않아도 곧바로 작동합니다!
새로운 위임 메서드를 도입해 글쓰기 도구 세션 중에 앱 상태를 제어하세요 앱에 특별한 요구 사항이나 기능이 있는 경우 글쓰기 도구의 동작과 허용되는 입력 옵션을 확인하세요
맞춤형 텍스트 뷰가 있는 경우 글쓰기 도구 기능을 제공할 수 있습니다 단 몇 단계만 거치면 됩니다 그리고 마지막으로 글쓰기를 즐기세요! 시청해 주셔서 감사합니다!
-
-
5:28 - Text view delegate methods for Writing Tools
func textViewWritingToolsWillBegin(_ textView: UITextView) { // Take necessary steps to prepare. For example, disable iCloud sync. } func textViewWritingToolsDidEnd(_ textView: UITextView) { // Take necessary steps to recover. For example, reenable iCloud sync. } if !textView.isWritingToolsActive { // Do work that needs to be avoided when Writing Tools is interacting with text view // For example, in the textViewDidChange callback, app may want to avoid certain things when Writing Tools is active }
-
7:11 - Opt-out of the full experience
textView.writingToolsBehavior = .limited textView.writingToolsBehavior = .none
-
7:31 - Specify accepted text formats
textView.writingToolsAllowedInputOptions = [.plainText] textView.writingToolsAllowedInputOptions = [.plainText, .richText, .table]
-
7:55 - WKWebView
// For `WKWebView`, the `default` behavior is equivalent to `.limited` extension WKWebViewConfiguration { @available(iOS 18.0, *) open var writingToolsBehavior: UIWritingToolsBehavior { get set } } extension WKWebViewConfiguration { @available(macOS 15.0, *) open var writingToolsBehavior: NSWritingToolsBehavior { get set } } extension WKWebView { /// @discussion If the Writing Tools behavior on the configuration is `.limited`, this will always be `false`. @available(iOS 18.0, macOS 15.0, *) open var isWritingToolsActive: Bool { get } }
-
8:48 - Protecting ranges
// Returned `NSRange`s are relative to the substring of the textView’s textStorage from `enclosingRange` func textView(_ textView: UITextView, writingToolsIgnoredRangesIn enclosingRange: NSRange) -> [NSRange] { let text = textView.textStorage.attributedSubstring(from: enclosingRange) return rangesInappropriateForWritingTools(in: text) }
-
9:58 - Indicate your text view supports editing
protocol UITextInput { @available(iOS 18.0, macOS 15.0, *) optional var isEditable: Bool { get } }
-
10:58 - Simple view that supports Copy
class CustomTextView: NSView, NSServicesMenuRequestor { required init?(coder: NSCoder) { super.init(coder: coder) self.menu = NSMenu() self.menu?.addItem(NSMenuItem(title: "Custom Text View", action: nil, keyEquivalent: "")) self.menu?.addItem(NSMenuItem(title: "Copy", action: #selector(copy(_:)), keyEquivalent: "")) } override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // Custom text drawing code... } }
-
11:05 - View extension to support Writing Tools
class CustomTextView: NSView, NSServicesMenuRequestor { override func validRequestor(forSendType sendType: NSPasteboard.PasteboardType?, returnType: NSPasteboard.PasteboardType?) -> Any? { if sendType == .string || sendType == .rtf { return self } return super.validRequestor(forSendType: sendType, returnType: returnType) } nonisolated func writeSelection(to pboard: NSPasteboard, types: [NSPasteboard.PasteboardType]) -> Bool { // Write plain text and/or rich text to pasteboard return true } // Implement readSelection(from pboard: NSPasteboard) as well for editable view }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.