WWDC Quick Look 💓 By SwiftGGTeam
Add Live Text interaction to your app

Add Live Text interaction to your app

观看原视频

Highlight

iOS 16 的 VisionKit 让 App 可以把 Live Text 加到静态图片和暂停视频帧中,用户能直接选择文字、翻译内容、触发地址电话等数据检测,并与 QR 码交互。


核心内容

一个普通图片查看器通常只负责显示、缩放和拖动图片。图片里有电话号码、地址、网址或 QR 码时,用户只能手动抄写,或者先保存到系统相册再使用 Live Text。

这场 session 讲的是 iOS 16 和 macOS 13 的 VisionKit 接口。它把系统 Live Text 的交互层开放给 App。开发者提供图片,ImageAnalyzer 做异步分析,结果保存为 ImageAnalysis,再交给 ImageAnalysisInteraction 或 macOS 的 ImageAnalysisOverlayView 显示交互层(01:33)。

演示项目是一个放在 scroll view 里的图片查看器。加入 Live Text 后,用户可以选中文字、复制内容、查找、翻译,点击电话号码拨号,点击地址打开地图,也可以和 QR 码交互(04:20)。

API 的重点是控制权。你可以使用 .automatic 获得系统默认行为,也可以只启用文字选择,或只启用数据检测。底部的 Live Text 按钮和 Quick Actions 也能隐藏、调整边距、使用自定义字体(05:26)。

详细内容

1. 最小接入:分析图片,再把结果交给 interaction(02:37

官方代码把接入流程压缩成三个对象:ImageAnalyzerImageAnalysisInteractionImageAnalyzer.Configuration。图片变化时先清掉旧分析结果,再启动新的异步分析。分析完成后,还要确认当前显示的图片没有被换掉。

import UIKit
import VisionKit

class LiveTextDemoController: BaseController, ImageAnalysisInteractionDelegate, UIGestureRecognizerDelegate {
   
    let analyzer = ImageAnalyzer()
    let interaction = ImageAnalysisInteraction()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        imageview.addInteraction(interaction)
    }
    
    override var image: UIImage? {
        didSet {
            interaction.preferredInteractionTypes = []
            interaction.analysis = nil
            analyzeCurrentImage()
        }
    }
    
    func analyzeCurrentImage() {
        if let image = image {
            Task {
               let configuration = ImageAnalyzer.Configuration([.text, .machineReadableCode])
                do {
                    let analysis = try await analyzer.analyze(image, configuration: configuration)
                    if let analysis = analysis, image == self.image {
                        interaction.analysis = analysis;
                        interaction.preferredInteractionTypes = .automatic
                    }
                }
                catch {
                    // Handle error…
                }
            }
        }
    }
}

关键点:

  • import VisionKit 引入 Live Text 所在框架。
  • let analyzer = ImageAnalyzer() 创建图片分析器。session 建议整个 App 共享一个 analyzer,以便更好利用系统资源(12:12)。
  • let interaction = ImageAnalysisInteraction() 创建 iOS 和 macOS 上的交互对象。
  • imageview.addInteraction(interaction) 把交互层挂到承载图片的 view 上。
  • 图片变化时,preferredInteractionTypes = []analysis = nil 清理旧图片的交互状态。
  • ImageAnalyzer.Configuration([.text, .machineReadableCode]) 告诉 analyzer 同时查找文字和机器可读码。
  • try await analyzer.analyze(image, configuration: configuration) 异步生成分析结果。
  • image == self.image 防止异步分析完成时,界面已经切换到另一张图片。
  • interaction.analysis = analysis 把识别结果交给交互层。
  • preferredInteractionTypes = .automatic 使用系统默认 Live Text 行为。

这个例子也解释了为什么要先清空旧状态。分析过程会花费一段不确定时间。用户可能在分析过程中切到下一张图。如果不检查当前图片,旧分析结果可能覆盖新图片。

