Reputation: 12625
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
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