ios - 如何使用 2 个手势识别器同时接收事件?

标签 ios swift

我正在使用滑动手势在选项卡之间导航,使用假的 UITabBarController 在 UIScrollView 内托管 4 个 UIViewController 子类,每个子类上都有 UIPanGestureRecognizers 以检测滑动并相应地切换选项卡。由此产生的效果是核心导航,就像您在 Snapchat 上看到的一样。但是,其中一个屏幕包含一个 map View (Mapbox map ),它内置了自己的手势识别器。

我希望用户能够通过检测从屏幕最右边缘的滑动来在 map 本身上滑动以导航到另一个选项卡。我尝试将 UIPanGestureRecognizer 添加到右侧此区域中的不可见 UIView 中,但这提供了一个生涩的、无动画的选项卡开关,并且它从下面的 map 中截取触摸事件。

如何允许两个手势识别器同时接收事件,或者仅根据滑动的开始位置及其距离和方向过滤掉一些事件?

滑动 View Controller 代码:

class KUSwipeViewController: EZSwipeController {

    fileprivate var fakeTabBar: UIView!

    fileprivate var tabBarHeight: CGFloat = 50

    override func viewDidLoad() {
        initUI()
    }

    override func setupView() {
        super.setupView()
        datasource = self
        navigationBarShouldNotExist = true
    }

