版本记录
版本号 | 时间 |
---|---|
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
毫无关系。segue
和Segway
唯一的共同点是它们是同音异义词。
本教程介绍了一种实现名为IBSegueAction
的UIKit 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
文件中两个视图控制器之间的转换。一个动作——例如点击一个按钮、执行一个手势或点击一个表格的单元格行——就会触发segue
。segue
的端点是你想要显示的视图控制器。
下图演示了连接相同两个视图控制器的两个segue
。segue
箭头指向端点。
最常见的是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 Outline
的RazeNotes Scene
中展开控件的层次结构;它在storyboard screen
的左侧。要为新note
创建一个segue
,可以从Document Outline
中的new note
按钮Control-drag
到Edit note
视图控制器。从弹出的菜单中,选择Show segue
类型。
选择新的segue
。然后打开Attributes inspector
,这样就可以用newNoteSegue
填充Identifier
属性。稍后您将使用标识符来确定触发了哪个segue
。
创建用于编辑笔记的segue
的过程是相同的。从notesCell
中Control-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
。正如您将看到的,Xcode
的Interface Builder
将为您生成这个方法。 - 3) 端点视图控制器必须有一个
init
,它接受传递给IBSegueAction
的coder
参数。您可以传入任意多的其他参数,但至少必须传入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
不会改变,因此它应该是一个let
。note
应该是private
的,因为其他类不应该设置它的值。最后,你不需要用一个!
来强制解包note
。您可以让Swift
强制执行属性的初始化。
声明属性私有和常量是IBSegueAction
的优点之一。使用prepare(for:sender:)
,你必须在UIKit初始化视图控制器之后设置EditNoteViewController
的note
。这就做出了必要的妥协。
打开Main.storyboard
,然后在助理编辑器中打开NotesListViewController.swift
。单击newNoteSegue
,它将在画布中以蓝色突出显示该segue
。从突出显示的segue control
-拖动到viewWillAppear()
之后。将方法命名为makeNewNoteViewController
。接受默认的None
作为参数,因为您不会使用可选的sender
或identifier
参数。
用以下代码替换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
,感兴趣的给个赞或者关注~~~