技术人生之pointinside 自定义按钮

iOS 圆角那些事

似乎没有那家公司比Apple更爱圆角了,事实上,圆角也会让图形/产品看起来更加无侵略性,能够带来更好的用户体验.
iOS开发中各种圆角也随处可见,最简单给控件添加圆角的方式就是给视图的layer设置corner属性了:

self​​​​.blueView.layer.cornerRadius = 5.f; ​​
​​self​​​​.blueView.layer.masksToBounds = ​​​​YES​​​​;​​

这种方式会带来两个问题:

当图片数量比较多的时候,这种添加圆角方式特别消耗性能,比如在UITableViewCell
添加过多圆角的话,甚至会带来视觉可见的卡顿.
无法配置圆角数量(只能添加view的四个角全为圆角),无法配置某个圆角大小.
第一个问题实际上是由于数量太多的情况下,系统会频繁的调用GPU的离屏渲染(Offscreen Rendering)机制,导致内存损耗严重.更多关于离屏渲染的详解,可以看​​ ​这里​​​,本文不多赘述.
第二个问题,我们可以使用UIBezierPath
来完美解决.以下是示例代码:

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:​​​​self​​​​.blueView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(20, 0)]; ​​
​​CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];​​
​​maskLayer.frame = ​​​​self​​​​.blueView.bounds; ​​
​​maskLayer.path = maskPath.CGPath; ​​
​​self​​​​.blueView.layer.mask = maskLayer; ​​
​​self​​​​.blueView.layer.cornerRadius = 5.f; ​​
​​self​​​​.blueView.layer.masksToBounds = ​​​​YES​​​​;​​

想要配置某个角为圆角的话,只需要指定对应的UIRectCorner
即可

也可以采用下面这种方法 : 采取预先生成圆角图片,并缓存起来这个方法才是比较好的手段。预处理圆角图片可以在后台处理,处理完毕后缓存起来,再在主线程显示,这就避免了不必要的离屏渲染了。

self​​​​.layer.cornerRadius = 6;​​
​​self​​​​.layer.masksToBounds = ​​​​YES​​​​; ​​​​// 裁剪​​
​​self​​​​.layer.shouldRasterize = ​​​​YES​​​​; ​​​​// 缓存​​
​​self​​​​.layer.rasterizationScale = [UIScreen mainScreen].scale;​​

当shouldRasterize设成true时,layer被渲染成一个bitmap,并缓存起来,等下次使用时不会再重新去渲染了。实现圆角本 身就是在做颜色混合(blending),如果每次页面出来时都blending,消耗太大,这时shouldRasterize = yes,下次就只是简单的从渲染引擎的cache里读取那张bitmap,节约系统资源。

如果在滚动tableView时,每次都执行圆角设置,肯定会阻塞UI,设置这个将会使滑动更加流畅
圆角(RounderCorner)是一种很常见的视图效果,相比于直角,它更加柔和优美,易于接受。但很多人并不清楚如何设置圆角的正确方式和原理。设置圆角会带来一定的性能损耗,如何提高性能是另一个需要重点讨论的话题。我查阅了一些现有的资料,收获良多的同时也发现了一些误导人错误。

layer.backgroundColor = [UIColor cyanColor].CGColor; // 给图层添加背景色
layer.contents = (id)[UIImage imageNamed:@“view_BG.png”].CGImage; // 给图层添加背景图片
layer.cornerRadius = 8; // 将图层的边框设置为圆脚
layer.masksToBounds = YES; // 隐藏边界
layer.borderWidth = 5; // 给图层添加一个有色边框
layer.borderColor = [UIColor colorWithRed:0.52 green:0.09 blue:0.07 alpha:1].CGColor;
layer.shadowOffset = CGSizeMake(0, 3); // 设置阴影的偏移量
layer.shadowRadius = 10.0; // 设置阴影的半径
layer.shadowColor = [UIColor blackColor].CGColor; // 设置阴影的颜色为黑色
layer.shadowOpacity = 0.9; // 设置阴影的不透明度
2. 使用绘图设置圆角

