UIGestureRecognizer trong WKWebView iOS

I. Giới thiệu

Nhắc đến UIGestureRecognizer trong iOS, chúng ta nghĩ đến những gesture basic như tap, long tap, doubletap, pinch, pan, swipe…. Hoặc thậm chí nhiều khi chúng ta tự custom chúng cho phù hợp với mục đích sử dụng, và may mắn là các kỹ thuật này đều khá dễ dàng thực hiện.

Tuy nhiên, nếu một WKWebView đã có những gesture của riêng nó(dùng javascript) rồi và yêu cầu đặt ra bạn muốn gán thêm gesture cho webView( bằng native iOS) thì chúng ta lại gặp chút rắc rối, đơn cử như webView không nhận bốn phares touchesBegan(😃, touchesMoved(😃, touchesEnded(😃, touchesCancelled(_😃… vì vậy việc custom một gesture khá khó khăn. Hơn nữa, nếu gesture được gán tới webView bằng javascript trùng với gesture bạn muốn gán bằng native iOS thì việc phân biệt chúng, lấy gesture nào, bỏ cái nào cũng khá rắc rối. Sau đây mình xin trình bày một phương pháp để giải quyết vấn đề này. Mình tạm gọi phương pháp này của mình là FloatGesture( làm nổi – dùng đệ quy lấy tất cả gestures của view và subView gán lên trên một top view trong suốt)

II. Nội dung

Giả sử bạn có một webView có sẵn 2 gesture: UITapGestureRecognizer, UIPinchGestureRecognizer được gán sẵn bên phía javacript. Mục đích của bạn là gán thêm một pinch gesture bên phía native iOS, và bỏ pinch gesture bên phía javascipt.

Bạn tiến hành qua các bước sau:

  • Tạo một classs TopView, kế thừa UIView và add một instance của class đó(gọi là topView) lên trên bề mặt của webView
class TopView : UIView {
    func handlerPinchGesture(gesture: UIGestureRecognizer) {
        // Handler pinch gesture here...
    }
}
let topView = TopView(frame: webView.bounds)
  • Bạn tạo một pinch gesture mà bạn muốn custom, sau đó add gesture đó lên topView
let pinchGesture = UIPinchGestureRecognizer(target: topView, action: #selector(TopView.handlerPinchGesture(_:)))
topView.addGestureRecognizer(pinchGesture)
  • Bạn tạo một hàm đệ quy getAllGesturesOfWebView(view: UIView, level: Int) để lấy tất cả gestures của WKWebViews và các subView của nó. Trong đó view là view ban đầu, level là độ sâu của đệ quy.
//Rescursive to get all gestures of webView and its subViews
var gestureArray = [UIGestureRecognizer]()
func getAllGesturesOfWebView(view: UIView, level: Int) {
     guard let superView = superView else {
        return
    }
    for subView in superView.subviews {
        guard let gestures = subView.gestureRecognizers else {
            continue
        }
        gestureArray += gestures
        getAllGesturesOfWebView(subView, level + 1)
    }
}
  • Bạn tạo một file extension của UIGestureRecognizer, trong đó khai báo function getName() -> String để lấy được tên hoặc những thông tin cần thiết của gesture
extension UIGestureRecognizer {
    func getName() -> String {
        let gestureString = "\(self)"
        let gestureComponents = gestureString.componentsSeparatedByString(";")
        guard let nameComponent = gestureComponents.first else {
            return ""
        }
        let prefix = nameComponent.componentsSeparatedByString(":").first!
        let name = prefix.componentsSeparatedByString("<").last!
        return name
    }
}
  • Bạn tạo một function addGestureToReceiveEventView() để gán tất cả những gesture nào bạn thấy cần thiết lên trên TopView. Trong trường hợp này, bạn muốn bỏ gesture UIPinchGesture của WKWebView đi, và thay thế bằng 1 pinch gesture của riêng bạn, vì vậy bạn sẽ dùng xét các gestures đã đạt được ở bước 3, nếu gesture đó có getName() ở bước 4 chứa “PinchGesture” thì bạn bỏ quá, ko add nó lên topView, song song với việc add gesture lên topView, bạn cũng remove gesture đó ở view cũ
//TopView adds gestures
func addGestureToReceiveEventView() {
    for gesture in gestureArray {
        guard let view = gesture.view else {
            return
        }
        view.removeGestureRecognizer(gesture)
        topView.addGestureRecognizer(gesture)
    }
}

Như vậy, bạn đã tạo được một topView lấy đầy đủ được gestures cần thiết của webView và thêm cả những gesture của riêng bạn. Bây giờ, tất cả gesture đều trên cùng một view nên việc xử lý sẽ đơn giản hơn rất nhiều.

III. Kết luận

Mặc dù phương pháp này giải quyết được vấn đề thêm gestures cho webView và lọc bỏ được những gestures từ webView mà bạn không muốn. Tuy nhiên, việc thống nhất thêm gestures từ hoàn toàn một phía( javascript hoặc native) nếu được vẫn ưu thế hơn và dễ dàng hơn cho iOS developers trong việc tổ chức code lẫn maintain sau này.

Chúc các bạn thành công!