SwiftUI框架详细解析 (二十八) —— 基于SwiftUI的文字识别(一)

版本记录

版本号 时间
V1.0 2022.02.23 星期三

前言

今天翻阅苹果的API文档,发现多了一个框架SwiftUI,这里我们就一起来看一下这个框架。感兴趣的看下面几篇文章。
1. SwiftUI框架详细解析 (一) —— 基本概览(一)
2. SwiftUI框架详细解析 (二) —— 基于SwiftUI的闪屏页的创建(一)
3. SwiftUI框架详细解析 (三) —— 基于SwiftUI的闪屏页的创建(二)
4. SwiftUI框架详细解析 (四) —— 使用SwiftUI进行苹果登录(一)
5. SwiftUI框架详细解析 (五) —— 使用SwiftUI进行苹果登录(二)
6. SwiftUI框架详细解析 (六) —— 基于SwiftUI的导航的实现(一)
7. SwiftUI框架详细解析 (七) —— 基于SwiftUI的导航的实现(二)
8. SwiftUI框架详细解析 (八) —— 基于SwiftUI的动画的实现(一)
9. SwiftUI框架详细解析 (九) —— 基于SwiftUI的动画的实现(二)
10. SwiftUI框架详细解析 (十) —— 基于SwiftUI构建各种自定义图表(一)
11. SwiftUI框架详细解析 (十一) —— 基于SwiftUI构建各种自定义图表(二)
12. SwiftUI框架详细解析 (十二) —— 基于SwiftUI创建Mind-Map UI(一)
13. SwiftUI框架详细解析 (十三) —— 基于SwiftUI创建Mind-Map UI(二)
14. SwiftUI框架详细解析 (十四) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(一)
15. SwiftUI框架详细解析 (十五) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(二)
16. SwiftUI框架详细解析 (十六) —— 基于SwiftUI简单App的Dependency Injection应用(一)
17. SwiftUI框架详细解析 (十七) —— 基于SwiftUI简单App的Dependency Injection应用(二)
18. SwiftUI框架详细解析 (十八) —— Firebase Remote Config教程(一)
19. SwiftUI框架详细解析 (十九) —— Firebase Remote Config教程(二)
20. SwiftUI框架详细解析 (二十) —— 基于SwiftUI的Document-Based App的创建(一)
21. SwiftUI框架详细解析 (二十一) —— 基于SwiftUI的Document-Based App的创建(二)
22. SwiftUI框架详细解析 (二十二) —— 基于SwiftUI的AWS AppSync框架的使用(一)
23. SwiftUI框架详细解析 (二十三) —— 基于SwiftUI的AWS AppSync框架的使用(二)
24. SwiftUI框架详细解析 (二十四) —— 基于SwiftUI的编辑占位符的使用(一)
25. SwiftUI框架详细解析 (二十五) —— 基于SwiftUI的编辑占位符的使用(二)
26. SwiftUI框架详细解析 (二十六) —— 基于SwiftUI和Xcode12的Multiplatform App的搭建(一)
27. SwiftUI框架详细解析 (二十七) —— 基于SwiftUI和Xcode12的Multiplatform App的搭建(二)

开始

首先看下主要内容:

了解如何将 iPhone 摄像头中的文本捕捉到您的 SwiftUI 应用程序中,以便您的用户可以更快速、更轻松地输入数据。内容来自翻译。

接着看下写作环境:

Swift 5.5, iOS 15, Xcode 13

下面就是正文了

您的 iPhone 相机可以让您捕捉风景、人物和事件,但它也是一个有用的信息收集工具。您会看到一张音乐会海报或您需要的服务的广告或一家看起来很有趣的餐厅,然后拍照。稍后,您在搜索引擎或新联系人中键入或说出照片中的一些文本(URL、日期、电话号码)。

但是键盘或语音输入很容易出错,要是您可以从照片或直接从相机视图中复制和粘贴文本该多好?更好的是,要是您可以将照片或相机视图中的文本直接扫描到您的应用程序中会怎样?热烈欢迎 iOS 15实时文本!

在本教程中,您将学习如何将 iPhone 相机中的文本捕获到您的 SwiftUI应用程序中,让您的用户更快速、更轻松地输入数据。

