xib相关(二十四) —— 使用IBSegueAction提升Storyboard Segues(一)

版本记录

版本号 时间
V1.0 2020.05.23 星期六

前言

iOS中的视图加载可以有两种方式,一种是通过xib加载,另外一种就是通过纯代码加载。它们各有优点和好处,xib比较直观简单,代码比较灵活但是看着很多很乱,上一家公司主要风格就是用纯代码,这一家用的就是xib用的比较多。这几篇我们就详细的介绍一个xib相关知识。感兴趣的可以看上面写的几篇。
1. xib相关(一) —— 基本知识(一)
2. xib相关(二) —— 文件冲突问题(一)
3. xib相关(三) —— xib右侧标签介绍(一)
4. xib相关(四) —— 连线问题(一)
5. xib相关(五) —— 利用layout进行约束之界面(一)
6. xib相关(六) —— 利用layout进行约束之说明和注意事项(二)
7. xib相关(七) —— Storyboard中的segue (一)
8. xib相关(八) —— Size Classes(一)
9. xib相关(九) —— 几个IB修饰符(一)
10. xib相关(十) —— xib的国际化(一)
11. xib相关(十一) —— xib的高冷用法之修改视图的圆角半径、边框宽度和颜色(一)
12. xib相关(十二) —— UIStackView之基本介绍(一)
13. xib相关(十三) —— UIStackView之枚举UIStackViewDistribution使用(二)
14. xib相关(十四) —— UIStackView之UIStackViewAlignment使用(三)
15. xib相关(十五) —— UIStackView之工程实践(四)
16. xib相关(十六) —— UINib之基本介绍(一)
17. xib相关(十七) —— UINib之Introduction(二)
18. xib相关(十八) —— UINib之Nib文件(三)
19. xib相关(十九) —— UINib之Nib文件(四)
20. xib相关(二十) —— UINib之字符串资源(五)
21. xib相关(二十一) —— UINib之图像、声音和视频资源(六)
22. xib相关(二十二) —— UINib之数据资源文件(七)
23. xib相关(二十三) —— 几个xib使用场景示例(一)

开始

首先看下主要内容

在本iOS教程中,您将学习如何使用IBSegueAction进行storyboard segues。您将了解这种新技术的优点和缺点。内容来自翻译 。

下面看下写作环境

Swift 5, iOS 13, Xcode 11

在UIKit中,segue是一个对象,它定义了一个storyboard文件中两个视图控制器之间的转换。它与two-wheeled scooter毫无关系。segueSegway唯一的共同点是它们是同音异义词。

本教程介绍了一种实现名为IBSegueActionUIKit segue的新方法。在iOS13中引入的IBSegueAction允许你在UIKit显示它们之前创建视图控制器。

在本教程中,您将使用基于prepare(for:sender:)pre-iOS 13方法创建两个segue。然后,您将使用@IBSegueAction重构这些相同的segue,以创建端点视图控制器。您将了解IBSegueActions相对于前一种方法的优点和缺点。就一个缺点。

starter文件夹中打开RazeNotes.xcodeproj

RazeNotes是一款简单的笔记应用。它预装了一些有趣的笔记,但你可以随意添加自己的笔记。

构建和运行。点击右上角的new note按钮和列表中的每个note。你会发现什么都没有发生;这是因为应用程序没有任何segue。然而。

要使其工作,您将向new note按钮和列表中的项添加segues


Exploring Segues

在向RazeNotes添加功能之前,最好了解更多关于segue的知识。在UIKit中,segue是一个对象,它定义了一个storyboard文件中两个视图控制器之间的转换。一个动作——例如点击一个按钮、执行一个手势或点击一个表格的单元格行——就会触发seguesegue的端点是你想要显示的视图控制器。

下图演示了连接相同两个视图控制器的两个seguesegue箭头指向端点。

最常见的是Show segue,它模态地将一个视图控制器放在当前视图控制器的顶部。这就是您在本教程中要实现的。

但是对于不同类型的转换,还有其他几种类型的segue。下面的菜单显示了所有不同类型的segue,包括一些不赞成使用的segue。不应该将不赞成的segue类型添加到新项目中。它们是为了向后兼容而存在的。

注意:苹果在其关于segue的文档documentation on segues中讨论了所有不同类型的segue。

在大多数情况下,你需要在segue显示之前配置它的端点视图控制器。在ios13之前,UIKit创建了端点视图控制器,然后你在开始视图控制器的prepare(for:sender:)中配置它。因此,必须在初始化之后对其进行配置。

这有一些缺点,一旦你以两种方式实现了segue,你就能体验到。


Making RazeNotes Segues Work Before iOS 13

是时候做一些开发了!在本节中,您将使用传统的处理segue的方法prepare(for:sender:)来处理新note和编辑note segue。在本节结束时,您将拥有一个可用的RazeNotes应用程序!

首先打开Main.storyboard。然后,点击故事板中的RazeNotes Scene。如果你在Xcode菜单中选择editor - assistant,在assistant菜单中打开NotesListViewController.swift,这一步将会很容易。右边的编辑器应该打开到NotesListViewController.swift