    func initUI() {
        fakeTabBar = UIView(frame: CGRect(x: 0, y: Screen.height - tabBarHeight, width: Screen.width, height: tabBarHeight))
        fakeTabBar.backgroundColor = .black

        let tab0 = UIButton(frame: CGRect(x: Screen.width * 0.08, y: 9, width: 32, height: 32))
        tab0.setBackgroundImage(#imageLiteral(resourceName: "tab-events"), for: .normal)
        tab0.addTarget(self, action: #selector(tapped0), for: .touchUpInside)
        fakeTabBar.addSubview(tab0)

        let tab1 = UIButton(frame: CGRect(x: Screen.width * 0.32, y: 9, width: 32, height: 32))
        tab1.setBackgroundImage(#imageLiteral(resourceName: "tab-feat"), for: .normal)
        tab1.addTarget(self, action: #selector(tapped1), for: .touchUpInside)
        fakeTabBar.addSubview(tab1)

        let tab2 = UIButton(frame: CGRect(x: Screen.width * 0.58, y: 9, width: 32, height: 32))
        tab2.setBackgroundImage(#imageLiteral(resourceName: "tab-chat"), for: .normal)
        tab2.addTarget(self, action: #selector(tapped2), for: .touchUpInside)
        fakeTabBar.addSubview(tab2)

        let tab3 = UIButton(frame: CGRect(x: Screen.width * 0.82, y: 9, width: 32, height: 32))
        tab3.setBackgroundImage(#imageLiteral(resourceName: "tab-profile"), for: .normal)
        tab3.addTarget(self, action: #selector(tapped3), for: .touchUpInside)
        fakeTabBar.addSubview(tab3)

        view.addSubview(fakeTabBar)
    }


    func tapped0() {
        self.moveToPage(0, animated: true)
    }

    func tapped1() {
        self.moveToPage(1, animated: true)
    }

    func tapped2() {
        self.moveToPage(2, animated: true)
    }

    func tapped3() {
        self.moveToPage(3, animated: true)
    }
}

extension KUSwipeViewController: EZSwipeControllerDataSource {
    func viewControllerData() -> [UIViewController] {

        let nav0 = UINavigationController()
        let nav3 = UINavigationController()

        let mapVC = EventMapViewController()
        let featuredVC = FeaturedEventsViewController()
        let chatVC = MessagesViewController()
        let profileVC = ProfileViewController()

        nav0.viewControllers = [mapVC]
        nav3.viewControllers = [profileVC]

        return [nav0, featuredVC, chatVC, nav3]
    }

    func titlesForPages() -> [String] {
        return ["", "", "", ""]
    }

    func indexOfStartingPage() -> Int {
        return 0
    }

    func changedToPageIndex(_ index: Int) {
        Haptic.selection.generate()
    }
}

map View Controller 代码:
class EventMapViewController: CommonViewController {

    fileprivate var mapView: MGLMapView!
    fileprivate var statusBarView: UIView!
    fileprivate var searchBar: FloatingSearchBar!
    fileprivate var searchButton: UIButton!
    fileprivate var filterButton: UIButton!
    fileprivate var peekView: UIView!
    fileprivate var architectView: UIView!
    fileprivate var panArchitectView: UIView!
    fileprivate var peekArchitectView: UIView!
    fileprivate var peekLabel: UILabel!
    fileprivate var panView: UIPanGestureRecognizer!

    fileprivate let peekViewHeight: CGFloat = 140

    fileprivate var selectedEvent: Event?

    fileprivate var cardShowing: Bool = false
    fileprivate var peekShowing: Bool = false

    /*
    override func statusBarStyle() -> UIStatusBarStyle {
        return cardShowing ? .lightContent : .default
    }
    */

    override func navigationBarHidden() -> Bool {
        return true
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        initUI()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "eventSegue" {
            let dvc = segue.destination as! EventViewController
            dvc.event = selectedEvent!
        }
    }

    func initUI() {
        mapView = MGLMapView(frame: view.bounds)
        mapView.styleURL = (UIColor.theme == .light) ? URL(string: mbThemeLight)! : URL(string: mbThemeDark)!
        mapView.showsUserLocation = true
        mapView.userTrackingMode = .none
        mapView.delegate = self
        mapView.setCenter(CLLocationCoordinate2D(latitude: 34, longitude: -118), zoomLevel: 5, animated: false)
        view.addSubview(mapView)
        mapView.snp.makeConstraints { (make) in
            make.edges.equalTo(view)
        }

        //architect view (for intercepting touch)
        architectView = UIView(frame: self.view.bounds)
        let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(EventMapViewController.handleTap(gestureRecognizer:)))
        gestureRecognizer.delegate = self
        architectView.addGestureRecognizer(gestureRecognizer)
        architectView.isHidden = true
        view.addSubview(architectView)

        statusBarView = UIView()
        statusBarView.backgroundColor = UIColor.clear
        view.addSubview(statusBarView)
        statusBarView.snp.makeConstraints { (make) in
            make.left.right.top.equalTo(0)
            make.height.equalTo(20)
        }

        searchButton = UIButton()
        searchButton.backgroundColor = UIColor.kuLightBackground
        searchButton.tintColor = (UIColor.theme == .light) ? UIColor.kuPrimary : UIColor.kuDeselected
        searchButton.setImage(#imageLiteral(resourceName: "icon_search_white").withRenderingMode(.alwaysTemplate), for: .normal)
        searchButton.layer.cornerRadius = 25
        searchButton.layer.borderColor = UIColor.kuExtraLightBackground.cgColor
        searchButton.layer.borderWidth = 1
        searchButton.addTarget(self, action: #selector(searchButtonTapped(_:)), for: .touchUpInside)
        view.addSubview(searchButton)
        searchButton.snp.makeConstraints { (make) in
            make.width.height.equalTo(50)
            make.left.equalTo(24)
            make.top.equalTo(34)
        }

        filterButton = UIButton()
        filterButton.backgroundColor = UIColor.kuLightBackground
        filterButton.tintColor = (UIColor.theme == .light) ? UIColor.kuPrimary : UIColor.kuDeselected
        filterButton.setImage(#imageLiteral(resourceName: "icon_filter_white").withRenderingMode(.alwaysTemplate), for: .normal)
        filterButton.layer.cornerRadius = 20
        filterButton.layer.borderColor = UIColor.kuExtraLightBackground.cgColor
        filterButton.layer.borderWidth = 1
        filterButton.addTarget(self, action: #selector(filterButtonTapped(_:)), for: .touchUpInside)
        view.addSubview(filterButton)
        filterButton.snp.makeConstraints { (make) in
            make.width.height.equalTo(40)
            make.left.equalTo(80)
            make.top.equalTo(40)
        }

        //peek view
        peekView = UIView(frame: CGRect(x: 0, y: UIScreen.main.bounds.height, width: UIScreen.main.bounds.width, height: peekViewHeight))
        peekView.backgroundColor = UIColor(white: 1, alpha: 0.5)
        peekView.layer.cornerRadius = 10
        view.addSubview(peekView)

        //peek label
        peekLabel = UILabel(frame: CGRect(x: 0, y: 12, width: UIScreen.main.bounds.width, height: 68))
        peekLabel.font = UIFont.kuBoldFont(ofSize: 38)
        peekLabel.textColor = .black
        peekLabel.textAlignment = .center
        peekView.addSubview(peekLabel)

        //peek architect
        peekArchitectView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: peekViewHeight))
        let peekGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(EventMapViewController.peekSelected(gestureRecognizer:)))
        peekGestureRecognizer.delegate = self
        peekArchitectView.addGestureRecognizer(peekGestureRecognizer)
        peekView.addSubview(peekArchitectView)

        loadEventAnnotations()
    }

    func enterPeekView() {
        guard !peekShowing else { return }

        architectView.isHidden = false
        peekLabel.text = "\(selectedEvent!.title!) >"

        peekView.cheetah
            .move(0, peekViewHeight * -1)
            .duration(0.18)
            .easeOutExpo
            .run()

        peekShowing = true
    }

    func exitPeekView() {
        guard peekShowing else { return }

        architectView.isHidden = true
        peekView.cheetah
            .move(0, peekViewHeight)
            .duration(0.18)
            .easeInExpo
            .run()

        peekShowing = false
    }

    func loadEventAnnotations() {
        RealmManager.shared.defaultRealm.objects(Event.self).forEach { (event) in

            let annotation = EventAnnotation()
            annotation.event = event
            mapView.addAnnotation(annotation)
        }
    }

    func searchButtonTapped(_ sender: UIButton) {
        let eventSearchCardViewController = EventSearchCardViewController()
        eventSearchCardViewController.delegate = self
        UIApplication.rootViewController()?.presentCardViewController(eventSearchCardViewController)
    }

    func filterButtonTapped(_ sender: UIButton) {
        let eventFilterCardViewController = EventFilterCardViewController()
        eventFilterCardViewController.delegate = self
        UIApplication.rootViewController()?.presentCardViewController(eventFilterCardViewController)
    }
}

extension EventMapViewController: CardViewControllerDelegate {

    func cardViewControllerWillAppear(cardViewController: CardViewController) {
        cardShowing = true
        setNeedsStatusBarAppearanceUpdate()
    }

    func cardViewControllerWillDisappear(cardViewController: CardViewController) {
        cardShowing = false
        setNeedsStatusBarAppearanceUpdate()
    }
}

extension EventMapViewController: MGLMapViewDelegate {

    func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {

        guard let eventAnnotation = annotation as? EventAnnotation else {
            return
        }

        selectedEvent = eventAnnotation.event

        enterPeekView()
    }

    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
        return false
    }

    func mapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? {
        var annotationImage = mapView.dequeueReusableAnnotationImage(withIdentifier: UIColor.kuMarkerName)

        if annotationImage == nil {
            var image = UIImage(named: UIColor.kuMarkerName)!
            image = image.withAlignmentRectInsets(UIEdgeInsets(top: 0, left: 0, bottom: image.size.height/2, right: 0))
            annotationImage = MGLAnnotationImage(image: image, reuseIdentifier: UIColor.kuMarkerName)
        }

        return annotationImage
    }
}

extension EventMapViewController: UIGestureRecognizerDelegate {

    func handleTap(gestureRecognizer: UIGestureRecognizer) {
        //close peek and keyboard on tap
        view.endEditing(true)

        if peekShowing {
            exitPeekView()
        }
    }

    func peekSelected(gestureRecognizer: UIGestureRecognizer) {
//        performSegue(withIdentifier: "eventSegue", sender: nil)

        let eventController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "EventViewController") as! EventViewController
        self.navigationController?.pushViewController(eventController, animated: true)
    }
}

最佳答案

要使用多个手势,请让您的类(class)成为 GestureRecognizer 委托(delegate) UIGestureRecoginizerDelegate然后使用此功能:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if (gestureRecognizer is UIPanGestureRecognizer || gestureRecognizer is UITapGestureRecognizer) {
            return true
        } else {
            return false
        }
    }

关于ios - 如何使用 2 个手势识别器同时接收事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44379968/

相关文章:

ios - Lion(金大师)是 iOS 开发者计划的一部分吗?

ios - 我如何设置一个 NSPredicate 来总结 iOS 字典数组中字典中的属性值?

swift - 为什么我无法删除核心数据中表的数据

ios - fatal error : unexpectedly found nil while unwrapping an Optional value with web view

ios - react native 不支持的 url (IOS)

ios - iOS 6.0 以下设备的广告标识符

ios - libdispatch(苹果开源)中的这段代码的含义是什么?

ios - 如何使用 URL scheme 向相机或预选视频打开 Vine?

swift - 使用 Swift 将 Core Image Filter 应用于 OS X 上的视频

ios - 转换为不相关的类型总是失败