2. 交互类型:在文字选择和数据检测之间取舍(05:26

preferredInteractionTypes 决定用户能做什么。.automatic 会提供文字选择,并在 Live Text 按钮激活时为数据检测项加下划线,行为和系统内建 App 一致。

概念示例:

// Conceptual Swift based on the session transcript.
interaction.preferredInteractionTypes = .automatic

关键点:

  • .automatic 对应 session 推荐给大多数 App 的默认行为。
  • 文本可以被选择、复制、查找和翻译。
  • Live Text 按钮激活后,地址、电话号码、URL 等数据检测项会出现一键操作。

如果 App 只想让用户选择文字,可以改成 .textSelection。在这个模式下,Live Text 按钮状态不会改变交互类型。

// Conceptual Swift based on the session transcript.
interaction.preferredInteractionTypes = .textSelection

关键点:

  • .textSelection 只保留文字选择。
  • 数据检测不会因为 Live Text 按钮状态自动出现。
  • 适合图片编辑、标注等需要减少误触的界面。

如果 App 只需要地址、电话、URL 等动作入口,可以使用 .dataDetectors。session 明确提到,这个模式不会显示 Live Text 按钮,因为文字选择已经被关闭。

// Conceptual Swift based on the session transcript.
interaction.preferredInteractionTypes = .dataDetectors

关键点:

  • .dataDetectors 关闭文字选择。
  • 被检测出的项目会直接加下划线。
  • 用户可以一次点击触发对应动作。

还有一个细节:在文字选择或 .automatic 模式中,长按仍然可以触发数据检测。这个行为由 allowLongPressForDataDetectorsInTextMode 控制,默认开启(06:21)。

// Conceptual Swift based on the session transcript.
interaction.allowLongPressForDataDetectorsInTextMode = false

关键点:

  • 属性名说明作用范围:只影响 text mode 下长按触发数据检测。
  • 设置为 false 后,可以减少和 App 自己长按手势的冲突。
  • 文字选择本身不受这个设置影响。

3. 补充界面:处理 Live Text 按钮和 Quick Actions(06:43

Live Text 底部的系统界面由 interaction 管理。右下角是 Live Text 按钮,左下角是 Quick Actions。Quick Actions 来自分析结果中的数据检测项,只有 Live Text 按钮激活时才显示。

如果 App 底部已有自己的工具栏,可以隐藏补充界面,只保留文字选择能力。

// Conceptual Swift based on the session transcript.
interaction.isSupplementaryInterfaceHidden = true

关键点:

  • isSupplementaryInterfaceHidden 控制 Live Text 按钮和 Quick Actions 的可见性。
  • 设置为 true 后,用户仍可在已启用的交互类型范围内操作图片内容。
  • 适合底部 UI 已经很密集的图片浏览器。

如果只是不想遮挡已有按钮,可以调整边距,让系统界面避开 App 内容。

// Conceptual Swift based on the session transcript.
interaction.supplementaryInterfaceContentInsets = UIEdgeInsets(
    top: 0,
    left: 0,
    bottom: 44,
    right: 16
)

关键点:

  • supplementaryInterfaceContentInsets 告诉 VisionKit 哪些边缘需要避让。
  • 底部 inset 可以给 App 自己的工具栏留出空间。
  • 右侧 inset 可以避开悬浮按钮。
  • 这里的数值是概念示例,实际项目应按界面布局计算。

如果 App 使用自定义字体,也可以让 Live Text 按钮和 Quick Actions 使用相同字体。session 提醒:为了保持按钮尺寸一致,Live Text 会忽略 point size(07:52)。

// Conceptual Swift based on the session transcript.
interaction.supplementaryInterfaceFont = UIFont.preferredFont(forTextStyle: .body)

关键点:

  • supplementaryInterfaceFont 影响文字和符号的字体权重。
  • point size 不会被用于按钮尺寸。
  • 这个属性适合需要统一视觉风格的图片查看界面。

4. 非 UIImageView:用 contentsRect 对齐高亮区域(08:10

UIImageView 可以用自己的 contentMode 自动计算图片内容区域。自定义 view 没有这个便利。interaction 的 bounds 可能大于图片实际显示区域,导致文字高亮和图片内容错位。

修正方式是实现 delegate 方法 contentsRectForInteraction,返回单位坐标空间中的图片内容矩形。

// Conceptual Swift based on the session transcript.
func contentsRect(for interaction: ImageAnalysisInteraction) -> CGRect {
    return CGRect(x: 0.1, y: 0.2, width: 0.8, height: 0.6)
}

关键点:

  • contentsRect 使用 unit coordinate space,取值范围按 view bounds 归一化。
  • 返回的矩形描述图片内容相对 interaction bounds 的位置。
  • 示例数值只说明形状,实际项目要用当前布局计算。
  • 当 interaction bounds 变化时,VisionKit 会重新询问这个 rect。

如果图片内容区域改变了,但 interaction 的 bounds 没变,需要主动通知 VisionKit 更新。

// Conceptual Swift based on the session transcript.
interaction.setContentsRectNeedsUpdate()

关键点:

  • setContentsRectNeedsUpdate() 请求重新获取 contents rect。
  • 适合图片裁切、content mode 或自定义布局变化的场景。
  • 不需要重新分析图片,只更新交互层和内容区域的对应关系。

图片放在 scroll view 里时,session 给出明确建议:把 interaction 放在承载图片内容的 view 上,不要放在 scroll view 上(09:18)。scroll view 的可见区域和缩放持续变化,contents rect 会更难维护。

5. 手势冲突:让 App 和 Live Text 各处理自己的事件(10:08

Live Text 自带一组复杂手势。图片浏览器、标注工具、地图截图查看器也可能有自己的点击、长按、拖动手势。冲突出现时,可以从两个方向处理。

第一种方向是阻止 Live Text 在某些点位开始交互。session 建议实现 interaction(_:shouldBeginAt:for:):如果指定点下面没有可交互项目,并且当前没有活跃文字选择,就返回 false,让 Live Text 不处理这次动作。

概念流程:Live Text delegate 决定是否开始交互
1. 收到 interaction、触点 point、interactionType
2. 检查 point 下是否有 Live Text 可交互项目
3. 检查当前是否有活跃文字选择
4. 两者都没有时返回 false
5. 否则返回 true,让 Live Text 继续处理

关键点:

  • interaction(_:shouldBeginAt:for:) 是处理 Live Text 手势冲突的入口。
  • “point 下是否有可交互项目”对应文字、链接、电话、地址或 QR 码等分析结果。
  • “是否有活跃文字选择”用于保留点击空白处取消选择的体验。
  • 返回 false 时,Live Text 不执行对应动作。

第二种方向是让 App 自己的 gesture recognizer 在 Live Text 需要事件时退让。session 的思路是在 gestureRecognizerShouldBegin 里做相同检查。

概念流程:App gesture recognizer 决定是否开始
1. 读取 gestureRecognizer 的触点
2. 先把触点转到 window 坐标
3. 再把触点转到 interaction 所在 view 的坐标
4. 如果该点有 Live Text 可交互项目,返回 false
5. 如果当前有活跃文字选择,返回 false
6. 其他情况返回 true,让 App 自己的手势继续

关键点:

  • 先转到 window 坐标,再转到 interaction view 坐标,对 scroll view 放大场景有帮助。
  • session 特别提醒,如果点位对不上,可以尝试这种坐标转换方式。
  • 返回 false 后,App 自己的手势不开始,Live Text 可以接管。

session 还提到可以覆盖 UIViewhitTest(_:with:) 做类似判断,按命中位置决定事件交给哪个 view(11:46)。

6. 性能和视频帧:减少转换,只分析该分析的内容(12:00

ImageAnalyzer 使用 Neural Engine,效率高,但仍然需要按界面时机调用。session 给出三条规则。

第一,整个 App 尽量共享一个 ImageAnalyzer。第二,传入手头已有的原始图像类型,减少格式转换;如果已有 CVPixelBuffer,它是最有效率的输入。第三,只在图片即将出现或已经出现时分析。时间线场景里,等滚动停止后再开始分析(12:12)。

概念示例:

// Conceptual Swift based on the session transcript.
final class LiveTextAnalyzerStore {
    static let sharedAnalyzer = ImageAnalyzer()
}

关键点:

  • static let 表示 App 内共享同一个 analyzer。
  • 共享 analyzer 避免在多个页面重复创建分析器。
  • 真实项目还要结合页面生命周期取消不再需要的分析任务。

视频有两个入口。使用 AVPlayerViewAVPlayerViewController 时,iOS 16 默认通过 allowsVideoFrameAnalysis 支持暂停帧 Live Text,且只适用于非 FairPlay 保护内容(13:12)。

// Conceptual Swift based on the session transcript.
playerViewController.allowsVideoFrameAnalysis = true

关键点:

  • allowsVideoFrameAnalysis 控制 AVKit 暂停帧分析。
  • session 提到它默认开启。
  • FairPlay 保护内容不支持这个能力。

如果使用 AVPlayerLayer,App 需要自己管理分析和 interaction。关键是用 currentlyDisplayedPixelBuffer 拿当前帧。session 明确说,这是保证分析正确帧的唯一方式。它只在播放速率为零时返回有效值,返回的是 shallow copy,不能写入(13:32)。

// Conceptual Swift based on the session transcript.
if player.rate == 0,
   let pixelBuffer = playerLayer.currentlyDisplayedPixelBuffer {
    Task {
        let configuration = ImageAnalyzer.Configuration([.text, .machineReadableCode])
        let analysis = try await analyzer.analyze(pixelBuffer, configuration: configuration)
        interaction.analysis = analysis
        interaction.preferredInteractionTypes = .automatic
    }
}

关键点:

  • player.rate == 0 对应暂停帧场景。
  • currentlyDisplayedPixelBuffer 读取屏幕上当前显示的视频帧。
  • analyze(pixelBuffer, configuration:) 避免把视频帧先转成其他图片类型。
  • interaction.analysis = analysis 把分析结果交给交互层。
  • 示例没有展示完整错误处理和状态检查,真实项目要像静态图片示例一样处理异步完成后的状态变化。

核心启发

  1. 做什么:给图片笔记 App 增加“选中图片中文字并复制”的能力。 为什么值得做:Live Text 已经负责文字识别、选择手势和复制菜单,App 不需要自己写 OCR 交互层。 怎么开始:在图片 view 上添加 ImageAnalysisInteraction,用 ImageAnalyzer.Configuration([.text]) 分析当前图片。

  2. 做什么:给收据、名片、海报图片增加一键拨号、打开地图、访问网址。 为什么值得做.automatic.dataDetectors 会把电话号码、地址、URL 变成可点击操作。 怎么开始:把 preferredInteractionTypes 设为 .automatic;如果界面只需要动作入口,改用 .dataDetectors

  3. 做什么:在图片查看器里保留自定义底部工具栏,同时启用 Live Text。 为什么值得做:supplementary interface 可以隐藏,也可以通过 content insets 避让现有 UI。 怎么开始:先接入 interaction,再按界面密度选择 isSupplementaryInterfaceHiddensupplementaryInterfaceContentInsets

  4. 做什么:给视频播放器增加暂停帧识别文字和 QR 码。 为什么值得做:AVKit 对 AVPlayerViewAVPlayerViewController 已经提供默认暂停帧分析;自定义 AVPlayerLayer 也能通过当前 pixel buffer 接入同一套分析流程。 怎么开始:AVKit 场景确认 allowsVideoFrameAnalysis;自定义 layer 场景在暂停时读取 currentlyDisplayedPixelBuffer 并交给 ImageAnalyzer

  5. 做什么:给带缩放和手势的图片标注 App 增加可控 Live Text。 为什么值得做:delegate 和 gesture recognizer delegate 能把文字选择、标注拖拽、点击空白处取消选择分开处理。 怎么开始:先把 interaction 放在 image view 上,再用 interactionShouldBeginAtPointgestureRecognizerShouldBegin 处理冲突点位。


关联 Session

评论

GitHub Issues · utterances