原文链接: How To Make a Custom Control Tutorial: A Reusable Slider
前几天去流利说现场听了 Saul Mora 的一场分享会,讲的就是 custom control,看似简单的自定义控件,却有很多人写不好(其实我也写的不咋地🙄🤐),昨天练习 Swift 时正好碰到 Ray Wenderlich 出品的这篇好文章,跟着敲了一遍,顺便写了这篇总结,正好可以复习一下 Swift 和 UIControl 、CoreGraphics 的知识。
目录
- 学习笔记
- 问题与收获
- 练习代码
- 延伸阅读
学习笔记
-
Choose a existing class for custom control to subclass or extend: You’ll be using the target-action pattern in this custom control, so
UIControl
will serve as a great starting point. -
Before you write any code for your control, you should add your control to the view controller so that you can watch the evolution of the control.
-
API Design: Before you add the visual elements to your control, you’ll need a few properties to keep track of the various pieces of information that are stored in your control.
-
Well-designed controls should define some default property values.
-
Images vs. CoreGraphics
5.1 Images: Simple to use, but it’s difficult to modify the control from code.
5.2 Core Graphics: More flexible, but you have to write the rendering code yourself.
5.3 Apple tend to opt for using images in their controls.(You could see thatUISlider
uses images to configure appearance from API and by debugging view hierarchy.) -
Adding Interactive Logic: store which thumb is being dragged, and reflect that in the UI.
-
Adding Touch Handlers: add the ability for the user to drag the slider thumbs around.
7.1UIControl
provides several methods for tracking touches, so we can override these methods to add interaction logic.
7.2beginTrackingWithTouch(:with:)
7.3continueTrackingWithTouch(:with:)
7.4endTrackingWithTouch(:with:)
-
Change Notifications
-
NSNotification,** Key-Value-Observing (KVO): If you look at the UIKit controls, you’ll find they don’t use NSNotification or encourage the use of KVO, so for consistency with UIKit you can exclude those two options.
- the delegate pattern**:- A protocol can contain a number of methods.
- A delegate method can take any number of parameters.
- A control only accept a single delegate instance.
-
the target-action pattern:
- The target-action pattern is provided by the UIControl base class.
- You can provide multiple targets to control actions.
- while it is possible to create custom events (see UIControlEventApplicationReserved) the number of custom events is limited to 4.
- Control actions do not have the ability to send any information with the event.
- The key differences between the above two patterns are as follows:
- Multicast:
one-to-one
vs.one-to-many
- Flexibility:
can pass extra information
vs.cannot pass extra information directly
- Multicast:
-
NSNotification,** Key-Value-Observing (KVO): If you look at the UIKit controls, you’ll find they don’t use NSNotification or encourage the use of KVO, so for consistency with UIKit you can exclude those two options.
-
Modifying Your Control With Core Graphics: override
draw(in:)
method -
Handling Changes to Control Properties: implement property observers that update the control’s frame or drawing.
-
Test:
When you are developing a custom control, it’s your responsibility to exercise all of its properties and visually verify the results.
A good way to approach this is to create a visual test harness with various buttons and sliders, each of which connected to a different property of the control.
That way you can modify the properties of your custom control in real time — and see the results in real time. -
Where To Go From Here?
-
Documentation: A good practice is to provide public API documentation, at a minimum, for all publicly shared code. This means documenting all public classes and properties.
-
Robustness: You need to ensure that the control state always remains valid — despite what some silly coder tries to do to it.
-
API Design: Creating a flexible, intuitive and robust API will ensure that your control can be widely used, as well as wildly popular. See Matt Gemmell’s 25 rules of API design.
-
Sharing: GitHub, CocoaPods, Cocoa Controls
-
问题与收获
1.平时自己写的自定义 UI 控件一般都是继承 UIView
,这个教程让我见识了 UIControl
的价值,还是得多看看官方的 API Reference 和 Guides 。
2.一个功能、一个控件、一个框架的实现都不是一蹴而就的,这是一个不断进化、完善的过程。
3.要想受欢迎,一个框架/库的 API 一定要设计好,使用简单、功能齐全、文档清晰,推荐阅读 Matt Gemmell’s 25 rules of API design。
4.实现一个控件的外观一般有两种方式:
- 图片:开发简单,只需要 UI 设计好图片资源直接替换就行了,缺点在于不方便开发人员自定义,缺乏灵活度。
- 用系统的库(CoreGraphics、 OpenGL ES)来绘制:完全由开发人员的代码来控制,灵活度高,方便自定义,缺点在于需要耗费开发资源,也就是说要写很多代码。
5.写代码前需要想清楚要做哪几件事,比如写好一个自定义 control,就需要考虑:要继承什么类、API 怎么设计、外观展示怎么实现(subviews 和 sublayers、images 和 CoreGraphics)、交互逻辑怎么处理(touch 事件)、事件和数据怎么传递(target-action等)。
6.事件传递的几种方式:
- delegate:一般情况下只支持一对一(当然你也可以采用一些特殊方式来解决),优点在于,方法数和参数值不限,定义清晰,内存管理方面相比 block 也有很多优势,缺点是代码比量较多,结构松散。
- notification 和 KVO:支持多对多,可以传值,APP 内任何地方都可以监听、发送通知,但是滥用的话很容易出现难以追踪的 bug,而且性能上会比 delegate 低(因为在 Objective-C 的运行时会动态生成类)。
- target-action:一般用于 control 类的控件,支持一对多,但是传值不太方便,一般需要通过访问
sender
参数才能间接同步到数据。 - block:代码结构紧凑,支持一对多(搞一个 blocks 数组),但是用的不好容易出现内存泄漏。
7.自测:一个好的程序应该是经过广泛覆盖的测试的,写程序总会有 bug 的,谁也无法保证他自己写过的代码永远不会出 bug,所以写完代码后减低 bug 出现率的最佳方式是 realtime test。
8.鲁棒性:优秀的程序员一定不能不考虑代码的健壮性,各种边界情况以及框架使用者的错误使用方式都要考虑,比如需要用断言提前判断是否传入空值,预防使用者设置最大值比最小值还小的情况。
9.文档:代码除了写了给机器运行之外,还要给人去读的,对于那些开源软件来说,需要更加注意这点,好的文档就如同产品说明书一般,使用者用起来也方便,YYKit 的代码就是最佳典范。
10.分享:把有价值的代码分享出去,既能帮助别人快速开发,又能提供给别人参考学习,除此之外,还能得到不同人的意见和建议、反馈(issue、pull request)。还有一点就是被人 star 的满足感。
11.更多 Swift 相关的问题见练习代码中的 // TODO:
标记。
练习代码
延伸阅读
- How To Make a Custom Control Tutorial: A Reusable Slider
- Custom Controls
- API Reference-UIControl
- Target-Action
- Matt Gemmell’s 25 rules of API design
- Drawing and Printing Guide for iOS
如果你也喜欢交流技术、喜欢阅读、积极践行,欢迎关注我的公众号:祥龙Shannon写字的地方,一起成长。
qrcode_for_gh_cc686217be41_344.jpg
网友评论