Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to make a custom control #45

Open
ShannonChenCHN opened this issue Apr 27, 2017 · 3 comments
Open

How to make a custom control #45

ShannonChenCHN opened this issue Apr 27, 2017 · 3 comments

Comments

@ShannonChenCHN
Copy link
Owner

ShannonChenCHN commented Apr 27, 2017

  • UIControl 是什么,能用来干嘛?
  • 为什么选择 UIControl 来继承?
  • 自定义一个 control 做哪些工作?
  • 如何通过 UIControl 监听 touch 事件?
  • UIControl 的 Target-Action 机制有什么优缺点?
  • 如何自定义 UIControl 的外观?
    • Image vs. CoreGraphics
    • UIView vs. CALayer
@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Apr 29, 2017

How To Make a Custom Control Tutorial: A Reusable Slider 的学习总结

目录

  • 学习笔记
  • 问题与收获

学习笔记

  1. 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.

  2. 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.

  3. 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.

  4. Well-designed controls should define some default property values.

  5. 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 that UISlider uses images to configure appearance from API and by debugging view hierarchy.)

  6. Adding Interactive Logic: store which thumb is being dragged, and reflect that in the UI.

  7. Adding Touch Handlers: add the ability for the user to drag the slider thumbs around.
    7.1 UIControl provides several methods for tracking touches, so we can override these methods to add interaction logic.
    7.2 beginTrackingWithTouch(:with:)
    7.3 continueTrackingWithTouch(:with:)
    7.4 endTrackingWithTouch(:with:)

  8. 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
  9. Modifying Your Control With Core Graphics: override draw(in:) method

  10. Handling Changes to Control Properties: implement property observers that update the control’s frame or drawing.

  11. 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.

  12. 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](http://www.mattgemmell.com/api-design/)*.

- **Sharing**: GitHub, CocoaPods, [Cocoa Controls](https://www.cocoacontrols.com)

问题与收获

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:标记。

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Apr 30, 2017

Custom Controls 的学习总结

学习笔记

1. Overview of the View Hierarchy

  • UIResponder
    • What's a responder's responsibility
    • Why UIResponder is a separate class, and not merged into UIView?
    • First responder and responder chain
    • Customize the input methods
  • UIView
    • What's a view's responsibility
    • A common misconception is that this area is defined by the view’s frame
    • Add gesture recognizers to views
  • UIControl
    • Building on views, the UIControl class adds more support for interactivity.
    • It has the target/action pattern
    • Some scenarios of not using custom controls

2. Rendering(TODO: What, Why, How)

  • Custom Rendering
    • You probably want to avoid doing rendering on the CPU, but instead offload it to the GPU. There is one rule of thumb to achieve this: try to avoid drawRect:, and instead compose your custom views out of existing views. See Getting Pixels onto the Screen
    • Often, the quickest way to render something is just by using image views. Most of what you can achieve with view's layer property will be faster than drawing your own things using Core Graphics.
    • Nonetheless, as always, it is important to profile your code.
    • By using stretchable images together with your image views, you can also greatly improve performance. See Taming UIButton, a comment by Andy Matuschak
    • If you are processing images, you can also often get away with letting the GPU do that for you by using Core Image or OpenGL, instead of doing it with Core Graphics.
  • Custom Drawing
    • Generate an image, and then cache that, either on disk or in memory
    • If your content is very dynamic, you can maybe use Core Animation
    • if it doesn’t work, go for Core Graphics
    • If you really want to get close to the metal, it is not that hard to use GLKit and raw OpenGL, but it does require a lot of work
    • If you do choose to override drawRect:, make sure to take a look at content modes

3. Custom Interaction:communication strategies between views and their owners

  • Overview

    • By subclassing UIControl, you can fire events using the target action mechanism
    • To respond to touches, you almost always want to use gesture recognizers
    • if you want to go low-level, you can still override the methods touchesBegan, touchesMoved, and touchesEnded to get access to the raw touches
    • To separate the gesture handling from your custom view or view controller, it is almost always more appropriate to create a gesture recognizer subclass
    • Communicating back the value to the classes that own them:
      • target-action
      • delegates
      • blocks
      • key-value observing
      • notifications.
  • Use Target-Action

    • Advantages: You only need to do very little in your custom view subclass, and you automatically get support for multiple targets.
  • Use Delegates

    • Advantages: You can do more complicated things than just letting the owner know that the value changed
    • Drawbacks:
      • You might need to check if your delegate implements the method you want to call
      • You can typically only have one delegate (or you need to create an array of delegates)
  • Use Blocks

    • Advantages: You can group related code together in the view controller
    • Drawbacks:
      • It is important to check if the block is set, because calling a block that is not set will crash
      • You typically have only one block per action
      • Be aware of the possibility of leading to retain cycle
    • Once the block bodies get out of hand, you will also probably extract them to methods of their own, and then you might as well have used delegates
  • Use KVO

    • Use this for observing
    • It’s a nice pattern to decouple things
    • You also need to remove the observer when you don't need it anymore.
    • Observing multiple children from the same object quickly gets messy
  • Use Notifications

    • Advantages: quite decoupled, a very loose coupling
    • Drawbacks: lose type safety, you get a notification object in your callback, and the compiler can’t check if the types between the notification sender and the notification receiver match

4. Accessibility

  • The standard iOS controls provided by Apple are all accessible.
  • For detail, see Accessibility Programming Guide explains how to make the controls accessible
  • If you have a view that has multiple elements inside it that should be accessible, but are not subviews, then you can implement the UIAccessibilityContainer protocol for your view

5. Localization

  • The most straightforward thing to localize in your custom views is string contents
  • Another really helpful tool in localization is Auto Layout, using Auto Layout to handling the label's size instead of hardcoding.
  • For detail, see Internationalization and Localization Guide

6. Testing

问题与收获

1.UIResponder 与 UIResponder Chain #51
2.touch 事件的处理,自定义 gesture recognizer #52
3.屏幕渲染原理和绘制,渲染视图时影响性能的主要因素是什么 #48
4.再次回顾了几种不同的事件回调方式的对比
5.有空研究一下 ReactiveCocoa #22
6.相比第一篇文章,这篇文章不是教程,更偏向于理论,理解起来不太直观,但是其中谈到了很多技术点,很值得研究
7.单元测试、UI 自动化测试基本上很少用,不知道国内有多少团队用了 #24

@ShannonChenCHN ShannonChenCHN changed the title 【问题】How to make a custom control How to make a custom control Jul 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant