1

My view controller possesses a stack view with 3 buttons. One fixed in the center with a fixed width and the stackview set to fill proportionally such that the other two buttons will be symmetrical.

However I am also customizing the corner radius of the buttons and as soon as the application loads the button resizes in an undesired fashion.

Ive attempted numerous stackview distribution and fill settings. Removing the buttons from the stackview and simply trying to contraint them to edges on a normal UIView to no avail as it seems, but uncertain if, the constraints get deleted.

Visually the button will be located at the bottom right hand corner of the screen with 0 space between the edge and the button. Currently it gets laid out in a manner where there is no constraint it seems on multiple devices causing it to have a space on larger displays, and exit the screen on small displays within the simulator.

Attempted coding efforts to round the desired corners:

@IBOutlet fileprivate weak var button: UIButton! { didSet {
    button.round(corners: [.topRight, .bottomLeft], radius: 50 , borderColor: UIColor.blue, borderWidth: 5.0)
    button.setNeedsUpdateConstraints()
    button.setNeedsLayout()
} }

override func viewDidLayoutSubviews() {
   button.layoutIfNeeded()
}

ovverride func updateViewContraints() {
    button.updateConstraintsIfNeeded()
    super.updateViewConstraints()
}

The UIView extension that is being used can be found here: https://stackoverflow.com/a/35621736/5434541

What solution is available to properly adjust the buttons corner radius' and allow the constraints to update the button to as it should be.

Community
  • 1
  • 1
lifewithelliott
  • 1,012
  • 2
  • 16
  • 35
  • Do you get the same behavior with both methods? – Ben Jan 18 '17 at 21:23
  • @Ben Yes, I get the same behavior with all methods attempted. Currently this is occurring in the simulator, tested on an iPhone 5 it seems to run as I would like it to, but the unexpected behavior for something like this leaves me worried not knowing if it could occur on others devices. Also as soon as I implemented these methods the application currently lags a lot! I know it is not relevant to the specific question but am curious if the viewDidlayout subviews should be executed on another thread? Thanks! – lifewithelliott Jan 19 '17 at 03:41

1 Answers1

0

Without seeing your full code, I suspect you're getting this because it's drawing multiple layers with the border, and never removing any! If the buttons get resized, the old layers are still there. Here's a snip that shows removing the old layer on redraw that you should be able to adapt. I tested this inside a stack view, and it also behaves correctly on rotation:

class RoundedCorner: UIButton {

    var lastBorderLayer:CAShapeLayer?
    override func layoutSubviews() { setup() }

    func setup() {
        let r = self.bounds.size.height / 2
        let path = UIBezierPath(roundedRect: self.bounds,
                                byRoundingCorners: [.topLeft, .bottomRight],
                                cornerRadii: CGSize(width: r, height: r))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        addBorder(mask: mask, borderColor: UIColor.blue, borderWidth: 5)
        self.layer.mask = mask
    }

    func addBorder(mask: CAShapeLayer, borderColor: UIColor, borderWidth: CGFloat) {
        let borderLayer = CAShapeLayer()
        borderLayer.path = mask.path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.lineWidth = borderWidth
        borderLayer.frame = bounds
        if let last = self.lastBorderLayer, let index = layer.sublayers?.index(of: last) {
            layer.sublayers?.remove(at: index)
        }
        layer.addSublayer(borderLayer)
        self.lastBorderLayer = borderLayer
    }
}

You also noted in your comment about the app lag. I'm not surprised, since layout can get called many times per update, and you're creating a new layer every single time. You can avoid this by saving the last dimensions of the botton frame and comparing. If it's identical, don't recreate the layer.

Ben
  • 1,117
  • 13
  • 21
  • So i was going about implementing your RoundedCorner Class but i felt after reading your code that maybe I was doing something wrong on my end in the original question. So I tried to research a bit more and edited the question above to update the findings. I appreciate the help greatly as your answer is leading me in the right direction. – lifewithelliott Jan 19 '17 at 20:28
  • Not sure what you edited, can you share the whole button class? – Ben Jan 19 '17 at 21:20
  • the button is just a storyboard button connected to the view controller. No custom subclassed button created for it. The application runs and as soon as the button is set then the didSet causes the `round() ` method to get called then following `setNeeds()` methods tells the controller to update the layout when the `viewDidUpdateLayoutSubviews()` is called. I believe thats how its going. – lifewithelliott Jan 19 '17 at 22:01
  • That method will still have the original issue I pointed out. Each time the button is redrawn, you're adding another sublayer. You must clear the old layer first (as my example is doing with layer.sublayers?.remove). This is why you see multiple rounded corners. – Ben Jan 20 '17 at 03:05