/** 设置圆形图片(放到分类中使用) */

  • (UIImage *)cutCircleImage {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0); // 获取上下文
    CGContextRef ctr = UIGraphicsGetCurrentContext(); // 设置圆形
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextAddEllipseInRect(ctr, rect); // 裁剪
    CGContextClip(ctr); // 将图片画上去
    [self drawInRect:rect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
    }
  1. 通过另一张mask图创建新图

首先需要一张mask图,然后将这张mask图和原图合成,得到带圆角的新图。效率和方法一类似,合成新图等同于在off-screen作图。该方法的优点是可以不局限于圆角,全凭mask图控制。
TYTabbarAnimationDemo
业务需求导致需要做一个tabbar,里面的按钮点击带有动画效果,tabbar中间的按钮凸出,凸出部分可以点击,支持badge 小红点等,为此封装了一个高度可定制的tabbar -> TYTabBar demo下载地址:https://github.com/qqcc1388/TYTabbarAnimationDemo

TYTabBar可以快速实现以下功能

每个Item都有单击,双击事件回调
tem可以支持多种动画(帧动画,缩放动画,旋转动画),每个Item都可以单独设置
支持badgeText,支持小红点功能
只需要配置一下,就可以实现不规则按钮效果,并且超出边界仍然有点击效果
支持横竖屏切换
效果图(由于图片资源的问题导致动画切换比较生硬,更改为满足尺寸的资源就好啦)

思路回顾(TYTabBar)
系统的Tabbar功能不算完善,有时候没法完全满足需求,我们这里通过kvc的方式把系统的tabbar替换成我们自己定义的tabbar。
[self setValue:tabbar forKeyPath:@“tabBar”];
自定义一个TYTabBar继承UITabBar这样就可以继承很多系统TabBar既有很多属性和功能
@interface TYTabBar : UITabBar
初始化配置信息(很重要关系到Item个数,PlusItem的位置)
#define barItemCount 5 //tabbarItem 个数
#define barItemPlusButtonIndex 2 //➕按钮的位置 -1表示不存在+按钮 从0开始
#define barItemFontSize 12 //字体大小
#define barItemNomalTextColor [UIColor grayColor] //字体默认颜色
#define barItemSelectedTextColor [UIColor redColor] //选中字体颜色
#define barItemSubMargin 3 //文字和图片的间距
初始化需要TYTabBar中需要展示的按钮并保存起来,并添加点击事件 传入默认第几个tabbarItem默认被选中
-(void)loadItemsWithData:(NSArray<TYBarItemModel *> *)itemModels defaultSelect:(NSInteger)index{
//初始化
NSMutableArray *mutalArr = [NSMutableArray array];

TYAnimationButton *tempItem = nil;
for (int i = 0; i < barItemCount; i++) {
    //取出model
    TYBarItemModel *model = [itemModels objectAtIndex:i];
    TYAnimationButton *button = [[TYAnimationButton alloc] init];
    button.images = model.images;
    [button setImage:[UIImage imageNamed:model.normalImage] forState:UIControlStateNormal];
    [button setImage:[UIImage imageNamed:model.selectedImage] forState:UIControlStateSelected];
    [button setTitle:model.title forState:UIControlStateNormal];
    button.tag = i;
    button.titleLabel.font = [UIFont systemFontOfSize:barItemFontSize];
    [button setTitleColor:barItemSelectedTextColor forState:UIControlStateSelected];
    [button setTitleColor:barItemNomalTextColor forState:UIControlStateNormal];
    [button addTarget:self action:@selector(itemClick:) forControlEvents:UIControlEventTouchDown];
    button.layoutType = LXButtonLayoutTypeImageTop;
    button.subMargin = barItemSubMargin;
    button.animationType = model.animationType;
    [self addSubview:button];

    //第index按钮默认选中
    if (i == index) {
        tempItem = button;
    }

    //保存按钮
    [mutalArr addObject:button];
}

self.myItems = mutalArr;
//设置默认选中第index个
if (tempItem) {
    [self itemClick:tempItem];
}else{
    //如果传入的index无效 则默认选中第0个
    TYAnimationButton *item = self.myItems.firstObject;
    [self itemClick:item];
}

}

