Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The frame size and 'required init(coder: NSCoder)' in a custom UIControl

Tags:

ios

swift

I have a custom UIControl, and I implement as:

required init(coder: NSCoder) {
    super.init(coder: coder)
    initSubComponents()
}

func initSubComponents() {
    // Display UIControl border for visual debugging
    self.layer.borderColor = UIColor.redColor().CGColor
    self.layer.borderWidth = 3.0

    subviewContainer = UIView(frame: self.bounds.rectByInsetting(dx: 0, dy: 0))
    // Display container border for visual debugging
    subviewContainer.layer.borderColor = UIColor.redColor().CGColor
    subviewContainer.layer.borderWidth = 3.0

    println("UIControl frame: \(self.frame)")
    println("subviewContainer frame: \(subviewContainer.frame)")
}

or

override func drawRect(rect: CGRect) {
    initSubComponents()
    // Code to add those subviews into this UIControl
}

func initSubComponents() {
    // Display UIControl border for visual debugging
    self.layer.borderColor = UIColor.redColor().CGColor
    self.layer.borderWidth = 3.0

    subviewContainer = UIView(frame: self.bounds.rectByInsetting(dx: 0, dy: 0))
    // Display container border for visual debugging
    subviewContainer.layer.borderColor = UIColor.redColor().CGColor
    subviewContainer.layer.borderWidth = 3.0

    println("UIControl frame: \(self.frame)")
    println("subviewContainer frame: \(subviewContainer.frame)")
}

I found a situation that I don't understand: the frame I got from the above 2 different approaches are different! Why? The first approach should be better, cause I should not init in override func drawRect(rect: CGRect), however I got the exact frame I expect in the second approach, not the first approach!

like image 366
Stephen Kuo Avatar asked Apr 17 '15 06:04

Stephen Kuo


1 Answers

This is happening because on init the control is getting it's frame from the storyboard/nib. The size of the view in the storyboard/nib can be different than the size on a device until it is laid out for the first time.

As people in the comments have stated drawRect is constantly being called, and so it has the correct frame because the control has already been laid out. But this is the wrong place to initialize sub components. Also as the default implementation states, unless you are actually drawing something in drawRect you shouldn't use it since it adversely affects performance.

There are a couple of solutions to you your problem I can think of right now:

  1. Initialize the sub components in the initWithCoder method with constraints and rely on autolayout updating everything when the control is laid out.
  2. Use a flag to initialize the sub components only once. Wait for the first layout in layoutSubviews and initialize from there.
like image 93
Nikola Lajic Avatar answered Oct 17 '22 01:10

Nikola Lajic