Chris
Chris

Reputation: 312

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

Upvotes: 0

Views: 1312

Answers (5)

DonMag
DonMag

Reputation: 77578

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.

Upvotes: 1

khush
khush

Reputation: 638

You can use Dispatch Queue for this:

      DispatchQueue.main.async {
          let topDistance = view.frame.minY - ALabel.frame.maxY
          let bottomDistance = BLabel.frame.minY - view.frame.maxY
          let total = topDistance + bottomDistance
          bottomConstraint.constant = total / 2
         self.view.layoutIfNeeded()
    }

Upvotes: 1

Mathias
Mathias

Reputation: 566

When I want to set constraints programmatically I usually proceed like this :

let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
v.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true
v.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true
v.heightAnchor.constraint(equalToConstant: 100).isActive = true
v.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true

Upvotes: 1

Bozzo
Bozzo

Reputation: 338

When you use View did appear is already when user see the VeiwController, Use viewDidLoad instead , u can't use viewWillAppear , because View isn't loaded yet.

Use it on:

override func viewDidLoad() {
    super.viewDidLoad()
}

And u can try call

self.view.layoutIfNeeded()

Upvotes: 1

AtulParmar
AtulParmar

Reputation: 4570

Write layoutIfNeeded after update constraint.

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

Upvotes: 1

Related Questions