iOS 自定义View

在日常开发中,一个App会有很多模块中的小模块相差不多。这时候我们就需要考虑公用 -- 自定义View , 这样就可以避免很多重复的代码。

有哪些需要考虑的

  • 首先要考虑不同的调用,纯代码的方式或者Xib 、Storyboard的方式都要可以使用
  • 要有一定的可定制化 ,也就是暴露出来的属性
  • 使用起来简单

实例

例如有这样的 View 在不同的地方有用到。

iOS 自定义View_第1张图片
截图

很简单的一个视图,图片加文字加边框。

首先,新建一个UIView的子类ImageTextView , 在class前面添加open关键字 (我们要做成公用的)

删除drawRect相关方法,将下面代码复制进去

 public override init(frame: CGRect) {
    super.init(frame: frame)
    #if !TARGET_INTERFACE_BUILDER  // 非interfabuilder环境下
    // 如果是从代码层面开始使用Autolayout,需要对使用的View的translatesAutoresizingMaskIntoConstraints的属性设置为false
      translatesAutoresizingMaskIntoConstraints = false
    #endif
    prepareView()
  }
  
  required public init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    prepareView()
  }
  
  // 布局
  func prepareView(){

  }

init?(coder:) 是从Xib或者Storyboard创建的时候会调用。 其他看注释咯 - _ -

UIImageView添加一个扩展,方便创建UIImageView对象

extension UIImageView {
  class func configuredImageView() -> UIImageView {
    let imageView = UIImageView()
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.clipsToBounds = true
    imageView.contentMode = .scaleAspectFill
    return imageView
  } 
}

回到ImageTextView中声明图片和文本的变量

fileprivate let imageView = UIImageView.configuredImageView()  
fileprivate lazy var textLabel = UILabel()

prepareView中对图片和文本进行布局

// 布局
  func prepareView(){
    textLabel.translatesAutoresizingMaskIntoConstraints = false
    textLabel.font = UIFont.systemFont(ofSize: CGFloat(textSize))
    
    addSubview(imageView)
    addSubview(textLabel)
    imageView.setContentCompressionResistancePriority(1000, for: .horizontal )
    NSLayoutConstraint.activate([
        imageView.leftAnchor.constraint(equalTo: leftAnchor, constant:10 ) ,
        imageView.centerYAnchor.constraint(equalTo: centerYAnchor) ,
        textLabel.leftAnchor.constraint(equalTo: imageView.rightAnchor , constant:2 ) ,
        textLabel.centerYAnchor.constraint(equalTo: centerYAnchor) ,
        textLabel.rightAnchor.constraint(equalTo: rightAnchor, constant:-10)
      ])
    
    layer.borderColor = UIColor.lightGray.cgColor
    layer.borderWidth = 0.5
    layer.cornerRadius = 3
  }

这里的布局方法是iOS 9 开始支持的API , 当然你也可以使用SnapKit

为了使我们的View可以在 InterfaceBuilder 中使用,在类前面使用注解 @IBDesignable
并暴露出可以自定义的属性

extension ImageTextView{
  
  @IBInspectable
  open var image:UIImage?{
    get{
      return imageView.image
    }
    set{
      imageView.image = newValue
    }
  }
  
  @IBInspectable
  open var text:String?{
    get{
      return textLabel.text
    }
    set{
      textLabel.text = newValue ?? ""
    }
  }
  
  @IBInspectable
  open var textColor:UIColor? {
    get{
      return textLabel.textColor
    }
    set{
      textLabel.textColor = newValue
    }
  }
}

在变量声明下面在加上字体大小

//  这里使用 textSize 是因为 IBInspectable 暂时还不支持 UIFont
  @IBInspectable
  open var textSize:UInt = 17 {
    didSet{
      textLabel.font = UIFont.systemFont(ofSize: CGFloat(textSize))
    }
  }

这块由于暂时不支持UIFont所以使用了UInt

我们还可以在InterfaceBuilder渲染前做一些事情

 override open func prepareForInterfaceBuilder() {
    super.prepareForInterfaceBuilder()
    // interface builder 渲染前执行  不会影响真正运行效果
    layer.borderColor = UIColor.lightGray.cgColor
    layer.borderWidth = 0.5
    layer.cornerRadius = 3
  }

到这里,这个自定义View就算完成了,看下载Storyboard中的使用吧

随便拖一个UIView
修改Class属性

iOS 自定义View_第2张图片
截图

保存后就可以看到这些属性了

iOS 自定义View_第3张图片
截图

设置完成后 InterfaceBuilder 立刻会渲染出来

截图

这里是我随便设了几个属性

iOS 自定义View_第4张图片
截图

有了这个自定义的View,那么在任何地方有相似效果,我们只需要像拖UILabel一样把他拖出来简单的设置下属性就好了(也可以通过手写代码创建)。当然,这只是一个简单的demo,并没有涉及到事件Layer以及动画等,如果有兴趣下次再讲。

你可能感兴趣的