注意:您应该熟悉使用 SwiftUI、SwiftXcode 开发 iOS 应用程序。您需要 Xcode 13 和运行 iOS 15 的 iPhone。您的 iPhone 必须是 2018 年或之后生产的,因此它具有 A12(或更高版本)神经引擎(Neural Engine)。其首选语言列表必须至少包括以下语言之一:英语、西班牙语、中文、法语、意大利语、德语、葡萄牙语。并检查您所在地区的此列表check this list for your region。


iOS 15 Live Text

精彩的新 iOS 15 实时文本功能开箱即用,并且仅适用于上述注释中列出的语言和地区。

您需要一部新的 (2018+) iPhone,配备 A12 或更高版本的神经引擎。 Live Text 使用 Apple 的 Vision 机器学习模型,该模型需要神经引擎。

Live Text 适用于iPhone XS, iPhone XR and later。 这些 iPhone 不支持实时文本:iPhone SE(第一代)、iPhone 6S、iPhone 6S Plus、iPhone 7、iPhone 7 Plus 和 iPhone X

Live Text 可以在 2018 年或之后的 iPad 上的照片中使用,但在 iPad 相机上则不行not iPad Camera。 这篇文章是关于使用带有相机的实时文本,所以它只是关于 iPhone。

现在拿起你的 iPhone 并确保 Live Text 已打开:在 Settings 中,打开 Camera ▸ Live Text 和 General ▸ Language & Region ▸ Live Text

注意:如果您没有看到这些设置,则您的 iPhone 没有神经引擎。 如果您正在寻找购买 iPhone 13 的借口,那这个接口就不错!

1. Live Text in Photos

实时文本检测照片和Camera ▸ Photo取景器中的文本。 在本教程中,您将使用 iPhone 的相机。 但首先,看看它在您现有的照片上有多棒。 在手持相机视图中没有移动的图像上使用实时文本也更容易练习。

打开照片并找到包含一些文字的照片,尤其是 URL、电话号码、电子邮件或街道地址。

我有这张我在yarn expo上拍的照片,作为提醒我以后想查找的供应商。

我点击了Live Text按钮(取景器正方形中的三行); 它变成了蓝色。 然后我点击了 tarndie.com,他们的网页在 Safari 中打开了!

如果您的照片中有地图地址,点击它会打开地图Maps。 点击电话号码会显示呼叫、发送消息、FaceTime 等常用菜单。

如果有一个电子邮件地址,点击它会在您的电子邮件应用程序中打开一条新消息。

如果您想从不允许您选择文本的应用程序中复制文本,只需截取屏幕截图并打开预览照片:

注意:感谢 Harshil Shah 发推文tweeting。 截屏来自 Chris Wu 的Museum Shuffle。

Live Text在相机Camera应用程序中的原理相同,但您需要稳定的手。 如果你不能让它专注于你想要的东西,只需拍张照片,然后使用Live Text

现在继续阅读以了解如何在应用程序中使用Live Text

打开demo文件夹,在 starter 文件夹中打开 WaitForIt 项目。 这是一个简单的应用程序,您可以在其中记录您需要等待某人生日的时间。 它使用新的 Date.RelativeFormatStyle 方法 relative(presentation:unitsStyle:)

要获得相机输入,您必须在您的 iPhone-with-Neural-Engine 上运行此应用程序。

2. Build and Run on Your Device

使用电线将 iPhone 连接到 Mac。 在targetSigning & Capabilities 选项卡中,自定义 Bundle Identifier 并设置 Team

从运行目标菜单中选择您的 iPhone,然后构建并运行。

以这种格式写下或输入您的姓名和生日:

相机输入适用于手写,但根据我的经验,书写需要非常清晰,更像是仔细打印而不是草书。

点击 + 按钮。 在 Add Person 视图中,点击 Nametext field,然后再次点击它以显示 Scan Text 按钮:

注意:您可能只会在粘贴按钮旁边看到扫描按钮图标。 这往往会在您使用扫描按钮几次之后发生,并且系统决定您不再需要文本标签。

点击此按钮打开相机并将相机对准您的姓名和生日文本:

检测到的文本周围出现括号,检测到的文本也出现在text field中。 括号和text field文本可以随着您的手移动相机而改变,检测不同数量的文本。

您可以点击以指示您希望相机聚焦的位置,您可以在相机视图上向上拖动以放大它:

