说到 iOS 上的用户界面实现,现在大家脑海中浮现的就是 SwiftUI 框架。SwiftUI 每天都在吸引更多的开发人员,并逐年改进新功能和可能性。然而,这并不意味着 UIKit 框架已经过时;相反,UIKit 是,并且将在可预见的未来的大部分时间里继续存在。
所以,在每一次 WWDC 中,除了所有让我们兴奋的关于 SwiftUI 的消息外,还有关于 UIKit 的公告和改进。而这样的 UIKit 新功能正是本文今天要讨论的内容。
UIKit 最初与 iOS 14 一起出现在 WWDC 2020 中,它可以避免使用我们多年来熟悉的target-action 模式;当用户与 UIKit 控件交互时,需要指定要调用的选择器方法。相反,我们可以使用闭包,因此将控件的操作与其初始化和配置保持在一起。
下面的几个例子将完全清楚我在说什么。如果您还没有意识到这种技术,那么我敢打赌,您在阅读了这篇文章后会发现它非常有趣。
你会在这篇文章中读到什么......
- 目标-动作模式
- 使用闭包代替选择器方法
- 使用带有更多 UIKit 控件的闭包
- 结论
目标-动作模式
使用 UIKit 实现用户界面时,我们可以采取两条路径:要么使用故事板和 XIB 文件的图形,要么以编程方式制作。选择前一种方法时,有必要实现特殊操作方法并将其连接到提供用户交互的控件。这些方法用@IBAction
关键字标记,当它们匹配的控件触发某些事件时调用它们。
以编程方式构建用户界面时发生的场景非常相似。为了开始讨论一些代码示例,请参阅以下几行初始化和配置 UIButton:
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
// Additional button configuration...
}
这里有趣的一行是按钮初始化之后的一行。它说的很简单;将类的当前实例(UIView、UIViewController)视为查找按钮操作(目标)的位置。实际行动是buttonAction()
方法。它在touchUpInside
偶数发生时被调用,这意味着当用户抬起按钮的手指时。
然而,这还不够。为了真正制作buttonAction()
一个动作方法,也称为选择器方法,需要用@objc
关键字来标记它:
@objc
func buttonAction() {
// Do something when the button is tapped...
}
这个表示法在 Swift 中继承自旧的好的 Objective-C,以及刚刚呈现的整个目标-动作模式。这里真正的缺点是需要定义一个额外的方法来实现按钮的操作。作为一个自然的子序列,控件越多,我们需要实现的选择器方法就越多。不可避免地,这会导致可读性和可维护性问题,尤其是在大型代码库中。
使用闭包代替选择器方法
值得庆幸的是,从 iOS 14 开始,事情会变得更好、更简单。可以遵循替代路线,并使用闭包而不是选择器方法。您很快就会意识到,当实现的操作只需要几行代码时,这非常方便。但这不是一个严格的规则。闭包方法可用于小型和大型实现。
是时候看看所有这些的第一个例子了。我们将首先初始化一个UIAction
对象,如下所示:
let action = UIAction { _ in
print("Hello there!")
}
注意: UIAction 是不是很眼熟?如果您曾经使用 UIKit 展示过操作表,那么您肯定遇到过它。
在上面的代码片段中,我们初始化了一个UIAction
实例并将其分配给action
常量。我们提供给它的参数是当我们接下来创建的按钮被点击时我们想要调用的闭包。
下一步是初始化一个 UIButton,将上述操作作为参数传递:
let button = UIButton(primaryAction: action)
注意:提供按钮类型是可选的。如果我们在这里省略它.system
,默认情况下使用按钮样式。
以上就是我们对用户交互做出反应所需要的全部内容!无需创建额外的选择器方法,或指定self
为目标。当然,应该在真实的应用程序中进行额外的配置,以便将按钮添加到视图中,设置样式等等。
在上面的两个后续步骤中,我使用闭包和按钮初始化了动作。这是故意的,并且出于演示原因。实际上,两者都可以并且为了节省空间应该合并在一个步骤中。以下代码演示了具有自定义类型的按钮的初始化。提供的 UIAction 使用内联操作闭包进行初始化:
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { _ in
print("Hello there!")
}))
如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。
使用带有更多 UIKit 控件的闭包
最有可能的是,按钮将成为您最常与闭包一起使用的 UIKit 控件。但是,闭包也可以与其他控件一起使用,我们将在这里看到其中的几个。
第一个将是 UISwitch。这是一个可以发现新事物的好例子;如何在控件初始化后为 UIAction 实例提供闭包。在下一个片段中查看addAction(_:for:)
方法;它接受动作闭包以及一个附加值,即触发闭包调用的事件:
let switchControl = UISwitch()
switchControl.addAction(UIAction(handler: { _ in
// Do something when the switch is toggled...
}), for: .valueChanged)
尽管以上对于大多数情况来说已经足够了,但有时您需要访问闭包内的实际控件。参考上面的例子,那将是 switch 实例。
我们可以很容易地获得对控件的引用;它是闭包的参数值,到目前为止,我一直在之前的所有示例中用下划线符号替换它。这个值是 UIAction 实例,它有一个名为sender
type的属性Any
。我们只需要将它转换为控件的类型即可使用它。
以下示例演示了所有这些:
let switchControl = UISwitch()
switchControl.addAction(UIAction(handler: { action in
guard let control = action.sender as? UISwitch else { return }
let state = control.isOn ? "on" : "off"
print("Switch is \(state)")
}), for: .valueChanged)
除了上述所有内容之外,UIKit 控件还具有初始化器,允许提供动作闭包和另一个值。例如,接下来的 switch 使用作为第一个参数给出的帧进行初始化:
let rect = CGRect(x: 0, y: 60, width: 100, height: 44)
let switchControl = UISwitch(frame: rect, primaryAction: UIAction(handler: { _ in
}))
可以像上面演示的 UISwitch 一样创建和处理更多控件。接下来您可以看到一个类似的示例,但这次使用的是 UITextField。动作闭包是通过addAction(_:for:)
方法给出的,指定的事件不同:
let textField = UITextField()
textField.addAction(UIAction(handler: { action in
guard let textField = action.sender as? UITextField else { return }
print(textField.text)
}), for: .editingChanged)
结论
使用动作闭包而不是带有 UIKit 控件的目标动作模式似乎非常方便。然而,如果我们有多个控件,并且在每个动作闭包中会有 10-15 行代码,以及额外的配置代码呢?所有这些的容器方法最终会很长,难以阅读且难以维护。在这种情况下,坚持我们已经知道的目标-动作模式可能是更可取的解决方案。但最终,您最终选择哪种方法完全取决于您。现在你知道了 UIKit 控件中的动作闭包,所以考虑把你在这里读到的东西变成你工具带中的另一个工具,并在合适的时候把它付诸实践。我希望你觉得这篇文章很有趣,谢谢你的阅读!
文末推荐:iOS热门文集
- 面试基础
iOS面试基础知识 (一)
https://github.com/iOS-Mayday/heji
iOS面试基础知识 (二)
https://github.com/iOS-Mayday/heji
iOS面试基础知识 (三)
https://github.com/iOS-Mayday/heji
iOS面试基础知识 (四)
https://github.com/iOS-Mayday/heji
iOS面试基础知识 (五)
https://github.com/iOS-Mayday/heji - 知识详解
iOS面试要点之GCD面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之多线程面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之block面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之Runtime面试要 点
https://github.com/iOS-Mayday/heji
iOS面试要点之RunLoop面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之内存管理面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之MVC、MVVM面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之网络性能优化要点
https://github.com/iOS-Mayday/heji
iOS面试要点之网络编程面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之KVC&KVO面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之数据存储面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之混编技术面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之设计模式面试要点
https://github.com/iOS-Mayday/heji
iOS面试要点之UI面试要点
https://github.com/iOS-Mayday/heji
网友评论