iOS中含Charts的UIScrollView垂直滚动及图表交互逻辑调整
解决UIScrollView与LineChartView的手势冲突问题
要实现你想要的交互逻辑——垂直平移时让UIScrollView响应滚动,仅在图表上水平平移时触发LineChartView的高亮操作,核心是通过手势识别代理区分平移方向,控制两个视图的手势优先级。下面是具体的实现步骤和代码示例:
核心思路
LineChartView自带了用于平移高亮的UIPanGestureRecognizer,我们需要通过这个手势的代理方法,判断用户的平移方向:
- 当垂直方向的位移/速度大于水平方向时,让UIScrollView的滚动手势优先响应,禁用Chart的平移手势;
- 当水平方向的位移/速度更大时,允许Chart的平移手势生效,触发数据点高亮。
具体实现
1. 基础配置(Objective-C/Swift)
首先确保你的视图控制器遵守UIGestureRecognizerDelegate协议,然后对ScrollView和ChartView做基础配置:
Objective-C
- (void)viewDidLoad { [super viewDidLoad]; // 禁止Chart的缩放手势(如果不需要的话,避免干扰) self.lineChartView.pinchZoomEnabled = NO; // 设置Chart平移手势的代理为当前控制器 self.lineChartView.panGestureRecognizer.delegate = self; // 配置ScrollView:禁止水平滚动(避免和Chart的水平平移冲突) self.scrollView.showsHorizontalScrollIndicator = NO; // 设置ScrollView的垂直内容尺寸,保证能垂直滚动 self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, 1000); }
Swift
override func viewDidLoad() { super.viewDidLoad() // 禁止Chart的缩放手势 lineChartView.pinchZoomEnabled = false // 设置Chart平移手势的代理 lineChartView.panGestureRecognizer.delegate = self // 配置ScrollView scrollView.showsHorizontalScrollIndicator = false scrollView.contentSize = CGSize(width: view.bounds.width, height: 1000) }
2. 实现手势代理方法
通过代理方法判断平移方向,控制手势是否生效:
Objective-C
#pragma mark - UIGestureRecognizerDelegate // 控制Chart的平移手势是否应该开始 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer == self.lineChartView.panGestureRecognizer) { // 获取手势在视图中的速度 CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:self.view]; // 水平速度绝对值大于垂直时,允许Chart手势生效(触发高亮) return fabs(velocity.x) > fabs(velocity.y); } return YES; } // 允许Chart手势和ScrollView手势同时识别(通过shouldBegin控制最终谁生效) - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return (gestureRecognizer == self.lineChartView.panGestureRecognizer && otherGestureRecognizer == self.scrollView.panGestureRecognizer); }
Swift
extension ViewController: UIGestureRecognizerDelegate { func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer, panGesture == lineChartView.panGestureRecognizer else { return true } let velocity = panGesture.velocity(in: view) // 水平方向占主导时,允许Chart手势触发高亮 return abs(velocity.x) > abs(velocity.y) } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return gestureRecognizer == lineChartView.panGestureRecognizer && otherGestureRecognizer == scrollView.panGestureRecognizer } }
额外说明
- 我们禁止了ScrollView的水平滚动(
showsHorizontalScrollIndicator = NO且contentSize宽度和视图一致),这样即便Chart的水平平移触发,ScrollView也不会产生水平滚动冲突; - 用速度判断方向比用位移更灵敏,能更快响应用户的意图;
- 如果需要处理斜向滑动的场景,这个逻辑会自动选择占主导的方向来响应,体验更自然。
内容的提问来源于stack exchange,提问作者A. Walker
相关产品推荐
相关产品推荐