如果您只想要部分检测到的文本,请点击右下角的扫描按钮以显示文本:

然后点击或滑动以从检测到的文本中选择您想要的内容:

然后点击Insert以接受您选择的文本:

现在以相同的方式添加生日文本,然后点击Done返回列表视图:

3. It’s Magic

现在查看 AddPersonView.swift 中的代码。代码中绝对没有关于从相机扫描文本的内容。此功能是 iOS 15 的一部分,您可以在任何可编辑的视图中免费使用。

那么这篇文章的其余部分是什么?改善用户体验的几个功能:

  • 针对特定文本内容类型(如日期、电话号码和电子邮件地址)过滤相机输入。
  • 显示Scan Text按钮以使您的用户可以看到相机输入功能。

您还可以实现Scan Text按钮来创建不是text field or text view的可编辑视图,例如WWDC presentation。


Filtering Text Content Types

您可能对这种扫描、点击过程有点不知所措。如果您的应用正在寻找特定格式的信息——URL、日期、电子邮件或电话号码——您希望相机仅“看到”相关文本而忽略其余文本。

您的应用程序可能已经指定了键盘到了类型,以便用户更方便地输入数字或电子邮件地址。也许您还指定文本内容类型来指导键盘的建议和自动填充。

好消息:您可以使用文本内容类型来过滤相机文本输入!

1. Filtering Date Text

首先将此修饰符添加到 AddPersonView.swift 中的second (Birthday) TextField

.textContentType(.dateTime)

这告诉系统您希望输入文本是某种形式的日期、时间或持续时间。 神经引擎的Vision模型将使用此提示来过滤相机的日期或时间文本输入。

有几种与人名相关的文本内容类型,那么为什么不修改Name text field呢? 好吧,目前,相机输入仅适用于少数文本内容类型。

在属性检查器文本内容类型菜单中的所有Text Content Type中,相机当前仅过滤 fullStreetAddress、telephoneNumber、emailAddress、URL、shippingTrackingNumber、flightNumberdateTime

好的,是时候看看你的修饰符是否有帮助。

在您的设备上构建并运行,然后点击 + 按钮。 在Add Person视图中,点击Birthday text field ,然后再次点击它:

注意:与Scan Text按钮标签一样,您可能会看到Paste | scan-button-icon图标而不是Scan Date or Time。 日期过滤器仍然有效。

现在相机只突出显示与日期或时间相关的文本:

和以前一样,任何检测到的日期或时间文本都会立即出现在text field中。您仍然必须点击Insert以接受文本。

从相机加速文本输入的好方法!

注意:我添加了一个电子邮件地址来试用该文本内容类型。如果将 dateTime 更改为 emailAddress,相机将只关注电子邮件地址。


Display a Camera Button

到目前为止,所有内容都内置在 iOS 15 中。但您也可以将相关代码添加到您的应用程序中。

例如,为了让您的用户更容易看到相机输入功能,您可以添加一个按钮来设置整个神奇过程。一旦您知道魔法是如何发生的,您就可以使用它将来自相机的文本扫描到不是text fields or text views的视图中。

1. Magic Method

新方法 captureTextFromCamera(responder:identifier:)是魔法的关键,它在您的应用调用此方法启动相机时开始。响应者必须遵循 UIResponderUIKeyInput。响应者使用 UIKeyInput 方法来实现简单的文本输入。

哦,UI 前缀……确实,captureTextFromCamera(responder:identifier:) 是一个 UIAction,所以你需要一个 UIView来调用它。您将创建一个 AddPersonView 可以显示的 UIButton。您将将此按钮的操作设置为 captureTextFromCamera(responder:identifier:)。并且动作的responder将从相机捕获的任何文本传递到 AddPersonView 中的 TextField

2. UIViewRepresentable

要创建可以在 SwiftUI 应用程序中使用的 UIView,您需要构建一个符合 UIViewRepresentable 的结构。

注意:在 SwiftUI Apprentice 和 SwiftUI by Tutorials中了解更多关于 UIViewRepresentable 的信息。

首先,创建一个新的 Swift 文件并将其命名为 ScanButton。在这个新文件中,将 import Foundation 替换为以下代码:

import SwiftUI

struct ScanButton: UIViewRepresentable {
  func makeUIView(context: Context) -> UIButton {
    let button = UIButton()
    return button
  }

