概览
在前不久的 WWDC 24 中,苹果推出了全新的 SwiftUI 6.0。碰巧的是,它和诞生刚好十周年的 Swift 6 一并成为了“六六大顺”组合。
在 SwiftUI 6.0 中,苹果做出了诸多激动人心的改进和升级,其中就包括全新的 UIGestureRecognizerRepresentable 协议,现在我们可以使用它来恣意打造自己的自定义手势了。
在本篇博文中,您将学到如下内容:
- 适配 UIGestureRecognizerRepresentable 协议
- 自定义手势在 SwiftUI 中的使用
- 为自定义手势添加更加细粒度的控制
闲言少叙,让我们马上开始自定义手势大冒险吧! Let‘s go!!!;)
1. 适配 UIGestureRecognizerRepresentable 协议
早就如痴如醉沉浸于 SwiftUI 开发许久的小伙伴们都知道,苹果早在 SwiftUI 1.0 版本就推出了 UIViewRepresentable 和 UIViewControllerRepresentable 协议用来桥接 UIKit 中的原生视图。
不出意外,苹果在 SwiftUI 6.0 中同样“照葫芦画瓢”推出了与此颇为相似的 UIGestureRecognizerRepresentable 协议专注于实现自定义手势。
和之前 UIKit 视图桥接所需的两个协议类似,UIGestureRecognizerRepresentable 协议允许我们将 UIKit 中任何 UIGestureRecognizer 手势识别器对象桥接(或包装)到 SwiftUI 的视图里。
如下代码所示,我们只需实现其中两个协议方法即可:
struct MyGestureRecognizerRepresentable: UIGestureRecognizerRepresentable {
let action: () -> Void
func makeUIGestureRecognizer(context: Context) -> some UIGestureRecognizer {
let recognizer = UITapGestureRecognizer()
recognizer.numberOfTouchesRequired = 2
return recognizer
}
func handleUIGestureRecognizerAction(_ recognizer: UIGestureRecognizerType, context: Context) {
switch recognizer.state {
case .possible:
case .ended:
action()
default:
break
}
}
}
在上面的代码中,我们创建了一个自定义点击手势(Tap Gesture),并将所需的指尖触摸数量设置为 2。值得注意的是:我们在双击手势结束时回调了用户指定的方法,这是在 handleUIGestureRecognizerAction方法里实现的。
现在我们的自定义手势已整装待发,下面看我们如何在 SwiftUI 视图中使用它吧。
注意:UIGestureRecognizerRepresentable 协议中的 makeUIGestureRecognizer 方法必须要实现,而后面那个 handleUIGestureRecognizerAction 方法的实现是可选的。
一般来说,我们需要实现 handleUIGestureRecognizerAction 方法来处理手势在不同状态(UIGestureRecognizer.State)下的定制功能。
2. 自定义手势在 SwiftUI 中的使用
和所有 SwiftUI 中内置的原生手势相似,我们可以如法炮制般的使用自定义手势:
struct ContentView: View {
var body: some View {
Circle()
.gesture(MyGestureRecognizerRepresentable {
print("two fingers tap happened")
})
}
}
在上面的代码中,我们利用了 gesture 视图修改器同样在 SwiftUI 的圆形上应用了之前创建的自定义手势,so easy!
顺面提一下,虽然 SwiftUI 本身已经提供了海量内置的手势类型,但 UIGestureRecognizerRepresentable 仍然可以为我们提供更加高度定制化的复合手势,比如打钩或画圈手势等。
3. 为自定义手势添加更加细粒度的控制
正如上面所说的,通过遵循 UIGestureRecognizerRepresentable 协议中的两个方法,我们已经可以实现众多功能“琳琅满目”的自定义手势了。
不过,我们还可以更进一步!
对于 UIKit 中某些需要代理对象(Gesture Delegate)以便实现更多高级特性的手势识别器,我们还可以用协调器(Coordinator)来如虎添翼。
struct MyGestureRecognizerRepresentable: UIGestureRecognizerRepresentable {
let action: () -> Void
func makeUIGestureRecognizer(context: Context) -> some UIGestureRecognizer {
let recognizer = UITapGestureRecognizer()
recognizer.numberOfTouchesRequired = 2
recognizer.delegate = context.coordinator
return recognizer
}
func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator {
Coordinator(converter: converter)
}
func handleUIGestureRecognizerAction(_ recognizer: UIGestureRecognizerType, context: Context) {
switch recognizer.state {
case .possible:
case .ended:
action()
default:
break
}
}
final class Coordinator: NSObject, UIGestureRecognizerDelegate {
let converter: CoordinateSpaceConverter
init(converter: CoordinateSpaceConverter) {
self.converter = converter
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
// your logic here
}
}
}
在如上代码中,我们做了这样几件事:
- 在遵守 UIGestureRecognizerRepresentable 协议的 MyGestureRecognizerRepresentable 结构内部实现了Coordinator 协调器类,它需要遵守 UIGestureRecognizerDelegate 协议;
- 在 makeCoordinator 方法中创建了自定义手势所需的协调器对象;
- 将协调器对象设置为自定义手势的代理(Delegate);
- 在协调器类中实现所需的手势回调方法(gestureRecognizerShouldBegin);
使用该手势的方法自然是和之前一毛一样:
struct ContentView: View {
var body: some View {
Circle()
.gesture(MyGestureRecognizerRepresentable {
print("two fingers tap happened")
})
}
}
现在,利用协调器对象我们可以继续增强自定义手势的功能,让实现的灵活性百尺竿头更进一步,棒棒哒!💯
总结
在本篇博文中,我们讨论了如何在 SwiftUI 6.0 中利用全新的 UIGestureRecognizerRepresentable 协议恣意创建更加灵活的自定义手势。
感谢观赏,再会啦!8-)