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
运行效果:点击最上方的区域 | 点击区域外
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)