在TabBar的layoutSubviews方法中找到对应的系统的TabBarItem并隐藏起来,创建我们自己的Button并占用系统TabBarItem的位置
-(void)layoutSubviews{
[super layoutSubviews];

Class class = NSClassFromString(@"UITabBarButton");

int btnIndex = 0;

//假设这里有5个item
CGFloat width = self.bounds.size.width/barItemCount*1.0;
CGFloat height = self.bounds.size.height;
for (UIView *btn in self.subviews) {//遍历tabbar的子控件
    if ([btn isKindOfClass:class]) {
        
        //先隐藏系统的tabbarItem
        btn.hidden = YES;
        
        //取出前面保存的按钮
        UIButton *item = [self.myItems objectAtIndex:btnIndex];
        
        //设置item的位置
        item.frame = CGRectMake(width*btnIndex, 0, width, height);
        
        if (barItemPlusButtonIndex == btnIndex) {  //plusButton位置  具体偏移根据时间情况进行调节
            item.frame = CGRectMake(width*btnIndex, -20, width, height+20);
            item.subMargin = 10;
        }
        btnIndex++;
    }
}

}

单击 双击事件处理和传递(这里通过代理将信息传递出去realDelegate一定要要实现,涉及到控制器跳转)
-(void)itemClick:(TYAnimationButton *)item{

//双击效果处理
//2次双击之间间隔至少1s
static  NSTimeInterval lastClickTime = 0;
if ([self checkIsDoubleClick:item]) {
    NSTimeInterval clickTime = [NSDate timeIntervalSinceReferenceDate];
    if (clickTime - lastClickTime > 1) {  //去掉连击的可能性
        if (_realDelegate && [_realDelegate respondsToSelector:@selector(tabBar:doubleClick:)]) {
            [self.realDelegate tabBar:self doubleClick:item.tag];
        }
    }
    lastClickTime = clickTime;
}

//按钮重复点击没有效果
if (item == _lastItem && self.canRepeatClick) { //可以重复点击
    if (!item.isAnimating) { //正在动画什么都不做否则开始动画
        [item animationStart];
    }
}else if(item != _lastItem){  //不可以重复点击
    //先把上次item动画关闭
    _lastItem.selected = NO;
    [_lastItem animationStop];
    
    //新点击的item动画开启
    item.selected = YES;
    [item animationStart];
    //把按钮的点击状态传到出去 让tabbarController可以切换控制器
    if (_realDelegate && [_realDelegate respondsToSelector:@selector(tabBar:clickIndex:)]) {
        [self.realDelegate tabBar:self clickIndex:item.tag];
    }
    _lastItem = item;
}

}

  • (BOOL)checkIsDoubleClick:(UIButton *)currentBtn
    {
    static UIButton *lastBtn = nil;
    static NSTimeInterval lastClickTime = 0;

    if (lastBtn != currentBtn) {
    lastBtn = currentBtn;
    lastClickTime = [NSDate timeIntervalSinceReferenceDate];

      return NO;
    

    }

    NSTimeInterval clickTime = [NSDate timeIntervalSinceReferenceDate];
    if (clickTime - lastClickTime > 0.5 ) {
    lastClickTime = clickTime;
    return NO;
    }

    lastClickTime = clickTime;
    return YES;
    }
    badgeText 小红点设置(参考JSBadgeView并在源码上做了一点修改) 显示数字 @“2” 不显示内容@““或者nil 显示小红点@”.”
    -(void)badgeText:(NSString *)text forIndex:(NSInteger)index{
    //给指定的badgeText设置角标
    if (index < 0 || index >(self.myItems.count - 1)) {
    return;
    }
    TYAnimationButton *item = self.myItems[index];

    //设置角标
    [item setBadgeText:text];

}
思路回顾(TYAnimationButton)
TYAnimationButton是整个TYTabBar的核心,每一个Item都是一个TYAnimationButton
提供的动画类型
typedef NS_ENUM(NSUInteger, TYBarItemAnimationType) {

TYBarItemAnimationTypeNomal = 0, //系统默认tabar效果
TYBarItemAnimationTypeFrames,    //帧动画(imageView)
TYBarItemAnimationTypeScale,     //缩放动画
TYBarItemAnimationTypeRotate     //旋转动画

};
关于动画提供了2个动画方法 一个开始动画,一个结束动画,如果是帧动画,需要传入动画帧
#pragma mark - 动画
-(void)animationStart{

if (_animationType == TYBarItemAnimationTypeNomal) {  //系统默认不带动画
    
}else if(_animationType == TYBarItemAnimationTypeFrames){  //帧动画
    if (_images) {  //有提供动画图片的才可以动画
        if (!self.imageView.isAnimating) {  //没有动画 则开启动画 如果当前正在动画 则什么都不做
            [self frameAnimation];
        }
    }
}else if(_animationType == TYBarItemAnimationTypeScale){  //
    [self scaleAnimationRepeatCount:1];
}else if(_animationType == TYBarItemAnimationTypeRotate){
    [self rotateAnimation];
}else{
    
}

}

