Note: Things have moved on since this question was asked; see here for a good recent overview.
Before auto layout, you could change the anchor point of a view's layer without moving the view by storing the frame, setting the anchor point, and restoring the frame.
In an auto layout world, we don't set frames any more, but constraints don't seem up to the task of adjusting the position of a view back to where we want it to. You can hack the constraints to reposition your view, but on rotation or other resizing events, these become invalid again.
The following bright idea doesn't work as it creates an "Invalid pairing of layout attributes (left and width)":
layerView.layer.anchorPoint = CGPointMake(1.0, 0.5);
// Some other size-related constraints here which all work fine...
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:layerView
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:20.0]];
My intention here was to set the left edge of layerView
, the view with the adjusted anchor point, to half of its width plus 20 (the distance I want inset from the left edge of the superview).
Is it possible to change the anchor point, without changing the location of a view, in a view that is laid out with auto layout? Do I need to use hardcoded values and edit the constraint on every rotation? I do hope not.
I need to change the anchor point so that when I apply a transform to the view, I get the correct visual effect.
My current solution is to manually adjust the layer's position in
viewDidLayoutSubviews
. This code could also be used inlayoutSubviews
for a view subclass, but in my case my view is a top-level view inside a view controller, so this meant I didn't have to make a UIView subclass.It seems like too much effort so other answers are most welcome.
I think you are defeating the purpose of autolayout with that method. You did mention that the width and right edge depends on the superview, so why not just add constraints along that line of thinking?
Lose the anchorPoint/transform paradigm and try:
The
NSLayoutAttributeRight
constraint means exactly likeanchorPoint = CGPointMake(1.0, 0.5)
, and theNSLayoutAttributeWidth
constraint is roughly equivalent to your previous code'sNSLayoutAttributeLeft
.If you're using auto layout, then I don't see how manually setting position will serve in the long run because eventually auto layout will clobber the position value you've set when it calculates its own layout.
Rather, what's needed is to modify the layout constraints themselves to compensate for the changes produced by setting the anchorPoint. The following function does that for untransformed views.
I admit this is probably not everything you were hoping for, since usually the only reason you'd want to modify the anchorPoint is to set a transform. That would require a more complex function that updates the layout constraints to reflect all the frame changes that could be caused by the transform property itself. This is tricky because transforms can do a lot to the frame. A scaling or rotation transform would make the frame bigger, so we'd need to update any width or height constraints, etc..
If you're only using the transform for a temporary animation, then what's above may suffice since I don't believe auto layout will prevent the in-flight animation from presenting images that represent purely transient violations of the constraints.
tl:dr: You can create an outlet for one of the constraints so that it can be removed and added back again.
I created a new project and added a view with a fixed size in the center. The constraints are shown in the image below.
Next I added an outlet for the view that is going to rotate and for the center x alignment constraint.
Later in
viewDidAppear
I calculate the new anchor pointThen I remove the constraint that I have an outlet for, create a new one that is offset and add it back again. After that I tell the view with the changed constraint that it needs to update the constraints.
Finally I just add the rotation animation to the rotating view.
The rotating layer looks like it stays centered (which it should) even when rotating the device or otherwise causing it to update the constraints. The new constraint and the changed anchor point visually cancel each other out.
Inspired my matt's answer, I decided to try a different approach. A container view, with constraints applied appropriately, can be used. The view with the modified anchor point can then be placed within the container view, using autoresizing masks and explicit frame setting just like in the bad old days.
It works a treat, for my situation anyway. The views are set up here in viewDidLoad:
It doesn't matter that the frames for the red view are zero at this point, because of the autoresizing mask on the green view.
I added a rotation transform on an action method, and this was the result:
It did seem to lose itself during device rotation, so I added this to the viewDidLayoutSubviews method:
It is a big topic and I have not read all of the comments but was facing the same issue.
I had a view from XIB with autolayout. And I wanted to update its transform property. Embedding the view into a container view does not solve my problem because the autolayout was acting weirdly on the container view. That's why I just added second container view to contain the container view that contains my view and was applying transformations on it.