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 在任何 UITextField、UITextView 或 UISearchBar 上手写,系统会实时将手写内容转换为文本。

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