写在前面
本文后面附有github源码,建议大家下载后对着源码参看.本文实现比较简单基础,大家觉得好的话,劳驾github给颗星,哈哈。
背景
在项目开发中,我们经常会遇到这么一种情况:App中某些原生控件满足不了我们的需求,所以这时候我们需要自定义来让控件具有自己公司产品的风格.在大公司中,有很多原生控件都是被封装过的,这样大家在用的时候直接用就好了.自定义控件其实是一件一劳永逸的事情,很好的体现了封装思想.在做公司的项目中遇到一个经常遇到情况,用户更换头像弹出ActionSheet,但是设计效果和原生的又相差较大,所以我选择自定义封装,借这个需求,我简述一下自定义控件的一个过程.首先看下效果图:
样式一:
样式二:
样式三:
系统自带ActionSheet
其实个人感觉还是不错的,无奈与设计师风格不同,所以咱就换呗...
开始前思考
1.实现actionSheet效果选用什么作为载体?
要实现ActionSheet样式并不难,刚开始想来好几种控件实现形式,比如可以用UIView结合UIButton来实现,也可以用TableView这个最常用的表视图来实现.比较这两者发现,UITableView可以根据其代理实现不同数量的Sheet单元格,而且还有cell的selecte方法,对于每个sheet单元格的复杂样式,我们还可以通过自定义cell来实现,相比较Button不仅省去了动态创建Button数量的开销,而且系统封装性和可扩展性更好.所以选用tableView是一个比较好的选择.
2.如何封装提高复用性?
提高复用性这一步对于当前这个ActionSheet用TableView实现来说很简单,因为UITableView系统本身就给我自带了很多代理方法,通过代理方法我们可以控制创建Sheet的分区数量以及每个分区内sheet的单元格数量.
但是作为一个自定义控件,特别是github上好的一些第三方,不仅在功能上有很大的实用性,而且每个功能的使用上也是很简便,对我们来说使用越简单,说明封装性越好,在这里我个人觉得复用性的东西就是在一些模块高度封装的基础之上的多次代码延伸,拿这个ActionSheet来说,我最终希望的是当别人看到我这个控件之后,只需要关心你自己的业务需求,比如需要创建什么样式,需要创建多少个sheet单元格,关心这些就够了,所以我暴露给你的就是对应的给你一个创建样式,给你一个创建多少个的方法就可以了.当然这样也有缺点,封装性越好,别人再用的时候再去扩展性就越差,所以,在自定义一个控件的时候要尽可能多考虑到各种需求来提高你封装的代码适应性.
3.确定思路
使用UITableView+自定义Cell方式,根据TableView的不同代理方法控制ActionSheet的单元格数量以及头部及尾部分区的样式.
实现
创建TableView,在此我是将TableView添加在View上
实现TableView相关代理方法
自定义TableViewCell样式,并实现不同ActionSheet风格
测试
这一步到不是有什么技术含量,我写在这里也是为了还原实现这一个Demo的思路,但是这一步其实也很重要,因为测试之后才能知道自己的程序写的怎么样,是否可行,这一步直接会影响到后续的优化.我关注的点是:
功能是否实现了?
使用起来是否简单方便?
实现同样的功能,是否有更好的方法?
不好的地方怎么优化?
对以上关注的点来看,我当时写完之后记录是这样的:
功能基本实现,但是太单一,扩展性不高
使用起来不方便,初始化代码复杂,实现功能点代码过于分散
代码逻辑有待改进,代码复用性不强
UI处理比较粗糙
优化
通过上述测试之后,我把遇到的问题都列出来,然后一一进行优化.
1.功能单一,扩展性不高
通过上面的截图能看出来,我希望这个ActionSheet可以自定义不同的样式,比如头部和底部字体大小,颜色,高度;单元格字体大小,颜色,背景颜色以及行高,还有单元格不同的样式,是只需要文字还是需要图标加文字.这些应当提供给用户做更多的选择.但是这些样式并不是刚需,所以可以作为额外代码配置.可以单独设置某一项,也可以一项都不设置,那就会以默认样式来展示.通过属性方式提供给用户配置更多样式:
2.使用起来不方便,初始化代码复杂,实现功能点代码过于分散.
对于一个基本控件的初始化,苹果API给了我们很好的实例,创建时我们最好把这个控件最基本的需求放在初始化方法里,比如创建一个view 我们可以使用
UIView *view = [[UIView alloc] init];
也可以使用
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
这两种方法都是可以的,但是像TableView创建时,我们使用的
UITableView *table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) style:(UITableViewStyle)]
这个方法会带有一个style参数,这时我们就可以很方便快速的创建一个tableView的实例,但是像tableView的backgroundColor这样的参数我们就不会放在初始化方法里.对于一个ActionSheet的控件,我将标题,单元格标题,取消按钮标题以及代理作为初始化方法需要的参数,最终的初始化方法如下:
3.实现同样的功能,是否有更好的方法?
这个问题可以通过实现过程中遇到的问题相结合说明,Demo中有一个设置圆角的属性,通过设置圆角可以实现和苹果官方提供的ActionSheet类似的风格,当时这个sheet单元格第一个和最后一个都是半边圆角,但是中间没有圆角的样式,我想过要不要通过判断样式用背景图片来实现,但是仔细一想这样还需要借助美工切图,在性能差不多的情况下尽量不依赖美工实现我觉得比较可取,所以我又采用了使用贝塞尔曲线定向切圆角的方法来实现.
总结
通过实现这一个ActionSheet,从刚开始想,到功能草草实现再到优化,基本上涵盖了我们自定义控件的一个流程,虽然这只是一个很简单的Demo,但是即使我们去开发自己的SDK,过程也是类似的,我们平时大部分情况下往往忽略了优化,其实优化这一步真的很重要,不单单是对你整个思路的一个整合提高,更是减少了我们后续改Bug的几率.
尾巴
之前在简书上看到了很多有关技术的文章,大家对技术文章的评论也都是褒贬不一,有的文章写的很好,大家都大肆赞扬,有的文章写的很初级,都觉得没有什么价值.这都对,但是我觉得出发点是好的,希望能将自己的心得与大家分享,也希望能听听大家的高见,最重要的是希望自己能进步,能与大家一起进步,从一个菜鸟变成大神,希望大家都能成为一名开发工程师而不是单单的做一名码农.
Demo链接:自定义一个好看的ActionSheet