Search code examples
iosswiftautolayoutnslayoutconstraint

Setting Constraints in viewWillAppear


I have a uiview in the center of the viewcontroller. There is a uilabel above (A) and below (B) this uiview.

I want to programmatically change the bottom constraint of this uiview so that the distance above and below the uiview are equal to each other as screen size changes. So calculating the distance between top uiview to bottom of uilabel (A) and distance between bottom uiview to top of uilabel (B), dividing by 2 and then setting the bottom constraint constant accordingly.

I have the calculation done, but it only seems to work when i place it in viewDidAppear and i need the constraints and views to be in their right place before the user is able to see the objects. Is this possible to do in viewWillAppear. it feels like all the elements are not set until viewDidAppear for me to calculate correctly..i believe it has to do with the lifecycle of autolayout not completing prior to the calculation..

let topDistance = view.frame.minY - ALabel.frame.maxY
    let bottomDistance = BLabel.frame.minY - view.frame.maxY
    let total = topDistance + bottomDistance
    bottomConstraint.constant = total / 2

Below is a before/after example of the desired result where the purple uiview has a bottom constraint to the bottom orange uilabel. when the screen size increases in length, as an example, i want the bottom constraint to have the same constant as the distance between the purple uiview and top orange uilabel. The top uilabel has a top constraint to the top and the bottom uilabel has a bottom constraint to the bottom. Example


Solution

  • To try and answer your question:

    You're right, that auto-layout has not finished doing its work in viewDidLoad() or even viewWillAppear(). Generally, when you need to do layout adjustments like that, you want to do it in viewDidLayoutSubviews(). From Apple's docs:

    viewDidLayoutSubviews

    When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method.

    However, if I understand what you're actually trying to do...

    By using a UIStackView you can avoid needing to use any code.

    Embed your labels, views, buttons, etc in a Vertical UIStackView. Set the properties to:

    Alignment: Fill   (or Leading or Center as suits your layout)
    Distribution: Equal Spacing
    Spacing: 0
    

    Constrain the stack view's Top to the Top of the view (plus any desired padding) and the Bottom to the Bottom of the view (plus any desired padding).

    Result with iPhone SE size:

    enter image description here

    Result with iPhone 8 size:

    enter image description here

    Result with iPhone XS size:

    enter image description here

    And, here's iPhone 8 with a couple additional elements of varying heights:

    enter image description here

    As you see, the elements have equal vertical spacing between them, and it's all handled by the stack view.