UI基础 - UIBezierPath:FillMode

FillMode

1 - 在绘图中经常需要对路径进行填充操作。那么问题来了, 在一个路径的覆盖范围内,如何判断哪些区域需要被填充 ?其实很简单:在你的画布中设置一个点,判断这个点是否在路径覆盖范围内即可

2 - 多边形:不自交和自相交

① 不自交:一个多边形仅顶点处连接,而在画布内没有其他的公共交点

② 自相交:一个多边形除了顶点连接外,在画布内还有其他的公共交点

3 - 如果是一个不自交的多边形,那很容易判断一个点是否在这个多边形路径的覆盖范围内。但是如何判断一个点是否在自相交多边形内, 则需要用到一下两种原则来进行判断, 这两种原则也就是 FillMode

① Even-Odd Rule 奇偶原则:从路径覆盖范围内的任意一点做一条射线,如果与该射线相交的边的数量为奇数,则该点是路径的内部点,反之该点则是路径的外部点

② Nonzero Winding Number Rule 非零环绕数原则:在我们脑海中定义一个变量 count,然后从路径覆盖范围内的任意一点做一条射线,然后我们对每一条和该射线相交的路径进行统计,规则如是:当路径是从右向左穿过射线的时候 count++;当路径是从左向右穿过射线的时候 count--。当我们统计完所有相交的路径后,如果 count 不为 0,则该点是内部点,反之该点则是路径的外部点

6 - 代码示例:根据给出的坐标点绘制区域,并给区域添加轻拍手势。要求当点击某一区域,该区域就变换成绿色,其他区域为红色;当触点不在任意一区域内,那么所有区域均是红色

// - BezierView.h

1 #import <UIKit/UIKit.h>
2 @interface BezierView : UIView
3 
4 @property(nonatomic,strong)NSMutableArray *bezieArrray; // 贝塞尔数组
5 // 属性传值
6 @property(nonatomic,assign)int arrayIndex;// 下标,用来区分所绘的图
7 @property(nonatomic,assign)CGPoint currentPoint;// 点击的点
8 
9 @end

// - BezierView.m

 1 #import "BezierView.h"
 2 @implementation BezierView
 3 -(void)drawRect:(CGRect)rect{
 4 
 5     self.bezieArrray = [NSMutableArray array];
 6     // 数据源:坐标
 7     NSArray *arrayPoint1 = [NSArray arrayWithObjects:@[@100,@60],@[@180,@130],@[@160,@200],@[@120,@180],@[@140,@150],nil];
 8     NSArray *arrayPoint2 = [NSArray arrayWithObjects:@[@50,@230],@[@180,@320],@[@90,@300],nil];
 9     NSArray *arrayPoint3 = [NSArray arrayWithObjects:@[@299,@220],@[@299,@399],@[@200,@399],@[@200,@180],nil];
10     NSArray *allPointArray = [NSArray arrayWithObjects:arrayPoint1,arrayPoint2,arrayPoint3,nil];
11 
12     for (int i = 0; i < allPointArray.count; i++) {
13 
14        
15         self.arrayIndex +=i; // 下标
16         UIBezierPath *path_i = [UIBezierPath bezierPath];
17         // 将一条路径添加进数组
18         [self.bezieArrray addObject:path_i];
19         [path_i moveToPoint:CGPointMake([[[[allPointArray objectAtIndex:i] firstObject] firstObject] floatValue], [[[[allPointArray objectAtIndex:i] firstObject] lastObject] floatValue])];
20 
21         // 依次绘画的坐标点
22         for (int j = 0; j < (int)[[allPointArray objectAtIndex:i] count] -1; j++) {
23             [path_i addLineToPoint: CGPointMake([[[[allPointArray objectAtIndex:i]  objectAtIndex:j+1] firstObject] floatValue], [[[[allPointArray objectAtIndex:i] objectAtIndex:j+1] lastObject] floatValue])];
24         }
25 
26         // 存在点击区域的坐标
27         if([path_i containsPoint:self.currentPoint]){
28             [[UIColor greenColor] setFill];// 选中的颜色
29         }else{
30             [[UIColor redColor] setFill];  // 未选中的颜色
31         }
32 
33         [path_i closePath];
34         [path_i fill];
35         [UIColor.purpleColor setStroke];
36         path_i.lineWidth = 1.5;
37         [path_i stroke];
38     }
39 }
40 
41 @end

// - ViewController.m

 1 #import "ViewController.h"
 2 #import "BezierView.h"
 3 @interface ViewController (){
 4     int _arrayIndex;// 下标:区分所绘的图
 5 }
 6 @property(nonatomic,strong)BezierView *myView;
 7 @end
 8 
 9 @implementation ViewController
10 
11 - (void)viewDidLoad {
12     [super viewDidLoad];
13 
14     // 创建 View 并在 View 内绘图
15     _myView = [[BezierView alloc] initWithFrame:self.view.bounds];
16     _myView.backgroundColor = [UIColor yellowColor];
17     [self.view addSubview:_myView];
18 
19     // 给 View 添加点击事件
20     UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecognized:)];
21     [_myView addGestureRecognizer:tap];
22 
23 }
24 
25 - (void)tapGestureRecognized:(UITapGestureRecognizer *)gesture{
26 
27     // 获取点击的位置
28     CGPoint tapPoint = [gesture locationInView:_myView];
29     _myView.currentPoint = tapPoint;// 当前点击的点
30 
31     // 遍历路径
32     for (int i = 0; i < (int)_myView.bezieArrray.count; i++) {
33         // 获取相应的路径
34         UIBezierPath *path_i = [_myView.bezieArrray objectAtIndex:i];
35         
36         if ([path_i containsPoint:tapPoint]) {
37             _arrayIndex = i;// 获取对应的下标
38             _myView.arrayIndex = _arrayIndex;// 属性传值:下标
39             [_myView setNeedsDisplay];
40             return;
41         }
42     }
43     // 区域外的点通知重绘:区域全部为红色
44     [_myView setNeedsDisplay];
45 }
46 
47 @end

运行效果:点击最上方的区域 | 点击区域外

  

 

posted on 2018-04-13 16:55  低头捡石頭  阅读(18)  评论(0编辑  收藏  举报

导航