Frame size and "required init (encoder: NSCoder)" in a custom UIControl

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 do not understand: the frame that I received from the above two different approaches is different! What for? The first approach should be better, because I should not initialize in override func drawRect(rect: CGRect) , however I got the exact frame that I expect in the second approach, not the first approach!

+5
source share
2 answers

This is because, upon initialization, the control gets the frame from the / nib storyboard. The size of the presentation in the storyboard / nib may differ from the size on the device until it is laid out for the first time.

As people noted in the comments, drawRect constantly being called, and therefore it has the correct frame, because the control is already laid out. But this is the wrong place to initialize subcomponents. In addition, since the standard implementation states that if you are not actually drawing something in drawRect , you should not use it, as this adversely affects performance.

There are several solutions to your problem that I can think of now:

  • Initialize subcomponents in the initWithCoder with restrictions and rely on automatic updates to update everything when the control is laid out.
  • Use the flag to initialize auxiliary components only once. Wait until the first layout in layoutSubviews is initialized from there.
+2
source

A good solution would be to add auto-layout constraints to your subviewContainer , since the constraints are automatically updated every time self.frame changes.

Your code will look like this:

 required init(coder: NSCoder) { super.init(coder: coder) initSubComponents() } func initSubComponents() { // Original code, I removed debugging code for easier reading. subviewContainer = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) //Where the magic happens: subviewContainer.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint(item: subviewContainer, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0).isActive = true NSLayoutConstraint(item: subviewContainer, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1.0, constant: 0).isActive = true NSLayoutConstraint(item: subviewContainer, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0).isActive = true NSLayoutConstraint(item: subviewContainer, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0).isActive = true } 

There are several ways to add auto-placement restrictions, click to find out here .

This is a bit more code, but overall a much better way to deal with this problem, as there are situations where drawRect will not be saved.

0
source

All Articles