clearlight
clearlight

Reputation: 12625

How can I get UIView's (x, y) when using AutoLayout if initial frame was misconfigured?

Perhaps it is a misuse of a UIView's frame property by my setup code, for auto laid-out views, wherein, because I assume the view's initial frame property values won't matter once AutoLayout constraints are added, that I can use the frame.origin to cache values that aren't really the view's origin in coordinate space.

What I mean by "misusing" the frame property is: I am setting the frame.origin to values to be parameter values in my constraint setup function calls, because I like like the succinctness and convenience for tweaking constraint offsets by the CGRect, in my code. But perhaps it's backfiring? Not sure why though...

For example:

let view = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
NSLayoutConstraint.activate([
    view.leadingAnchor.constraint(equalTo: leftSideView.trailingAnchor, 
                                  constant: view.frame.origin.x)
    .
    .
    .
])

Initially it seems fine, views are placed as expected on the screen, but after views have been laid-out by AutoLayout, the frame.origin seems to reflect the original misappropriation by me... E.g. after AutoLayout frame.origin is still (10, 10)... anyway after AutoLayout, frame.origin are not the correct absolute values in the coordinate system one generally refers to the frame property to get.

After I do this:

 view.setNeedLayout()
 view.layoutIfNeeded()

When I print the frame.origin values, they aren't the view's actual position.

Why wouldn't they be? So, how can I find the right values?

Perhaps I need to call one of UIView's superview coordinate translation functions?

UPDATE: The view that is not having its origin set is actually the leftSideView, which I configured using the same approach as the view I'm constraining. I did run setNeedsLayout()/layout, but now I'm thinking that AutoLayout doesn't have enough data yet to calculate the frame so I have a chicken-egg problem where I'm trying to add constraints based on constraints that are unresolved yet, which won't work.

Upvotes: 1

Views: 466

Answers (1)

Sweeper
Sweeper

Reputation: 274460

You are asking the wrong view to layout. There is nothing much for view to layout, other than its own size. It doesn't know how to lay itself out relative to leftSideView, because leftSideView is not one of its subviews.

To layout view relative to leftSideView, find their common parent, and ask it to layout. Suppose view.superview is the common parent, do:

view.superview!.layoutIfNeeded()

Then view.frame will be the values you'd expect.

Alternatively, you can override viewDidLayoutSubviews in UIViewController or layoutSubviews in the common parent of the views you want to layout, to detect when layout has finished, without calling layoutIfNeeded. In there, frame will have the correct values too.

Minimal example:

class MyViewController: UIViewController {
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let v = UIView(frame: .zero)
        view.addSubview(v)
        v.backgroundColor = .blue
        v.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            v.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
            v.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30),
            v.widthAnchor.constraint(equalToConstant: 100),
            v.heightAnchor.constraint(equalToConstant: 100)
        ])
        let u = UIView(frame: .init(x: 10, y: 10, width: 100, height: 100))
        view.addSubview(u)
        u.backgroundColor = .red
        u.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            u.topAnchor.constraint(equalTo: v.bottomAnchor, constant: 10),
            u.leftAnchor.constraint(equalTo: v.leftAnchor),
            u.widthAnchor.constraint(equalToConstant: 100),
            u.heightAnchor.constraint(equalToConstant: 100)
        ])
        
        // before any layout
        print(u.frame)

        // laying out u, doesn't change anything
        u.layoutIfNeeded()
        // note that the above could change the frame of u depending on the frame 
        // of u and its size constraints, but I've deliberately chosen these
        // values and constraints so that it matches your behaviour
        print(u.frame)

        // laying out u, and v, making u.frame what you expect
        view.layoutIfNeeded() // view is the common parent between u and v
        print(u.frame)
    }
}

Upvotes: 1

Related Questions