如果助理编辑打开了其他文件,那么在编辑窗口的右上角选择Automatic并选择Top-Level Objects > NotesListViewController.swift

要显示New Note按钮,请在Document OutlineRazeNotes Scene中展开控件的层次结构;它在storyboard screen的左侧。要为新note创建一个segue,可以从Document Outline中的new note按钮Control-dragEdit note视图控制器。从弹出的菜单中,选择Show segue类型。

选择新的segue。然后打开Attributes inspector,这样就可以用newNoteSegue填充Identifier属性。稍后您将使用标识符来确定触发了哪个segue

创建用于编辑笔记的segue的过程是相同的。从notesCellControl-drag到左侧storyboard Document Outline中的Edit Note视图控制器。从弹出的菜单中,再次选择Show segue类型。

单击新的segue并打开Attributes inspector,这样就可以用editNoteSegue填充Identifier属性。

是时候让这些segue工作了。在助理编辑器中,NotesListViewController.swift已经打开。在viewWillAppear(_:)之后将prepare(for:sender:)添加到文件中。只需键入prepareforsend,然后让Xcode自动完成定义。

用以下代码粘贴到空的prepare(for:sender:)上:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  //1
  guard let destinationViewController = segue.destination 
      as? EditNoteViewController else {
    return
  }

  //2    
  switch segue.identifier {
  //3
  case "editNoteSegue":
    guard let selectedRow = notesTableView.indexPathForSelectedRow?.row else {
      return
    }
    destinationViewController.note = notesRepository[selectedRow]
      
  //4
  case "newNoteSegue":
    let newNote = Note()
    notesRepository.add(note: newNote)
    destinationViewController.note = newNote
    destinationViewController.title = "New Note"

  default:
    break
  }
}

要使这两个segue工作,prepare(for:sender:)是:

  • 1) 将segue.destination转换为EditNoteViewController,因为两个segue都需要该类型。
  • 2) 打开segue的标识符,以确定如何配置destinationViewController。注意,像这样打开一个字符串是这种方法的缺点之一。标识符必须与故事板中的标识符属性设置完全匹配。这可能是错误的来源。
  • 3) 对于editNoteSegue:
    • 确定选定的行。
    • notesRepository中从该行获取note
    • note传递给destinationViewController进行编辑。
  • 4) newNoteSegue:
    • 创建一个Note的新实例。
    • note添加到notesRepository
    • 将新note传递给destinationViewController进行编辑。
    • 重写destinationViewController的标题,以显示这是一个新note

构建和运行。点击New Note按钮创建一个新Note。当你点击返回时,你会在列表中看到新的note。你也可以点击任何note来阅读或编辑它的内容。点击Back,然后重新打开note,查看编辑是否成功。


Introducing IBSegueAction

ios13开始,苹果引入了一种处理segue的新方法@IBSegueAction属性。新方法完全颠倒了创建端点视图控制器的职责。

使用prepare(for:sender:), UIKit创建视图控制器并将实例传递给起点视图控制器的prepare(for:sender:)以进行配置。通过IBSegueAction,你可以创建完全配置好的视图控制器,并将它传递给UIKit进行显示。在您完成重构RazeNotes以使用IBSegueActions之后,您将能够看到它的优点。

对于IBSegueAction,您必须满足以下一些要求:

  • 1) 正如您已经看到的,必须始终在IBSegueAction前面加上@IBSegueAction属性。
  • 2) IBSegueAction必须接受一个NSCoder参数。它还可以接受Any?类型的sender参数和一个segue标识符字符串参数。在使用IBSegueAction时很少需要segue标识符,因为segue直接链接到故事板中的IBSegueAction。正如您将看到的,XcodeInterface Builder将为您生成这个方法。
  • 3) 端点视图控制器必须有一个init,它接受传递给IBSegueActioncoder参数。您可以传入任意多的其他参数,但至少必须传入coder。当您进行- 重构时,这一点将变得非常清楚。
  • 4) 要创建自己的init,需要重写required init?(coder:)

当您进行重构时,所有这些都更有意义。说到这里,是时候重构了!


Adding @IBSegueAction to RazeNotes

首先要做的是删除prepare(for:sender:)。UIKit将不再调用prepare(for:sender:),如果你把它留在这里,它会产生一些编译错误。

Xcode中,打开NotesListViewController.swift。找到prepare(for:sender:)并删除它。

EditNoteViewController.swift开放。此时,视图控制器只使用继承的初始化器。您需要一个初始化器,它接受一个note和一个可选的title。因此,您需要的签名是init(note:title:coder:)

在您可以声明那个新的初始化器之前,Swift将要求您重写required init?(coder:),即使IBSegueAction不会使用它。将下面的代码复制并粘贴到EditNoteViewController中,以覆盖所需的初始化器:

required init?(coder: NSCoder) {
  fatalError("init(coder:) is not implemented")
}

因为您不会在segue中使用这个初始化器,它会故意导致一个致命错误。如果在处理IBSegueAction时遗漏了一个步骤,这个错误会在开发过程中警告您。

