自定义形状按钮的实现

对于大多数iOS开发来说,这个是很少遇到的,毕竟一个不规则的按钮再移动端不常见,但是免不了会遇到某些特殊的需求,比如一个饼状图,比如一个扇形图,比如中国地图中的每个省…..
我们很可能在某一次开发中需要实现不规则形状的按钮,那么我们怎么去实现呢?
其实这只是一个很小很小的知识点,却有很多人不会去考虑,所以就遇到了这样的情况

偶然有一天,朋友给了我一套产品原型,他说叫我帮他写几个按钮,很简单的按钮,我当时感觉有坑,但是也没有想太多,想着本来这段时间有点闲,就帮下忙,
于是他发来原型图

哟,就是很简单的4+1个按钮蛮
先写4个正方形的九宫格式的按钮,再在中心加一个圆形按钮就好了呀.
于是接下来他又发来产品设计图

果然,有了设计之后,瞬间好看了好多,
但是想象中也不复杂呀.
先用一个view当底图,然后5个按钮加在这个view上,然后view来个半径,不就结束了吗?
但是事实真的是这么简单吗?

显然不是,一个很明显的结果就是,你会发现你使用了圆形半径的不显示的部分仍然能够响应按钮的点击.
这个是什么鬼,其实很简单的原理,就是虽然我们设置了半径,让圆形外的图层不显示,但是并不是代表其不存在,其仍然是可以点击的,这样在某些情况下就不符合按钮的设计标准了,因为我们想要的是所见即所得

那么所见即所得便成了我要实现的目标.
还得从按钮上下手,不要想得这么简单,虽然这个案例是一个很简单的比较偏正常形状的一个按钮,但是我需要将其想象成一个很复杂的不规则的图形来处理

1.因为是自定义形状的按钮,那么说白了,它还是一个按钮,我们没有必要自定义复杂的控件,继承UIButton即可

2.构思如何实现特殊形状
其实这块很容易想到,实现一个圆形按钮,就是对layer进行修改形状而已

如果你有阅读过iOS核心动画高级技巧的话,你很容易发现我们想要的东西

是的,这里我们能看到几个关键点,一个原来的背景+一个mask遮罩 = 一个自定义形状的图形

引用到我们想要的按钮上,我们就会发现同样适用,
那么我们必须要在适用图片来制作mask吗?
很显然,我们在开发中不可能这么去做,那么我们如何自定义一个形状呢?


于是我们能够很方便的使用UIBezierPath来绘制成我们想要的形状,然后使用CAShapeLayer来根据形状创建图层的路径
创建完成之后,我们使用这样的图层来当做按钮的mask即可

所以对于我们来说比较有用的就是一个贝塞尔曲线的定制
于是很简单的一个不规则按钮的类即可实现

#import <UIKit/UIKit.h>@interface IrregularButton : UIButton@property(nonatomic, strong)UIBezierPath *maskPath;@end#import "IrregularButton.h"@implementation IrregularButton-(void)setMaskPath:(UIBezierPath *)maskPath{_maskPath = maskPath;CAShapeLayer *layer = [[CAShapeLayer alloc]init];layer.path = maskPath.CGPath;self.layer.mask = layer;
}@end

当我们需要定制一个特殊按钮的时候,我们只要继承该类 ,或者直接使用该类
,并给maskPath赋予一个我们想要的路径即可

于是上面的四个半圆按钮甚至全圆形的按钮我们就可以轻松实现

#import "IrregularButton.h"typedef NS_ENUM(NSUInteger, QuarterCircleType) {QuarterCircleTypeTopNone,QuarterCircleTypeTopLeft,QuarterCircleTypeTopRight,QuarterCircleTypeBottomLeft,QuarterCircleTypeBottomRight,QuarterCircleTypeAllCircle,
};@interface QuarterCircleButton : IrregularButton@property(nonatomic, assign)QuarterCircleType circleType;@end#import "QuarterCircleButton.h"@implementation QuarterCircleButton-(void)setFrame:(CGRect)frame{[super setFrame:frame];self.circleType = self.circleType;
}-(void)setCircleType:(QuarterCircleType)circleType{_circleType = circleType;CGFloat width = self.frame.size.width;CGFloat height = self.frame.size.height;switch (circleType) {case QuarterCircleTypeTopLeft:{CGPoint bottomRightPoint = CGPointMake(width, height);CGPoint bottomLeftPoint = CGPointMake(0, height);UIBezierPath *path = [[UIBezierPath alloc]init];[path moveToPoint:bottomRightPoint];[path addLineToPoint:bottomLeftPoint];[path addArcWithCenter:bottomRightPoint radius:MAX(width, height) startAngle:M_PI endAngle:-M_PI_2 clockwise:YES];[path addLineToPoint:bottomRightPoint];[path closePath];self.maskPath = path;}break;case QuarterCircleTypeTopRight:{CGPoint bottomLeftPoint = CGPointMake(0, height);CGPoint topLeftPoint = CGPointMake(0, 0);UIBezierPath *path = [[UIBezierPath alloc]init];[path moveToPoint:bottomLeftPoint];[path addLineToPoint:topLeftPoint];[path addArcWithCenter:bottomLeftPoint radius:MAX(width, height) startAngle:-M_PI endAngle:0 clockwise:YES];[path addLineToPoint:bottomLeftPoint];[path closePath];self.maskPath = path;}break;case QuarterCircleTypeBottomLeft:{CGPoint topRightPoint = CGPointMake(width, 0);CGPoint bottomRightPoint = CGPointMake(width, height);UIBezierPath *path = [[UIBezierPath alloc]init];[path moveToPoint:topRightPoint];[path addLineToPoint:bottomRightPoint];[path addArcWithCenter:topRightPoint radius:MAX(width, height) startAngle:M_PI_2 endAngle:M_PI clockwise:YES];[path addLineToPoint:topRightPoint];[path closePath];self.maskPath = path;}break;case QuarterCircleTypeBottomRight:{CGPoint topLeftPoint = CGPointMake(0, 0);CGPoint topRightPoint = CGPointMake(width, 0);UIBezierPath *path = [[UIBezierPath alloc]init];[path moveToPoint:topLeftPoint];[path addLineToPoint:topRightPoint];[path addArcWithCenter:topLeftPoint radius:MAX(width, height) startAngle:0 endAngle:M_PI_2 clockwise:YES];[path addLineToPoint:topLeftPoint];[path closePath];self.maskPath = path;}break;case QuarterCircleTypeAllCircle:{CGPoint centerPoint = CGPointMake(width/2, height/2);UIBezierPath *path = [[UIBezierPath alloc]init];[path addArcWithCenter:centerPoint radius:MAX(width/2, height/2) startAngle:0 endAngle:M_PI * 2 clockwise:YES];[path closePath];self.maskPath = path;}break;default:break;}
}

于是,很轻松的我创建了这样的图形

但是问题仍然不可忽视的,又来了,在半圆形外面的四个区域仍然可以相应按钮点击,
解释下来也就是mask外面的区域虽然看不到,但是其仍然属于按钮的区域,仍然可以响应点击,

所以我们需要规避
那么如何规避呢?
其毕竟仍然是一个不规则的形状啊?
其实我们前期已经做好准备了….

3.规避不规则区域外的点击
我们只要简单的实现这个点击是否响应的方法即可
于是完整的不规则按钮的实现是这样的

#import <UIKit/UIKit.h>@interface IrregularButton : UIButton@property(nonatomic, strong)UIBezierPath *maskPath;@end#import "IrregularButton.h"@implementation IrregularButton-(void)setMaskPath:(UIBezierPath *)maskPath{_maskPath = maskPath;CAShapeLayer *layer = [[CAShapeLayer alloc]init];layer.path = maskPath.CGPath;self.layer.mask = layer;
}-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{BOOL res = [super pointInside:point withEvent:event];if (res) {if (!self.maskPath || [self.maskPath containsPoint:point]) {return YES;}return NO;}return res;
}@end

我们只要保证点击的point在我们的mask的区域内即可(这里要保证我们的mask是一个封闭的区域),在mask区域内允许点击,区域外则不允许点击,
于是一个maskPath被我们使用了两次,第一次是设置遮罩显示不规则按钮,第二次是判断点击时候的响应区域

至此,能够适应各种自定义形状的基类按钮便完成了
当我们想要实现的时候,我们只要根据原型中的不规则形状绘制我们想要的图形来创造一个贝塞尔曲线即可(这部分因为形状不同,我们只能自己绘制)

当然我们也有第三方工具
如果你觉得绘制一个不规则形状太麻烦的话(偷懒)
paintCode也可以帮你的忙,你只要动动鼠标既可以跟类似ps一样的绘制一个不规则图形,而这个工具会自动帮你把贝塞尔曲线的代码给写好,你只要复制进去即可

好了demo奉上吧…
https://github.com/spicyShrimp/IrregularButton

