Meet Scribble for iPad
Swift & UI 入门 22m

认识 iPad 上的 Scribble 手写输入

Meet Scribble for iPad

2020年6月24日

在 Apple 官方观看视频

一句话判断

Scribble 让用户用 Apple Pencil 在任何文本框中直接手写输入文字,系统会实时转换为打印体。如果你的 App 有自定义的文本输入控件,需要确保它们正确支持 Scribble。

这场 Session 讲了什么

iPadOS 14 引入了 Scribble,这是一个系统级的 Apple Pencil 手写识别功能。用户不需要切换输入法,直接用 Apple Pencil 在任何 UITextFieldUITextViewUISearchBar 上手写,系统会实时将手写内容转换为文本。

Session 详细介绍了 Scribble 的工作原理。当用户在文本框中开始手写时,系统会捕获笔画数据,通过 on-device 的机器学习模型进行识别,然后模拟键盘输入将识别结果插入到文本框中。对开发者来说,Scribble 的工作方式与键盘输入几乎完全一致——你的 UITextInput 协议实现不需要任何改动就能支持 Scribble。但如果你有自定义的文本输入视图,需要注意一些适配细节。Session 还介绍了如何使用 UIScribbleInteraction 来自定义 Scribble 的行为。

值得深挖的点

自动输入与智能识别

Scribble 不只是简单的手写转文字。它支持智能识别和自动修正:手写的电话号码会自动识别为电话号码格式,手写的地址会自动识别为地址,手写的日期会自动识别为日期。用户还可以用手势来编辑文字:划掉一个词可以删除它,在文字之间画一条竖线可以插入空格,圈住一段文字可以选中它。这些手势交互对用户来说非常自然,但需要你的文本控件正确支持 UITextInput 协议。

自定义 Scribble 行为

通过 UIScribbleInteraction,你可以控制 Scribble 在特定视图中的行为。比如在一个绘图 App 中,你可能想禁用 Scribble(因为 Pencil 应该用来画画而不是写字),或者只在特定区域启用 Scribble。UIScribbleInteraction 的 delegate 可以返回 false 来禁止 Scribble,也可以在 Scribble 开始和结束时收到回调。

代码片段

基本的 Scribble 支持(无需额外代码)

场景:标准的 UIKit 文本控件自动支持 Scribble。

import UIKit

class FormViewController: UIViewController {
    
    // 这些标准控件自动支持 Scribble,无需额外配置
    private let nameField = UITextField()
    private let emailField = UITextField()
    private let notesView = UITextView()
    private let searchBar = UISearchBar()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupForm()
    }
    
    private func setupForm() {
        // UITextField、UITextView、UISearchBar 都自动支持 Scribble
        // 用户用 Apple Pencil 直接在这些控件上写字即可
        
        nameField.placeholder = "输入姓名"
        emailField.placeholder = "输入邮箱"
        emailField.keyboardType = .emailAddress  // Scribble 会智能识别邮箱格式
        
        notesView.font = .systemFont(ofSize: 16)
        notesView.isScrollEnabled = true
    }
}

使用 UIScribbleInteraction 自定义行为

场景:在绘图 App 中禁止 Scribble。

import UIKit

class CanvasViewController: UIViewController {
    
    private let canvasView = CanvasView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCanvas()
    }
    
    private func setupCanvas() {
        // 为画布视图添加 Scribble 交互
        let scribbleInteraction = UIScribbleInteraction(delegate: self)
        canvasView.addInteraction(scribbleInteraction)
    }
}

extension CanvasViewController: UIScribbleInteractionDelegate {
    
    // 控制是否允许 Scribble
    func scribbleInteractionShouldBegin(_ interaction: UIScribbleInteraction) -> Bool {
        // 在画布区域禁止 Scribble,让 Pencil 用于绘画
        return false
    }
    
    // 可选:监听 Scribble 状态变化
    func scribbleInteractionDidBegin(_ interaction: UIScribbleInteraction) {
        // Scribble 开始(如果上面返回了 true)
        print("用户开始手写输入")
    }
    
    func scribbleInteractionDidFinish(_ interaction: UIScribbleInteraction) {
        // Scribble 结束
        print("手写输入完成")
    }
}

部分区域启用 Scribble

场景:绘图 App 中只有文本标注区域支持 Scribble。

class AnnotationCanvasView: UIView {
    
    private var textAnnotations: [CGRect] = []  // 文本标注区域
    
    func addTextAnnotation(at frame: CGRect) {
        textAnnotations.append(frame)
        
        // 在标注区域添加文本框
        let textField = UITextField(frame: frame)
        textField.placeholder = "添加标注..."
        textField.borderStyle = .roundedRect
        addSubview(textField)
    }
}

extension AnnotationCanvasView: UIScribbleInteractionDelegate {
    
    func scribbleInteractionShouldBegin(_ interaction: UIScribbleInteraction) -> Bool {
        // 只在文本标注区域允许 Scribble
        // 检查 Pencil 的当前位置是否在标注区域内
        // 交互开始时会提供一个参考点来判断
        return true  // 让系统根据是否在文本框上来决定
    }
}

处理 Scribble 的编辑手势

场景:确保自定义文本输入正确处理 Scribble 的编辑手势。

// Scribble 支持的手势:
// 1. 划掉文字 -> 删除
// 2. 竖线 -> 插入空格
// 3. 圈选 -> 选中文字
// 4. 加入符号(如 &、@)-> 自动识别

// 如果你的自定义视图实现了 UITextInput 协议,
// 这些手势会自动工作。确保以下方法正确实现:

class CustomTextInputView: UIView, UITextInput {
    
    // UITextInput 协议的关键方法
    // Scribble 依赖这些方法来定位光标和选择范围
    
    var selectedTextRange: UITextRange?
    
    func text(in range: UITextRange) -> String? {
        // 返回指定范围内的文本
        return nil
    }
    
    func replace(_ range: UITextRange, withText text: String) {
        // Scribble 识别的文字会通过这个方法插入
        // 实现文本替换逻辑
    }
    
    // 必须正确实现 selectionRects 方法
    func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
        // 返回选择范围的矩形,Scribble 用它来高亮手写区域
        return []
    }
}

最佳实践

已有项目:在 iPad 上用 Apple Pencil 测试你 App 的所有文本输入场景。标准 UIKit 控件应该开箱即用。如果有自定义的文本输入视图,确保完整实现了 UITextInput 协议。绘图类 App 应该添加 UIScribbleInteraction 并在画布区域禁用 Scribble。

新项目:使用标准的 UIKit 文本控件,它们已经完美支持 Scribble。如果必须自定义文本输入,从第一天就确保 UITextInput 协议的完整实现。在 QA 流程中加入 Apple Pencil 手写测试。

还有什么值得关注

  • Scribble 支持多种语言的手写识别,包括中英文混写。
  • Scribble 的识别完全在设备本地完成,不需要网络连接。
  • iPadOS 14 的 “数据探测器”(Data Detectors)可以自动识别手写的电话号码、地址、日期等。
WWDC 2020