是时候添加您将使用的初始化器了。复制并粘贴这个初始化器上面的前一个:

init?(note: Note, title: String = "Edit Note", coder: NSCoder) {
  //1
  self.note = note
  //2
  super.init(coder: coder)
  //3
  self.title = title
}

这里,新的初始值设定项是:

  • 1) 设置note的值。这是第一次,因为该类的非可选属性在调用超类初始化器之前必须有一个值。
  • 2) 调用UIViewController超类初始化器并传递coder,这样它就能从storyboard内容布局视图。
  • 3) 设置在UIViewController中声明的title。注意,如果没有使用参数,title有一个默认值。title被设置为last,因为它是超类的一部分,在调用超类初始化器之前无法设置。如果在调用超类初始化器之前尝试设置title,Xcode会给出一个错误。

现在,您可以进行一些在使用prepare(for:sender:)时无法进行的更改。替换var note: Note!复制并粘贴下面的代码。

private let note: Note

EditNoteViewController的生命周期中,note不会改变,因此它应该是一个letnote应该是private的,因为其他类不应该设置它的值。最后,你不需要用一个!来强制解包note。您可以让Swift强制执行属性的初始化。

声明属性私有和常量是IBSegueAction的优点之一。使用prepare(for:sender:),你必须在UIKit初始化视图控制器之后设置EditNoteViewControllernote。这就做出了必要的妥协。

打开Main.storyboard,然后在助理编辑器中打开NotesListViewController.swift。单击newNoteSegue,它将在画布中以蓝色突出显示该segue。从突出显示的segue control -拖动到viewWillAppear()之后。将方法命名为makeNewNoteViewController。接受默认的None作为参数,因为您不会使用可选的senderidentifier参数。

用以下代码替换Xcode插入的方法:

@IBSegueAction func makeNewNoteViewController(_ coder: NSCoder) 
    -> EditNoteViewController? {
  //1
  let newNote = Note()
  //2
  notesRepository.add(note: newNote)
  //3
  return EditNoteViewController(note: newNote, title: "New Note", coder: coder)
}

makeNewNoteViewController(_:)替换了prepare(for:sender)switch中的“newNoteSegue”情况。但是,makeNewNoteViewController(_:)并不需要使用字符串文字来决定执行什么。

这个方法是这样做的:

  • 1) 创建一个新的空note
  • 2) 将note保存在notes存储库中。
  • 3) 创建并返回一个EditNoteViewController实例,该实例带有新note和传入的title。按照IBSegueAction的要求,coder也被传入。

edit note segue执行同样的操作。Control-drag从底部segue到你刚创建的方法下面。命名新的makeEditNoteViewController命名。用以下代码替换Xcode插入的方法。

@IBSegueAction func makeEditNoteViewController(_ coder: NSCoder) 
    -> EditNoteViewController? {
  //1
  guard let selectedRow = notesTableView.indexPathForSelectedRow?.row else {
    return nil
  }
    
  //2
  let note = notesRepository[selectedRow]
  //3
  return EditNoteViewController(note: note, coder: coder)
}

下面是这个方法的作用:

  • 1) 确定所选行。如果没有返回nil
  • 2) 使用行号从note’s存储库中获取正确的记录。
  • 3) 创建并返回一个EditNoteViewController的实例。与前面一样,coder也被传入。

构建和运行。应用程序应该像以前一样工作。点击New Note按钮创建一个新Note。当你点击返回时,你会在列表中看到新的note。你可以点击任何note并编辑其内容。点击Back,然后重新打开note,查看您的编辑仍然在那里。

现在是比较这两种方法的时候了。


Comparing IBSegueAction vs prepare(for:sender:)

下面是使用IBSegueAction的优点:

  • 1) Cleaner code - 更简洁的代码:每个segue都可以有自己的IBSegueAction,从而使代码更易于设计和维护。
  • 2) No switching on string constants - 不需要打开字符串常量prepare(for:sender:)就需要打开segue.identifier。它可能与故事板文件中的标识符属性不同步。
  • 3) Better encapsulation - 更好的封装:属性现在可以是私有的,因为值可以由初始化器设置,而不是在初始化prepare(for:sender:)之后设置。
  • 4) Immutability - 不可变性:必要的属性可以在适当的时候设置为let常量,因为值是由初始化器设置的。
  • 5) Less casting - 较少的强制转换:不需要将sender.destination强制转换为EditNoteViewController来配置其属性。创建所需的视图控制器类型的实例。
  • 6) Easier to test - 易于测试:因为你不依赖于UIKit来创建你的视图控制器实例,为你的视图控制器创建测试将会容易得多。

使用IBSegueAction有一个缺点:它只适用于iOS 13或更高版本。因此,如果你的应用程序必须在iOS的早期版本上运行,你将不得不等待开始使用这种新方法。

后记

本篇主要讲述了使用IBSegueAction提升Storyboard Segues,感兴趣的给个赞或者关注~~~

你可能感兴趣的:(xib相关(二十四) —— 使用IBSegueAction提升Storyboard Segues(一))