-(void)animationStop{
if (_animationType == TYBarItemAnimationTypeNomal) {

}else if(_animationType == TYBarItemAnimationTypeFrames){
    if (_images) {  //有提供动画图片的才可以动画
        if (self.imageView.isAnimating)
        {  //正在动画 则开始动画
            self.imageView.animationImages = nil;
            [self.imageView stopAnimating];
        }
    }
}else if(_animationType == TYBarItemAnimationTypeScale){
    
}else if(_animationType == TYBarItemAnimationTypeRotate){
    
}else{
    
}

}

-(void)frameAnimation{
//设置动画图片 给button imageView 添加帧动画
UIImageView * imageView = self.imageView;
//设置动画帧
NSMutableArray *mutalImages = [NSMutableArray array];
for (NSString *imageName in self.images) {
[mutalImages addObject:[UIImage imageNamed:imageName]];
}
imageView.animationImages= mutalImages;
//设置动画总时间
imageView.animationDuration = _duration;
//设置重复次数,0表示无限
imageView.animationRepeatCount = 1;

[imageView startAnimating];

}

//缩放动画

  • (void)scaleAnimationRepeatCount:(float)repeatCount {
    //需要实现的帧动画,这里根据需求自定义
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @“transform.scale”;
    animation.values = @[@1.0,@1.3,@0.9,@1.15,@0.95,@1.02,@1.0];
    animation.duration = 1;
    animation.repeatCount = repeatCount;
    animation.calculationMode = kCAAnimationCubic;
    [self.imageView.layer addAnimation:animation forKey:nil];
    }

//旋转动画

  • (void)rotateAnimation {
    // 针对旋转动画,需要将旋转轴向屏幕外侧平移,最大图片宽度的一半
    // 否则背景与按钮图片处于同一层次,当按钮图片旋转时,转轴就在背景图上,动画时会有一部分在背景图之下。
    // 动画结束后复位
    // CGFloat oldZPosition = self.layer.zPosition;//0
    self.layer.zPosition = 65.f / 2;
    [UIView animateWithDuration:0.32 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
    self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
    } completion:nil];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [UIView animateWithDuration:0.70 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{
    self.imageView.layer.transform = CATransform3DMakeRotation(2 * M_PI, 0, 1, 0);
    } completion:nil];
    });
    }

给每个TYAnimationButton添加一个badgeView使其具备角标的功能 监听屏幕横竖屏变化并实时修改badgeView的位置
/**
角标x轴方向的偏移 默认15
*/
@property (nonatomic,assign) CGFloat badgeOffsetX;

/**
角标y轴方向的偏移 默认15
*/
@property (nonatomic,assign) CGFloat badgeOffsetY;

/**
角标x轴方向的偏移(横屏状态) 默认15 请根据具体需求微调
*/
@property (nonatomic,assign) CGFloat badgeLandscapeOffsetX;

/**
角标y轴方向的偏移(横屏状态) 默认40 请根据具体需求微调
*/
@property (nonatomic,assign) CGFloat badgeLandscapeOffsetY;

-(void)layoutSubviews{
[super layoutSubviews];
if((self.bounds.size.width !=0 && !self.badgeView) || self.orientation){
//先移除badgeView
[self.badgeView removeFromSuperview];

    //重新添加新的badgeView
    self.badgeView = [[JSBadgeView alloc] initWithParentView:self alignment:JSBadgeViewAlignmentTopRight];
    //设置角标参数
    _badgeView.badgeTextFont = [UIFont systemFontOfSize:12];
    if (self.orientation == UIDeviceOrientationLandscapeLeft || self.orientation == UIDeviceOrientationLandscapeRight) { //横屏状态
        _badgeView.badgePositionAdjustment = CGPointMake(-_badgeLandscapeOffsetX, _badgeLandscapeOffsetY);
    }else{  //竖屏状态
        _badgeView.badgePositionAdjustment = CGPointMake(-_badgeOffsetX, _badgeOffsetY);
    }
    _badgeView.badgeStrokeWidth = 0.2;
    _badgeView.badgeText  = _badgeText;
    
    //清除旋转状态
    self.orientation = UIDeviceOrientationUnknown;
}

}
关于使用
初始化tabbar
-(void)setupTabbar{
TYTabBar *tabbar = [[TYTabBar alloc] init];
self.tyTabbar = tabbar;
self.tyTabbar.realDelegate = self;
//给tabbar设置内容
//准备数据
NSArray *arr = @[@“竞猜”, @“赛事”, @“发现”, @“优惠”, @“我的”]; //title
NSArray *animationImageArr = @[self.quizAnimationImages, self.matchAnimationImages, @[], self.discountAnimationImages, self.mineAnimationImages];
NSArray *barImages = @[@[@“quiz_normal”, @“quiz_selected”], //按钮默认图片和选中后图片
@[@“match_normal”, @“match_selected”],
@[@“discovery_normal”, @“discovery_selected”],
@[@“discount_normal”, @“discount_selected”],
@[@“mine_normal”, @“mine_selected”],
];
NSArray *anmations = @[@(TYBarItemAnimationTypeFrames),@(TYBarItemAnimationTypeFrames),@(TYBarItemAnimationTypeRotate),@(TYBarItemAnimationTypeFrames),@(TYBarItemAnimationTypeFrames)];

NSMutableArray *datas = [NSMutableArray array];
for (int i = 0;  i < 5; i++) {
    //0. 根据需求选择填写对应的内容
    //1. 每个按钮都可以单独设置图片 文字 动画效果(暂时只有4种效果)等
    //2. 如果点击后不需要帧动画 images可以传nil
    //3. 如果所有按钮都是统一动画,AnimationType 默认传一个值就可以了
    //4. title为BarItem显示的文字
    //5. nomalImage selectdImage 为BarItem选中和非选中的图片 选中和非选中按钮颜色在TYTabBar中配置
    TYBarItemModel *model = [[TYBarItemModel alloc] initWithTitle:arr[i] images:animationImageArr[i] normalImage:barImages[i][0] selectedImage:barImages[i][1] AnimationType:[anmations[i] integerValue]];
    [datas addObject:model];
}
//初始化tabbar数据
[self.tyTabbar loadItemsWithData:datas defaultSelect:2];

//替换系统的tabbar
[self setValue:tabbar forKeyPath:@"tabBar"];

//如果希望TabbarItem重复点击都有动画效果 则需要设置canRepeatClick= YES 默认为NO
self.tyTabbar.canRepeatClick = YES;

//设置角标
[self.tyTabbar badgeText:@"9" forIndex:0];
[self.tyTabbar badgeText:@"." forIndex:3];

}
初始化控制器

  • (void) loadViewControllers {
    //这里的title是导航栏的标题
    ViewController* homepageVC = [[ViewController alloc] init];
    [self setUpOneChildVcWithVc:homepageVC title:@“竞猜”];

    ViewController * classifyVC = [[ViewController alloc] init];
    [self setUpOneChildVcWithVc:classifyVC title:@“赛事”];

    ViewController* shoppingCartVC = [[ViewController alloc] init];
    [self setUpOneChildVcWithVc:shoppingCartVC title:@“发现”];

    ViewController * searchVC = [[ViewController alloc] init];
    [self setUpOneChildVcWithVc:searchVC title:@“优惠”];

    ViewController* personalCenterVC = [[ViewController alloc] init];
    [self setUpOneChildVcWithVc:personalCenterVC title:@“我的”];
    }

  • (void)setUpOneChildVcWithVc:(UIViewController *)Vc title:(NSString *)title {
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:Vc];

    Vc.navigationItem.title = title;

    [self addChildViewController:nav];
    }