  func updateUIView(_ uiView: UIButton, context: Context) { }
}

为了遵循 UIViewRepresentableScanButton 必须实现 makeUIView(context:)updateUIView(_:context:)

在这个最小形式中,makeUIView(context:)只是创建了一个 UIButtonAddPersonView 不会更新按钮,所以 updateUIView(_:context:)是空的。

3. Coordinator

点击可以捕获 ScanButton 必须传递给 AddPersonView 的文本的按钮。 要将数据从 UIView 传输到 SwiftUI ViewScanButton 需要一个coordinator

ScanButton 中添加此代码:

func makeCoordinator() -> Coordinator {
  Coordinator(self)
}

class Coordinator: UIResponder, UIKeyInput {
  let parent: ScanButton
  init(_ parent: ScanButton) { self.parent = parent }

  var hasText = false
  func insertText(_ text: String) { }
  func deleteBackward() { }
}

ScanButtonmakeUIView(context:) 之前调用 makeCoordinator()并将 Coordinator 对象存储在context.coordinator 中。

行为captureTextFromCamera(responder:identifier:)需要符合 UIKeyInputUIResponder 参数,因此您将 Coordinator 设为 UIResponder 的子类并添加 UIKeyInput 协议。 实现此协议将使协调器能够控制文本输入。

UIKeyInput 要求您提供 hasText、insertText(_:)deleteBackward()。 您需要相机输入而不是键盘输入,因此您只需实现 insertText(_:)即可处理相机输入。 hasText 的值无关紧要,所以设置为 false。 而 deleteBackward()不需要做任何事情。

Coordinator 的目的是将文本从相机传回调用 ScanButtonSwiftUI 视图,因此 ScanButton 需要绑定到 SwiftUI 视图中的 String 属性。

ScanButton 顶部添加此属性:

@Binding var text: String

AddPersonView 会将 $name$birthday 传递给 ScanButton

现在您可以完成 Coordinator 的设置。 将此行添加到 insertText

parent.text = text

是的,这确实是 Coordinator 需要做的所有事情!

4. Setting the Button’s Action

现在回到制作你的 UIButton

makeUIView(context:)中,将button声明替换为以下内容:

let textFromCamera = UIAction.captureTextFromCamera(
  responder: context.coordinator,
  identifier: nil)
let button = UIButton(primaryAction: textFromCamera)

您创建一个 UIAction 以使用您的 Coordinator 对象作为responder从相机捕获文本。

然后,使用此操作action作为主要操作创建按钮。 这会将按钮的标题和图像设置为操作action的标题和图像。

那么这看起来像什么? 设置预览以查看…

ScanButton 下方添加此代码:

struct ScanButton_Previews: PreviewProvider {
  static var previews: some View {
    ScanButton(text: .constant(""))
      .previewLayout(.sizeThatFits)
  }
}

出现预览画布。

如果该应用程序仍在您的手机上运行,请停止它。

在运行目标菜单中选择一个模拟器,然后按 Option-Command-P 或单击 Resume 以查看:

sizeThatFits 实际上是所选模拟器的全屏尺寸。 您将在 AddPersonView 中通过为其frame设置宽度和高度值来修剪它。

5. Adding ScanButton to AddPersonView

现在回到 AddPersonView.swift。 有两种方法可以在这里使用 ScanButton

一种方法是在text field旁边显示按钮。

用这个 HStack 替换 Name TextField

HStack {
  TextField("Name", text: $name)
  ScanButton(text: $name)
    .frame(width: 100, height: 56, alignment: .leading)
}

您将绑定传递给 name,设置按钮的宽度和高度,并确保操作的图像位于按钮frameleading边缘。

刷新预览以查看您的按钮:

显示按钮的另一种方法是在键盘工具栏中。

将此修饰符添加到Birthday TextField

.toolbar {
  ToolbarItemGroup(placement: .keyboard) {
    Spacer()
    ScanButton(text: $birthday)
  }
}

这一次,您将绑定传递给birthday。 工具栏约束按钮的frame,您插入 Spacer()以将按钮推到工具栏的后沿。

运行实时预览,然后点击Birthday text field以在键盘工具栏中查看您的按钮:

注意:实际上,此屏幕截图来自模拟器。 键盘不会出现在 Xcode 13 的实时预览中。