自定义形状按钮的实现相关推荐

  1. java 自定义形状按钮_制作自定义背景Button按钮、自定义形状Button的全攻略

    在Android开发应用中,默认的Button是由系统渲染和管理大小的.而我们看到的成功的移动应用,都是有着酷炫的外观和使用体验的.因此,我们在开发产品的时候,需要对默认按钮进行美化.在本篇里,笔者结 ...

  2. AIR 窗口,自定义形状窗口,按钮

    AIR ウィンドウの基礎 ネイティブウィンドウのスタイルと動作を制御するプロパティ http://help.adobe.com/ja_JP/AIR/1.1/devappsflex/WS5b3ccc51 ...

  3. 图片按钮+自定义形状

    //Create by ChimHsiung 2016 //图片按钮+自定义形状 using System; using System.Collections.Generic; using Syste ...

  4. ps自定义形状工具_【福利】3000款PS自定义形状免费打包下载

    PS形状介绍 获取方式见文末 ps自定义形状素材包包含例如:品牌标志Logo.动物形状,花卉形状,圆圈形状,边框形状,人物形状,海洋生物形状,南瓜形状,万圣节形状,扑克牌形状等等.合计3000款!如果 ...

  5. [安卓] 18、一个简单的例子做自定义动画按钮和自定义Actionbar

    在做安卓UI的时候有时候需自定义具有动画效果的按钮或需要自定义一下actionbar~ 本节用一个简单的demo讲如何自定义具有动画效果的按钮,以及个性化的actionbar 下面是效果: 其中: △ ...

  6. Tableau:如何自定义形状

    1. 自定义形状   常见的"形状"用法如下,这里可以给不同客户类型指定不同的形状. 除了"标记"下的"形状"选项卡中预先设置的形状外,用户 ...

  7. 如何在php中定义按钮,ppt中怎么插入自定义动作按钮

    ppt插入自定义动作按钮: 打开幻灯片文件,然后在菜单栏中点击插入,打开插入工具栏,之后在插入工具栏中选择形状工具,如下图所示: 点击形状工具后,就打开所有预置的形状列表,如下图所示,选择我们需要的形 ...

  8. iOS 自定义返回按钮,保留系统滑动返回

    1.简介 使用苹果手机,最喜欢的就是用它的滑动返回.作为一个开发者,我们在编写很多页面的时候,总是会因为这样那样的原因使得系统的滑动返回不可用.使用导航栏push出一个控制器,我们在控制器中自定义了一 ...

  9. 从Chrome中的css自定义样式按钮中删除蓝色边框

    本文翻译自:Remove blue border from css custom-styled button in Chrome I'm working on a web page, and I wa ...

  10. draw.io创建自定义形状

    使用文本编辑器在diagrams.net中创建自定义形状 你可以在diagrams.net中创建自己的自定义模板(形状),通过XML格式描述模板中组件的几何形状.连接点和样式. 提示:你可以将自定义模 ...

最新文章

  1. rpm包安装mysql5.6
  2. AI:百度飞桨EasyDL多门视频课程,手把手教你如何定制高精度AI模型
  3. 岛屿类-网格类问题-DFS | 力扣695. 岛屿的最大面积
  4. 文档如何清除html,html清除浏览器缓存.docx
  5. C# 使用int.TryParse,Convert.ToInt32,(int)将浮点类型转换整数时的区别
  6. leetcode 231. 2 的幂
  7. RHEL 5服务篇—常用网络配置命令
  8. 我的runtime学习笔记
  9. maven服务器项目,Maven项目搭建
  10. 谷歌:修复0day漏洞的平均耗时比3年前减少28天
  11. 【Luogu1996】约瑟夫问题(模拟,解法汇总)
  12. 不是shell具有的功能和特点的是_环境监控主机具有哪些功能特点
  13. HTTP session的原理
  14. linux下无法正常打开pdf文件的解决方法
  15. 一般的病毒通过注册表自启动的方式不断完善中。。。。
  16. DB2错误码sqlcode对应表
  17. Discuz!教程之论坛设置发帖时默认打开附加选项等功能
  18. python利器官网-python利器app
  19. 纷享销客《快消行业CRM应用与选型指南》重磅发布
  20. (附源码)springboot基于java的超市购物管理系统 毕业设计 271611

热门文章

  1. 欧拉角与四元数互相转换
  2. ARM嵌入式系统C语言编程分析
  3. ARM嵌入式系统中的体系结构
  4. 分享一下我的面试和入职经历
  5. 计算机病毒note01
  6. Zabbix学习笔记(三)---使用短信报警
  7. 自动计数报警器c语言程序,自动计数报警器.ppt
  8. 如果U盘中了文件夹隐藏病毒,怎么办?
  9. 《惢客创业日记》2021.07.25(周日)当生日遇上套路
  10. MicrosoftActiveSync 安装