论优雅的实现描边

最近使用了一下 IBAnimatable,这个库对 IBInspectable 的使用真让人叹为观止。突然意识到其实 IBInspectable 可以让我们减少很多不必要的重复代码。对一个 UIView 进行描边是我们在开发过程中比较常用的,UI 设计师们希望通过描边来增加界面的层次感。

常见的描边实现

通过 UIView 实现描一条线

在刚开始做 iOS 开发时,我很喜欢使用这样的方式。但后来发现这种实现用一下两个缺点:

  1. 后期如果 UI 需要重新调整,会变得比较痛苦。
  2. 一般描边都只需要1个像素的描边,在使用 XibStoryBoard 的时候需要设置 0.5 这样的值。XibStoryBoard 好像支持的不太好,经常会出现在 plus 上描边的线看起来特别粗。

通过 CALayer 绘制描边

这个方法实现描边的效果很不错。但还是有些缺点:

  1. 有些 UIView 其实在代码中不需要体现,但因为需要描边。我必须把它拖到代码中。
  2. 代码中到处出现,进行描边的代码,一定程度影响代码的美观和阅读性(PS: 我有洁癖)。

对这些实现进行封装的确是一个不错的选择。但我也许还有可能有更好的方案。

使用 IBInspectable

这里我只描述我的实现思路,不对 IBInspectable 进行介绍。毕竟 IBInspectable2014WWDC 的内容了(PS: 比我学习 iOS 还要早 = =),很多大神都有博客对其介绍。

思路如下:

  1. 可以使用 IBDesignable 将属性定义到 IB 中,通过定义属性,实现描边。
  2. 我们可以统一在 layoutSubviews 中进行描边处理。
  3. 我不希望为了实现描边还去继承一个类,所以我选择对 UIView 通过 Category 实现描边的功能。
  4. 我需要增加一下新的属性来定义描边,所以我需要使用 AssociatedObject 实现。对于 AssociatedObject 不了解的同学可以看南峰子的这篇博客 Objective-C Runtime 运行时之二:成员变量与属性
  5. 由于我希望通过 Category 实现描边,我还需要使用 Method Swizzling 实现。在每个 UIView 及其子类 在实现 layoutSubviews 去执行我的描边代码。不了解的同学可以看南峰子的这篇博客 Objective-C Runtime 运行时之四:Method Swizzling

最终实现效果

如图,只需要在 IB 上配置上属性,就可以实现描边的功能。

这样做还是有缺点的,IB 上的属性有点多。。。

对于代码感兴趣的同学可以到https://github.com/bay2/IBViewExt,我在这里就不贴代码了,代码不多看 UIView+IB.m 文件就可以了。

IBInspectable 的局限

  1. BDesignableIBInspectable 无法在 Cocoa Touch Frameworks 中生效,详细看这里,所以使用 Carthage 的时候,BDesignableIBInspectable 也无法正常工作, cocoapods 不会有次问题。
  2. BDesignable 无法再 categories/extensions 正常工作, stackoverflow 上也有提到这个问题