@IBDesignable view doesn't draw background color inside Interface Builder
Asked Answered
P

3

12

I'm curious why doesn't this set the backgroundColor to red?

@IBDesignable class CustomLabel: UIView {

  let view = UIView()

  func setup() {
    backgroundColor = UIColor.red
    view.backgroundColor = UIColor.green
    view.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
    addSubview(view)
  }

  override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
  }
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
  }
}

This is the result.

enter image description here

And this is what I expect it to look like.

enter image description here

Perfectly answered 6/9, 2016 at 10:53 Comment(0)
G
19

While rendering in Interface builder(IB), the properties which are set in IB of custom UI element are loaded after the call to init. Your code for setting the backgroundColor in init is getting called. But after that, it again sets the backgroundColor for the value of backgroundColor in IB.

I could not find the Apple documentation for this. I am saying this based on following analysis. I modified your code a little for debugging.

@IBDesignable class CustomLabel: UIView {

    let view = UIView()

    override var backgroundColor: UIColor? {
        didSet {
            print("here: "); // break point 1
        }
    }

    func setup() {
        self.backgroundColor = UIColor.redColor()  // break point 2
        view.backgroundColor = UIColor.greenColor()
        view.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
        addSubview(view)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)  // break point 3
        setup()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)  // break point 4
        setup()
    }
}

Now, put the breakpoints in all methods. Then, select the object of CustomLabel in Interface Builder, and choose Editor ➔ Debug Selected Views.

You can see the sequence of method calls. This just shows the sequence of rendering in interface builder, not the sequence it may follow in run time.

May be you know this, but for the sake of clarity, you can use following to reflect this in IB.

override func prepareForInterfaceBuilder() {
    super.prepareForInterfaceBuilder()
    backgroundColor = UIColor.grayColor()

}
Gerrygerrymander answered 7/9, 2016 at 5:13 Comment(0)
J
1

The logic of init(frame:) and init?(coder:) results in two different renderings in InterfaceBuilder rendering mode because IBDesignablesAgent-iOS which is used to render IBDesignable classes calls init(frame:) and after that it uses "User Defined Run Time Attributes" default / custom values to configure the custom view. So, the backgroundColor set in init(frame:) will be changed by default/custom value of background color set in Interface builder. In case of run time rendering when app runs on a device or simulator, the init?(coder:) method is used and "setup()" logic is applied after default / custom values from storyboard were applied. In case of creating a custom view from code then init(frame:) method is used but there is no decoding from "User Defined Run Time Attributes" of storyboard because this one does't exists.

Jessie answered 11/2, 2022 at 14:27 Comment(0)
G
1

Rendering behaves slightly differently in Interface Builder and runtime. IBDesignablesAgent-iOS, which is used to render IBDesignable classes in Interface Builder, calls init(frame:), and after that it uses "User Defined Run Time Attributes" default / custom values to configure the custom view. This means the background color set in init(frame:) will be changed by the default / custom value of background color set in Interface Builder, and the latter is what you see rendered in Interface Builder.

This is different than the rendering logic that happens during runtime (when the app runs either on a device or simulator). Here the default / custom values set in Interface Builder are applied after the init?(coder:) method is invoked. This means the background color set in init?(coder:) is what you see rendered on your device.

(Credit to @Blazej)

Gamages answered 23/12, 2022 at 18:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.