现在,关闭实时预览并在您的设备上运行该应用程序以试用这两个按钮。

您的 ScanButton 的行为与内置扫描按钮不完全相同。相机检测到的文本不会立即出现在text field中。此外,Birthday Scan Text按钮会忽略text field的文本内容类型设置。至少,Xcode 13iOS 15RC 版本会发生这种情况。您可能会在更高版本的 Xcode/iOS 中看到更好的结果。

双击Birthday text field,然后使用内置的扫描按钮,仍然会过滤日期文本。


Scan Text Into Title

ScanButton 可帮助您的用户了解此输入选项,但这并不是必需的。一旦用户了解了相机输入,他们就会期望将它与任何text field or text view一起使用。这很有效,不需要你的帮助。

如果您想将文本扫描进label之类的东西怎么办?没问题,ScanButton 不关心它在哪里插入捕获的文本:只需给它一个 String 变量来插入文本,然后调用视图可以在任何它想要的地方使用这个 String 值。

例如,您可以使用扫描文本来更改 AddPersonView 的导航标题。

ScanButton 中,添加此 @Binding

@Binding var title: String

insertText(_:)中,加下面这行:

parent.title = "Add \(text)"

previews中,将 ScanButton(text:)替换为:

ScanButton(text: .constant(""), title: .constant(""))

现在,AddPersonView.swift 警告参数title缺少参数。

将此@State 属性添加到 AddPersonView

@State private var title = "Add a Person"

Name text fieldHStack 中,将 ScanButton(text:)替换为以下行:

ScanButton(text: $name, title: $title)

Birthday text fieldtoolbar中,将 ScanButton(text:) 替换为以下行:

ScanButton(text: $birthday, title: .constant(title))

您不希望birthday text影响视图的导航标题。

最后,将 .navigationTitle("Add a Person")更改为:

.navigationTitle(title)

在您的手机上构建并运行,点击 + 按钮,然后点击Name text field旁边的Scan Text按钮。 扫描名称并将其插入。

导航标题更改以匹配插入的名称:

这仅适用于您的Scan Text按钮。 双击Name text field,然后使用该扫描按钮,不会更改标题。

还要检查将文本扫描到Birthday text field不会影响导航标题。


Button Menu

如果您希望Scan Text选项出现在按钮菜单中,或者您只想让扫描按钮占用更小的空间,请更改创建 ScanButton 的方式。

修改 ScanButton 中的 makeUIView(context:) :注释掉button声明并添加以下代码:

let button = UIButton()
button.setImage(
  UIImage(systemName: "camera.badge.ellipsis"),
  for: .normal)
button.menu = UIMenu(children: [textFromCamera])

您将button图像设置为一个小的 SF 符号,并将 textFromCamera 操作添加到按钮的菜单中。

AddPersonView.swift 中,将 Name text fieldScanButton 宽度更改为 56(或更小):

ScanButton(text: $name, title: $title)
  .frame(width: 56, height: 56, alignment: .leading)

在实时预览、模拟器或您的设备上查看这一点。 现在,长按会显示Scan Text选项。

在本教程中,您练习了在照片中使用实时文本Live Text,并使用内置的扫描文本功能从手机摄像头获取文本输入。 然后,您指定文本字段的 textContentType 以使 Vision 模型过滤相机输入以获取该格式的文本。

为了让用户更容易看到相机输入功能,您创建了一个 UIViewRepresentable 按钮来启动 captureTextFromCamera(responder:identifier:)。 对于text fields and text views,这提供了类似于内置扫描文本按钮的功能。 您可以轻松扩展按钮以将文本扫描到不可编辑的视图中。 最后,您修改了您的按钮,使其具有一个菜单,其中包含扫描文本作为其项目之一。


苹果资源

  • WWDC 2021 视频:Use a camera for keyboard input in your app
  • 了解响应者链* Understanding the responder chain
  • 从相机捕获文本Capture text from camera:在撰写本文时这里没有太多内容,但希望 Apple 会尽快添加更多内容。

后记

本篇主要讲述了基于SwiftUI的文字识别,感兴趣的给个赞或者关注~~~

你可能感兴趣的:(SwiftUI框架详细解析 (二十八) —— 基于SwiftUI的文字识别(一))