tabbar代理方法使用
-(void)tabBar:(TYTabBar *)tabbar clickIndex:(NSInteger)index{
[self setSelectedIndex:index];
}

-(void)tabBar:(TYTabBar *)tabBar doubleClick:(NSInteger)index{
NSLog(@“第%zi个Item被双击”,index);
//根据选中的Inex,拿到对应的控制器然后让控制器刷新数据

UINavigationController *naviVC = (UINavigationController *)self.selectedViewController;
ViewController *dpVc = (ViewController *)naviVC.viewControllers.firstObject;
[dpVc reloadColor];

}
更多详细使用参考demo中代码

参考来源:

JSBadgeView https://github.com/JaviSoto/JSBadgeView
CYLTabBarController https://github.com/ChenYilong/CYLTabBarController

我们先来看看效果 :
未命名.gif
一、关于贝塞尔曲线UIBezierPath :
关于贝塞尔曲线的 : 基本概念和使用方法 .

二、使用:
1.创建贝塞尔曲线路径path对象.
UIBezierPath *path = [UIBezierPath bezierPath];
2.设置绘制图形的路径起点.
CGPoint point = CGPointMake(<#CGFloat x#>, <#CGFloat y#>);
[path moveToPoint:point];
3.设置绘制图形的路径的其他点(简单理解为转折点、转向点).
CGPoint point = CGPointMake(<#CGFloat x#>, <#CGFloat y#>);
[path addLineToPoint:point];
三、举例子:以左上角梯形为例 ↓
// 1、创建按钮
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(20, 100, 120, 50);
btn.backgroundColor = [UIColor orangeColor];
[btn setTitle:@“按钮” forState:UIControlStateNormal];
[btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
// 2、设置贝塞尔曲线路径

/* 所用宏:
#define kViewWidth(View) CGRectGetWidth(View.frame)
#define kViewHeight(View) CGRectGetHeight(View.frame)
*/

UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(0.f, 0.f)];
[path addLineToPoint:CGPointMake(kViewWidth(btn), 0.f)];
[path addLineToPoint:CGPointMake(kViewWidth(btn) *3/4, btn.frame.size.height)];
[path addLineToPoint:CGPointMake(0.f, kViewHeight(btn)];

[path closePath];

// 3、绘制图形时添加path遮罩

  • (void)drawRect:(CGRect)rect
    {
    [super drawRect:rect];
    CAShapeLayer *shapLayer = [CAShapeLayer layer];
    shapLayer.path = self.path.CGPath;
    self.layer.mask = shapLayer;
    }
    至此绘制出一个梯形:
    Simulator Screen Shot - iPhone 6 - 2018-04-24 at 15.39.13.png
    但是,绿色箭头所指区域还是可以响应Button的点击手势.
    E2A1C616-3428-498B-B7A3-46533B65471D.png
    为了只让我们绘制出来的梯形触发手势,在这里我们需要增加一个点击区域的判断:
    // 点击的覆盖方法,点击时判断点是否在path内,YES则响应,NO则不响应
  • (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    {
    BOOL res = [super pointInside:point withEvent:event];
    if (res)
    {
    if ([self.path containsPoint:point])
    {
    return YES;
    }
    return NO;
    }
    return NO;
    }
    完。

这里主要是pointinside。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值