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.
I had a similar Isuue and just heard Back from the Autolayout Team at Apple. They Suggest to use the Container View Approach matt suggests but they create a Subclass of UIView to overwrite layoutSubviews and apply custom layout Code there - It works like a charm
The Header File looks like that so that you can link your subview of choice directly from Interface Builder
and the m File applyes the special Code like that:
As you can see, it grabs the center point of the View when first called and reuses that Position in further calls in order to place the View accordingly. This overwrites the Autolayout Code in that sense, that it takes place after [super layoutSubviews]; which contains autolayout Code.
Like that there is no longer any need to avoid Autolayout, but you can create your own autolayout when default Behaviours are no longer appropriate. Of course you can apply way more complicated stuff than what is in that Example but this was all I needed since my App can only use Portrait Mode.
tl;dr Let's say you changed the anchor point to (0, 0). The anchor point is now top left. Anytime you see the word center in auto layout, you should think top-left.
When you adjust your anchorPoint, you just change the semantics of AutoLayout. Auto layout will not interfere with your anchorPoint nor vice versa. If you don't understand this, you're going to have a bad time.
Example:
Figure A. No anchor point modifications
Figure B. Anchor point changed to top left
Figure A and Figure B look exactly the same. Nothing changed. Just the definition of what center refers to changed.
I find a simple way. And it works on iOS 8 and iOS 9.
Like adjust anchorPoint when you use frame-based layout:
When you adjust view's anchor with auto layout, you do the same thing but in constraints way. When anchorPoint change from (0.5, 0.5) to (1, 0.5), the layerView will move left with a distance by half the length of view width, so you need to compensate for this.
I don't understand the constraint in the question.So, assume that you add a centerX constraint relative to superView centerX with a constant: layerView.centerX = superView.centerX + constant
[EDIT: Warning: The entire ensuing discussion will be possibly outmoded or at least heavily mitigated by iOS 8, which may no longer make the mistake of triggering layout at the time that a view transform is applied.]
Autolayout vs. View Transforms
Autolayout does not play at all well with view transforms. The reason, as far as I can discern, is that you're not supposed to mess with the frame of a view that has a transform (other than the default identity transform) - but that is exactly what autolayout does. The way autolayout works is that in
layoutSubviews
the runtime comes dashing through all the constraints and setting the frames of all the views accordingly.In other words, the constraints are not magic; they are just a to-do list.
layoutSubviews
is where the to-do list gets done. And it does it by setting frames.I can't help regarding this as a bug. If I apply this transform to a view:
I expect to see the view appear with its center in the same place as before and at half the size. But depending on its constraints, that may not be what I see at all.
[Actually, there's a second surprise here: applying a transform to a view triggers layout immediately. This seems to me be another bug. Or perhaps it's the heart of the first bug. What I would expect is to be able to get away with a transform at least until layout time, e.g. the device is rotated - just as I can get away with a frame animation until layout time. But in fact layout time is immediate, which seems just wrong.]
Solution 1: No Constraints
One current solution is, if I'm going to apply a semipermanent transform to a view (and not merely waggle it temporarily somehow), to remove all constraints affecting it. Unfortunately this typically causes the view to vanish from the screen, since autolayout still takes place, and now there are no constraints to tell us where to put the view. So in addition to removing the constraints, I set the view's
translatesAutoresizingMaskIntoConstraints
to YES. The view now works in the old way, effectively unaffected by autolayout. (It is affected by autolayout, obviously, but the implicit autoresizing mask constraints cause its behavior to be just like it was before autolayout.)Solution 2: Use Only Appropriate Constraints
If that seems a bit drastic, another solution is to set the constraints to work correctly with an intended transform. If a view is sized purely by its internal fixed width and height, and positioned purely by its center, for example, my scale transform will work as I expect. In this code, I remove the existing constraints on a subview (
otherView
) and replace them with those four constraints, giving it a fixed width and height and pinning it purely by its center. After that, my scale transform works:The upshot is that if you have no constraints that affect a view's frame, autolayout won't touch the view's frame - which is just what you're after when a transform is involved.
Solution 3: Use a Subview
The problem with both the above solutions is that we lose the benefits of constraints to position our view. So here's a solution that solves that. Start with an invisible view whose job is solely to act as a host, and use constraints to position it. Inside that, put the real view as a subview. Use constraints to position the subview within the host view, but limit those constraints to constraints that won't fight back when we apply a transform.
Here's an illustration:
The white view is host view; you are supposed to pretend that it is transparent and hence invisible. The red view is its subview, positioned by pinning its center to the host view's center. Now we can scale and rotate the red view around its center without any problem, and indeed the illustration shows that we have done so:
And meanwhile the constraints on the host view keep it in the right place as we rotate the device.
Solution 4: Use Layer Transforms Instead
Instead of view transforms, use layer transforms, which do not trigger layout and thus do not cause immediate conflict with constraints.
For example, this simple "throb" view animation may well break under autolayout:
Even though in the end there was no change in the view's size, merely setting its
transform
causes layout to happen, and constraints can make the view jump around. (Does this feel like a bug or what?) But if we do the same thing with Core Animation (using a CABasicAnimation and applying the animation to the view's layer), layout doesn't happen, and it works fine:This question and answers inspired me to solve my own problems with Autolayout and scaling, but with scrollviews. I created an example of my solution on github:
https://github.com/hansdesmedt/AutoLayout-scrollview-scale
This is an example of an UIScrollView with custom paging completely made in AutoLayout and is scalable (CATransform3DMakeScale) with long press and tap to zoom. iOS 6 and 7 compatible.