如何使用 2 个手势识别器同时接收事件?
Posted
技术标签:
【中文标题】如何使用 2 个手势识别器同时接收事件?【英文标题】:How do I use 2 gesture recognizers to receive events simultaneously? 【发布时间】:2017-06-06 01:15:17 【问题描述】:我正在使用滑动手势在选项卡之间导航,它使用一个假的 UITabBarController,它在 UIScrollView 内托管 4 个 UIViewController 子类,每个子类上都有 UIPanGestureRecognizers 来检测滑动并相应地切换选项卡。由此产生的效果是核心导航,就像您在 Snapchat 上看到的一样。但是,其中一个屏幕包含一个地图视图(Mapbox 地图),它内置了自己的手势识别器。
我希望用户能够通过检测屏幕最右边缘的滑动来在地图本身上滑动以导航到另一个选项卡。我尝试将 UIPanGestureRecognizer 添加到右侧此区域中的不可见 UIView 中,但这提供了一个生涩的、无动画的选项卡切换,并且它从下面的地图中截取触摸事件。
如何允许两个手势识别器同时接收事件,或者仅根据滑动的开始位置及其距离和方向过滤掉一些事件?
滑动视图控制器代码:
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()
地图视图控制器代码:
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)
【问题讨论】:
【参考方案1】:要使用多个手势,请将您的类设为 GestureRecognizer 委托 UIGestureRecoginizerDelegate
,然后使用此函数:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool
if (gestureRecognizer is UIPanGestureRecognizer || gestureRecognizer is UITapGestureRecognizer)
return true
else
return false
【讨论】:
以上是关于如何使用 2 个手势识别器同时接收事件?的主要内容,如果未能解决你的问题,请参考以下文章