SwiftUI 6.0 中全新的 UIGestureRecognizerRepresentable 协议漫谈

261 阅读4分钟

在这里插入图片描述

概览

在前不久的 WWDC 24 中,苹果推出了全新的 SwiftUI 6.0。碰巧的是,它和诞生刚好十周年的 Swift 6 一并成为了“六六大顺”组合。

在这里插入图片描述

在 SwiftUI 6.0 中,苹果做出了诸多激动人心的改进和升级,其中就包括全新的 UIGestureRecognizerRepresentable 协议,现在我们可以使用它来恣意打造自己的自定义手势了。

在本篇博文中,您将学到如下内容:

  1. 适配 UIGestureRecognizerRepresentable 协议
  2. 自定义手势在 SwiftUI 中的使用
  3. 为自定义手势添加更加细粒度的控制

闲言少叙,让我们马上开始自定义手势大冒险吧! 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-)