如何创建iOS可视化控件

以前写过一个文章《如何用Swift创建自定义iOS控件》,文章主要以Swift语言讲解,图个新鲜,其原理跟使用OC语言是一致的。这次再写一个进阶的文章,重点讲一下如何制作可以和Xcode IDE交互的控件,以及如何与AutoLayout系统协作。

我们以一个自定义按钮为出发点讲解,创建方法和《如何用Swift创建自定义iOS控件》 类似,也是基于Nib来创建我们的控件。Demo项目在Github上这里, Demo还是有一定使用价值的,虽然还有很多需要完善的地方。

使用效果图如下:

效果图
效果图

因为是进阶教程,就不再一步一步手把手教了,重点阐述一些疑惑和踩坑的地方。

  1. 自定义控件如何与Xcode交互?

    • 属性前用IBInspectable修饰(Swift的话是@IBInspectable)
    • 类声明用IB_DESIGNABLE修饰(Swift的话是用@IBDesignable)
  2. -initWithFrame:, -initWithCoder:以及-awakeFromNib分别在什么时候调用。

  • -initWithFrame:是使用代码创建控件的时候调用,-init:方法初始化也会调用,相当于调用[self initWithFrame:CGZeroRect]

  • -initWithCoder:是在Nib加载的时候调用的,其中包括在独立的Xib文件,或者Storyboard文件,或者通过UINib类手写代码加载。

  • -awakeFromNib是Nib加载完毕,并且设置好所有的属性之后调用。

因此,你看代码就会看到在这三个方法里分别做三个不同的事情。

-initWithFrame:需要做完所有的工作,包括创建视图createViews,初始化默认值setupDefaults(吐槽一下OC的属性不能设置初始值,Swift就舒服很多很多),设置视图表现setupViews。

-initWithCoder:则需要调用createViews和setupDefaults,如果在这里尝试做setupViews的话,那么那些设计过程中修改了的属性值就不会生效了,因为这时候的属性值都还没有初始化。

因此,只有在-awakeFromNib才做setupViews的工作。

  1. Xcode IDE怎么更新自定义控件的视图表现的呢?

    最开始,我刚写demo的时候用的办法是为每个IBInspectable属性编写自定义set方法,每次设置属性就重新执行一下setupViews方法。这个做法在设计阶段不考虑执行效率的话还行。像我们的自定义按钮,有10多个自定义属性,如果运行时候每次设置属性都执行一次setupViews,这样的效率肯定是不能接受的。那么, 什么才是正道呢?答案是-prepareForInterfaceBuilder方法。

    Xcode需要更新设计UI的时候就会调用-prepareForInterfaceBuilder方法,并且,这个方法在运行时是不会被调用的,完美!另外,一个特别的宏TARGET_INTERFACE_BUILDER,这个宏只有在IDE执行代码的时候才会被定义,利用这个宏,可以做一些针对设计阶段执行的代码,例如本例就针对设计初始化一个Title的默认值。

  2. 如何与AutoLayout系统对接?

    我们自定义控件内部的子视图是在自己的Nib文件中做了AutoLayout的约束的。但是,自定义按钮作为一个整体被外部时候的时候,外部也会有AutoLayout去约束它的大小和位置。这时候,AutoLayout系统需要知道一个重要的信息:你的控件内容有多大?

    当AutoLayout需要询问控件的内容大小的时候,它会调用-intrinsicContentSize,我们的自定义控件根据各个子视图(都是一些UILabel和UIImageView)的内容大小和间隔信息返回一个值就行了。注意:这个值必须是不依赖frame的。

    想想UILabel和UIButton的行为你大概就会明白这点了,这些控件你只需要定义位置约束,而不需要定义视图大小约束就能完成约束了。当然,你也可以强制定义视图大小约束,这时候就有内容大小和视图大小约束优先级的问题,需要结合Hugging和Compression Resistance的优先级来确定最终内容大小和视图大小了。

  3. 布局完成之后的位置调整

最初版本的Demo有个BUG,如果Title左右位置的图标大小如果一样,那么整体内容并没有居中。出现这个BUG的原因是,我们把Title约束在整个内容视图的中间。当左右两个icon大小一致,或者Title左右的Span值不相同的情况下,需要把Title的中心位置偏移一下才能正确的表现整体内容居中的效果来。那么问题了,在哪里调整才合适呢?(怎么调整就只是算法问题,看代码就能明白)。答案是-layoutSubviews 这个方法会在AutoLayout完成视图布局之后调用,正式我们需要调整便宜的地方,因为,只有在视图布局完成之后,我们才能知道各个子视图正确的Frame大小。

  1. layoutSubviews里再修改约束不会导致无限循环吗?

    这个问题,我没有找到答案,只有调试能说明,答案是:不会?至于原理,没有找到合适的官方文档说明。有读者知道的,请不吝赐教。

  2. 其他的一些小细节

    IconFont看上去真是个挺不错的东西,资源小,矢量拉伸,可更改颜色,图标资源丰富。再结合一个制作工具,把自己项目用到的图标单独抽取出来,只带一个小字体文件就能搞定APP的所有图标,非常的赞。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容