当产品更我说需要做对账,账单详情页和支付宝基本一样,问我原声代码可不可以实现,这是考验技术吗?仔细看了一下图片,分析一下drawRect+layer+touch.但是有几个问题需要解决:1.环形内点击区域的判断,2.折线和直线的选择.
first question:环形内点击区域的判断.
首页想到的是CGRectContainsPoint方法,但是这个限于Rect规则图形,扇形图一般实现是利用UIBezierPath,“path” API中有CGPathContainsPoint这个方法,这样问题一有解决方法. CGPathContainsPoint(path, &CGAffineTransformIdentity, point, 0) 判断触摸点是否在对象layer所绘制的path上.
主要实现如下代码:
//初始化selectIndex
__block NSInteger selectIndex = -1;
//遍历饼图中的子layer
[self.layer.sublayers enumerateObjectsUsingBlock:^(CALayer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[XLShapeLayer class]]) {
XLShapeLayer *shapeLayer = (XLShapeLayer *)obj;
CGPathRef path = [shapeLayer path];
//CGPathContainsPoint: 如果点包含在路径中,则返回true 坐标重置
if (CGPathContainsPoint(path, &CGAffineTransformIdentity, point, 0)) {
selectIndex = shapeLayer.index;
*stop = YES;
}
}
}];
second question:折线和直线的选择.
这个问题真的是纠结了我很久,产品那边给不了明确的定义,我不断的刷支付宝账单,无奈数据实在太少,看不错个所以然... 最后自己给了一个相对合理的规则(自认为~~)
1.从支付宝的账单上看,以圆环的中心为原点,p(0,R)为第一条数据的起点,且都是折线展示.
2.中间的数据好像都是直线(可能的采样数据太少了,数据占比太正常).靠下方的数据有些展示直线,有些展示折线.规律还是摸不透...
3.在经过多组数据测试后,如果前后两组数据在整个圆环中占比小,如果是两条直线出来,描绘上文本,上下两条线的文本就会有可能重合, 线条之间存在这一个最小的间距,以避免文本重合.
so 第一条数据(扇形区域)从p(0,R)的位置出发,且标记的线是折线,前后两条线的Y轴方向间距保留一个最小值(避免文本产生部分重叠),折线点的位置,这边计算出文本最长宽度,展示下文本之后,就开始转折(这种方式有点偷懒了,异常数据时展示效果不是很美观,但控制了view的整体高度).
简要代码逻辑如下:
if (CGPointEqualToPoint(lastCenterPoint, CGPointZero)) {
//第一个点默认折线
straightLinePoint = CGPointMake((centerCoordinate.x < self.circleCenter.x)?(centerCoordinate.x-self.polyLineDisplacement):(centerCoordinate.x+self.polyLineDisplacement), (centerCoordinate.y < self.circleCenter.y)?(centerCoordinate.y-self.polyLineDisplacement):(centerCoordinate.y+self.polyLineDisplacement));
}
else
{
if ((centerCoordinate.x > self.circleCenter.x && lastCenterPoint.x > self.circleCenter.x)) {
//和前一个点位于圆心同一侧(右侧)
CGFloat yMin = centerCoordinate.y - lastCenterPoint.y;
if (yMin < KLineMinPaddingValue) {
straightLinePoint = CGPointMake(CGRectGetWidth(self.bounds)- maxTextWidth-KViewPadding, lastCenterPoint.y+KLineMinPaddingValue);
}
}
else if (centerCoordinate.x < self.circleCenter.x && lastCenterPoint.x < self.circleCenter.x)
{
//和前一个点位于圆心同一侧(左侧)
straightLinePoint = CGPointMake(maxTextWidth+KViewPadding, lastCenterPoint.y-KLineMinPaddingValue);
}
else
{
//没有在同一侧不会有文本交集 直接用直线 不需要设置
}
}
哈最后实现效果如下:
这个实现效果 有个弊端,当文本长度过长时,线的轨迹会出现异常.
当前后两个数据接近的时候,为了避免产生文本重合,需要改变折线的角度,整个试图的高度可能会产生相应的变动.