简体   繁体   中英

How to create IBDesignable custom view?

I am still improving my programing skills in iOS(Swift). Today I am thinking about creating a UIView subclasses in a good way. I want to create custom view which is:

  • @IBDesignable - I want to see a render of my view in storyboard/xib;
  • Creatable from code

I know such methods as init(withFrame) init() init(withDecoder) prepareForInterfacebuilder() , etc but I don't know how to use them to minimize redundancy of code.

For example here is my custom UIButton :

import UIKit

@IBDesignable
class SecurityButton: UIButton {

    @IBInspectable
    var color: UIColor = UIColor.black {
        didSet {
            setTitleColor(color, for: .normal)
            contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0)

            layer.borderWidth = 1.0
            layer.borderColor = color.cgColor
            layer.cornerRadius = 6.0
        }
    }
}

It works good but I know that contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0) layer.cornerRadius = 6.0 layer.borderWidth = 1.0 are called every I set a new color. Where should I place this custom setup to keep my requirements about custom view?

Edit

I do not looking for fix my example. I looking for a good place to initialize my custom view. Suppose that you need to do some time-consuming operations but only ones on create view.

You are missing the function

self.setNeedsDisplay()

Your code should look as follows

import UIKit

@IBDesignable
class SecurityButton: UIButton {

    @IBInspectable
    var color: UIColor = UIColor.black {
        didSet {
            setTitleColor(color, for: .normal)
            contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0)

            layer.borderWidth = 1.0
            layer.borderColor = color.cgColor
            layer.cornerRadius = 6.0

            self.setNeedsDisplay()
        }
    }
}

This will call the draw function that will update the storyboard view. The function should be called as last in the didSet Now it will show up after you set the color.

I tested it and It works for me in xcode, after I set the value color in the storyboard.

If you want it to draw always not only when u set a color by the inspector you can use the draw(rect) function it will look following

import UIKit

@IBDesignable
class SecurityButton: UIButton {

    @IBInspectable
    var color: UIColor = UIColor.black

    override func draw(_ rect: CGRect) {
        setTitleColor(color, for: .normal)
        contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0)

        layer.borderWidth = 1.0
        layer.borderColor = color.cgColor
        layer.cornerRadius = 6.0

        setNeedsDisplay()
    }
}

This will draw the border always, not only when you set in in the interface inspector.

You can create a "custom init" function to handle the setup tasks.

So, for example:

import UIKit

@IBDesignable
class SecurityButton: UIButton {

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        commonInit()
    }

    func commonInit() {

        layer.borderWidth = 1.0
        layer.borderColor = color.cgColor
        layer.cornerRadius = 6.0

        _contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 8.0)
        updateEdgeInsets()
    }

    private var _contentEdgeInsets: UIEdgeInsets = UIEdgeInsets.zero {
        didSet { updateEdgeInsets() }
    }
    override public var contentEdgeInsets: UIEdgeInsets {
        get { return super.contentEdgeInsets }
        set { _contentEdgeInsets = newValue }
    }

    private func updateEdgeInsets() {
        let initialEdgeInsets = contentEdgeInsets

        let t = _contentEdgeInsets.top + initialEdgeInsets.top
        let l = _contentEdgeInsets.left + initialEdgeInsets.left
        let b = _contentEdgeInsets.bottom + initialEdgeInsets.bottom
        let r = _contentEdgeInsets.right + initialEdgeInsets.right

        super.contentEdgeInsets = UIEdgeInsets(top: t, left: l, bottom: b, right: r)
    }

    @IBInspectable
    var color: UIColor = UIColor.black {
        didSet {
            setTitleColor(color, for: .normal)
        }
    }

}

Edit: Custom contentEdgeInsets have to be handled a little differently than most other properties.

@KamilHarasimowicz - Your "question edit" states "I do not looking for fix my example. I looking for a good place to initialize my custom view." ...

My initial answer did exactly that - it showed you where to initialize your custom view.

However, since your comment "storyboard don't render contentEdgeInsets in your solution" indicates that you actually DO want your example fixed (otherwise you would have just fixed it yourself), I have edited my answer to do so.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM