Sources/Compose/Component/Router/RouterPanGestureReader.swift

Summary

Maintainability
A
0 mins
Test Coverage
import Foundation
import SwiftUI

#if os(iOS)
import UIKit

struct RouterPanGestureReader : UIViewRepresentable {
    
    struct State {
        let gesture : UIPanGestureRecognizer
        let gestureState : UIPanGestureRecognizer.State
        let velocity : CGPoint
        let translation : CGPoint
        let predictedEndTranslation : CGPoint
        let startLocation : CGPoint
    }
    
    @Binding var isInteractiveGestureEnabled : Bool
    let action : (State) -> Void
    
    @SwiftUI.State fileprivate var isLoaded : Bool = false

    func makeUIView(context: Context) -> some UIView {
        let view = UIView()
        view.alpha = 0.0
        view.isUserInteractionEnabled = false
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        context.coordinator.recognizer?.isEnabled = isInteractiveGestureEnabled
        
        DispatchQueue.main.async {
            guard isLoaded == false else {
                return
            }
            
            isLoaded = true
            
            let recognizer = UIPanGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handlePan(_:)))
            recognizer.delegate = context.coordinator
   
            context.coordinator.recognizer = recognizer
            
            if uiView.superview?.superview?.gestureRecognizers?.contains(recognizer) == false {
                uiView.superview?.superview?.addGestureRecognizer(recognizer)
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(action: action)
    }
    
}

extension RouterPanGestureReader {
    
    class Coordinator : NSObject, UIGestureRecognizerDelegate {
        
        weak var recognizer : UIPanGestureRecognizer? = nil
        
        private let action : (State) -> Void
        private var startLocation : CGPoint = .zero
        
        init(action : @escaping (State) -> Void) {
            self.action = action
        }
        
        func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            return gestureRecognizer.location(in: gestureRecognizer.view).x <= 55
        }
        
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            false
        }
        
        @objc fileprivate func handlePan(_ recognizer : UIPanGestureRecognizer) {
            let translation = recognizer.translation(in: recognizer.view)
            let location = recognizer.location(in: recognizer.view)
            let velocity = recognizer.velocity(in: recognizer.view)
            
            if recognizer.state == .began {
                startLocation = location
            }
            
            action(.init(gesture: recognizer,
                         gestureState: recognizer.state,
                         velocity: velocity,
                         translation: translation,
                         predictedEndTranslation: .init(x: translation.x + velocity.x, y: translation.y + velocity.y),
                         startLocation: startLocation))
        }
        
    }
    
}

#endif