ios 使用自动布局时,如何调整 CALayer 的锚点?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12943107/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I adjust the anchor point of a CALayer, when Auto Layout is being used?
提问by jrturton
Note: Things have moved on since this question was asked; see herefor 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).
我的目的是layerView
将调整后的锚点的视图的左边缘设置为其宽度的一半加上 20(我想要从超级视图的左边缘插入的距离)。
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.
我需要更改锚点,以便在对视图应用变换时获得正确的视觉效果。
回答by matt
[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.]
[编辑:警告:整个随后的讨论可能会过时或至少被 iOS 8 严重缓解,它可能不再犯在应用视图转换时触发布局的错误。]
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.
自动布局在视图转换中完全不能发挥作用。据我所知,原因是您不应该弄乱具有转换(默认标识转换除外)的视图的框架 - 但这正是自动布局所做的。自动布局的工作方式是在layoutSubviews
运行时通过所有约束并相应地设置所有视图的框架。
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.
换句话说,约束不是魔术;它们只是一个待办事项清单。layoutSubviews
是待办事项列表完成的地方。它通过设置框架来实现。
I can't help regarding this as a bug. If I apply this transform to a view:
我不禁认为这是一个错误。如果我将此转换应用于视图:
v.transform = CGAffineTransformMakeScale(0.5,0.5);
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
解决方案 1:没有约束
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 isaffected by autolayout, obviously, but the implicit autoresizing mask constraints cause its behavior to be just like it was before autolayout.)
当前的一个解决方案是,如果我要对视图应用半永久变换(而不仅仅是以某种方式暂时摆动它),则删除所有影响它的约束。不幸的是,这通常会导致视图从屏幕上消失,因为自动布局仍然发生,现在没有限制告诉我们把视图放在哪里。因此,除了删除约束之外,我还将视图设置translatesAutoresizingMaskIntoConstraints
为 YES。该视图现在以旧方式工作,有效地不受自动布局的影响。(这是由自动布局的影响,很明显,但隐含的自动尺寸面具限制导致其行为是就像它是自动布局之前。)
Solution 2: Use Only Appropriate Constraints
解决方案 2:仅使用适当的约束
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:
如果这看起来有点激烈,另一种解决方案是设置约束以与预期的转换正常工作。例如,如果一个视图的大小完全由其内部固定的宽度和高度确定,并且完全由其中心定位,那么我的缩放变换将按我的预期工作。在这段代码中,我删除了子视图 ( otherView
)上的现有约束,并用这四个约束替换它们,给它一个固定的宽度和高度,并完全固定在它的中心。之后,我的比例转换工作:
NSMutableArray* cons = [NSMutableArray array];
for (NSLayoutConstraint* con in self.view.constraints)
if (con.firstItem == self.otherView || con.secondItem == self.otherView)
[cons addObject:con];
[self.view removeConstraints:cons];
[self.otherView removeConstraints:self.otherView.constraints];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.otherView.center.x]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:self.otherView.center.y]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.width]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.height]];
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
解决方案 3:使用子视图
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:
白色视图为主机视图;你应该假装它是透明的,因此是不可见的。红色视图是它的子视图,通过将其中心固定到宿主视图的中心来定位。现在我们可以毫无问题地围绕其中心缩放和旋转红色视图,事实上,插图表明我们已经这样做了:
self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);
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
解决方案 4:改用层变换
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:
例如,这个简单的“throb”视图动画可能会在自动布局下中断:
[UIView animateWithDuration:0.3 delay:0
options:UIViewAnimationOptionAutoreverse
animations:^{
v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
v.transform = CGAffineTransformIdentity;
}];
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:
即使最终视图的大小没有变化,仅设置其transform
导致布局发生,并且约束可以使视图跳跃。(这感觉像是一个错误还是什么?)但是如果我们对 Core Animation 做同样的事情(使用 CABasicAnimation 并将动画应用到视图层),布局不会发生,并且它工作正常:
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.autoreverses = YES;
ba.duration = 0.3;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
[v.layer addAnimation:ba forKey:nil];
回答by sensslen
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
我有一个类似的问题,刚刚从 Apple 的 Autolayout 团队那里听到了 Back。他们建议使用马特建议的容器视图方法,但他们创建了 UIView 的子类来覆盖 layoutSubviews 并在那里应用自定义布局代码 - 它就像一个魅力
The Header File looks like that so that you can link your subview of choice directly from Interface Builder
头文件看起来像这样,您可以直接从 Interface Builder 链接您选择的子视图
#import <UIKit/UIKit.h>
@interface BugFixContainerView : UIView
@property(nonatomic,strong) IBOutlet UIImageView *knobImageView;
@end
and the m File applyes the special Code like that:
并且 m 文件应用了这样的特殊代码:
#import "BugFixContainerView.h"
@implementation BugFixContainerView
- (void)layoutSubviews
{
static CGPoint fixCenter = {0};
[super layoutSubviews];
if (CGPointEqualToPoint(fixCenter, CGPointZero)) {
fixCenter = [self.knobImageView center];
} else {
self.knobImageView.center = fixCenter;
}
}
@end
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.
如您所见,它在第一次调用时会抓住 View 的中心点,并在后续调用中重用该 Position 以相应地放置 View。这在这个意义上覆盖了自动布局代码,它发生在 [super layoutSubviews] 之后;其中包含自动布局代码。
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.
这样就不再需要避免自动布局,但是当默认行为不再合适时,您可以创建自己的自动布局。当然,您可以应用比该示例中更复杂的东西,但这就是我所需要的,因为我的应用程序只能使用纵向模式。
回答by seedante
I find a simple way. And it works on iOS 8 and iOS 9.
我找到了一个简单的方法。它适用于 iOS 8 和 iOS 9。
Like adjust anchorPoint when you use frame-based layout:
就像在使用基于框架的布局时调整 anchorPoint 一样:
let oldFrame = layerView.frame
layerView.layer.anchorPoint = newAnchorPoint
layerView.frame = oldFrame
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.
当你用自动布局调整视图的锚点时,你做同样的事情,但以约束方式。当anchorPoint从(0.5, 0.5)变为(1, 0.5)时,layerView会向左移动视图宽度长度一半的距离,因此需要对此进行补偿。
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
我不明白问题中的约束。所以,假设您添加一个相对于 superView centerX 的 centerX 约束,并带有一个常量: layerView.centerX = superView.centerX + constant
layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2
回答by algal
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.
相反,需要的是修改布局约束本身以补偿通过设置锚点产生的变化。以下函数对未转换的视图执行此操作。
/**
Set the anchorPoint of view without changing is perceived position.
@param view view whose anchorPoint we will mutate
@param anchorPoint new anchorPoint of the view in unit coords (e.g., {0.5,1.0})
@param xConstraint an NSLayoutConstraint whose constant property adjust's view x.center
@param yConstraint an NSLayoutConstraint whose constant property adjust's view y.center
As multiple constraints can contribute to determining a view's center, the user of this
function must specify which constraint they want modified in order to compensate for the
modification in anchorPoint
*/
void SetViewAnchorPointMotionlesslyUpdatingConstraints(UIView * view,CGPoint anchorPoint,
NSLayoutConstraint * xConstraint,
NSLayoutConstraint * yConstraint)
{
// assert: old and new anchorPoint are in view's unit coords
CGPoint const oldAnchorPoint = view.layer.anchorPoint;
CGPoint const newAnchorPoint = anchorPoint;
// Calculate anchorPoints in view's absolute coords
CGPoint const oldPoint = CGPointMake(view.bounds.size.width * oldAnchorPoint.x,
view.bounds.size.height * oldAnchorPoint.y);
CGPoint const newPoint = CGPointMake(view.bounds.size.width * newAnchorPoint.x,
view.bounds.size.height * newAnchorPoint.y);
// Calculate the delta between the anchorPoints
CGPoint const delta = CGPointMake(newPoint.x-oldPoint.x, newPoint.y-oldPoint.y);
// get the x & y constraints constants which were contributing to the current
// view's position, and whose constant properties we will tweak to adjust its position
CGFloat const oldXConstraintConstant = xConstraint.constant;
CGFloat const oldYConstraintConstant = yConstraint.constant;
// calculate new values for the x & y constraints, from the delta in anchorPoint
// when autolayout recalculates the layout from the modified constraints,
// it will set a new view.center that compensates for the affect of the anchorPoint
CGFloat const newXConstraintConstant = oldXConstraintConstant + delta.x;
CGFloat const newYConstraintConstant = oldYConstraintConstant + delta.y;
view.layer.anchorPoint = newAnchorPoint;
xConstraint.constant = newXConstraintConstant;
yConstraint.constant = newYConstraintConstant;
[view setNeedsLayout];
}
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 allthe 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..
我承认这可能不是您所希望的一切,因为通常您想要修改 anchorPoint 的唯一原因是设置转换。这将需要一个更复杂的函数来更新布局约束以反映可能由转换属性本身引起的所有帧更改。这很棘手,因为变换可以对框架做很多事情。缩放或旋转变换会使框架更大,因此我们需要更新任何宽度或高度约束等。
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.
如果您仅将变换用于临时动画,那么上面的内容可能就足够了,因为我认为自动布局不会阻止飞行中的动画呈现表示纯粹瞬态违反约束的图像。
回答by David R?nnqvist
tl:dr:You can create an outlet for one of the constraints so that it can be removed and added back again.
tl:dr:您可以为约束之一创建一个出口,以便可以将其删除并重新添加回来。
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.
接下来,我为将要旋转的视图和中心 x 对齐约束添加了一个出口。
@property (weak, nonatomic) IBOutlet UIView *rotatingView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *xAlignmentContstraint;
Later in viewDidAppear
I calculate the new anchor point
稍后viewDidAppear
我计算新的锚点
UIView *view = self.rotatingView;
CGPoint rotationPoint = // The point I'm rotating around... (only X differs)
CGPoint anchorPoint = CGPointMake((rotationPoint.x-CGRectGetMinX(view.frame))/CGRectGetWidth(view.frame),
(rotationPoint.y-CGRectGetMinY(view.frame))/CGRectGetHeight(view.bounds));
CGFloat xCenterDifference = rotationPoint.x-CGRectGetMidX(view.frame);
view.layer.anchorPoint = anchorPoint;
Then 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.
然后我删除我有一个出口的约束,创建一个新的偏移并重新添加它。之后,我告诉具有更改约束的视图它需要更新约束。
[self.view removeConstraint:self.xAlignmentContstraint];
self.xAlignmentContstraint = [NSLayoutConstraint constraintWithItem:self.rotatingView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:xDiff];
[self.view addConstraint:self.xAlignmentContstraint];
[self.view needsUpdateConstraints];
Finally I just add the rotation animation to the rotating view.
最后我只是将旋转动画添加到旋转视图中。
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.toValue = @(-M_PI_2);
rotate.autoreverses = YES;
rotate.repeatCount = INFINITY;
rotate.duration = 1.0;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[view.layer addAnimation:rotate forKey:@"myRotationAnimation"];
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.
即使在旋转设备或以其他方式导致其更新约束时,旋转层看起来也保持居中(应该如此)。新约束和更改的锚点在视觉上相互抵消。
回答by jrturton
My current solution is to manually adjust the layer's position in viewDidLayoutSubviews
. This code could also be used in layoutSubviews
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.
我目前的解决方案是手动调整图层在viewDidLayoutSubviews
. 此代码也可用于layoutSubviews
视图子类,但在我的情况下,我的视图是视图控制器内的顶级视图,因此这意味着我不必创建 UIView 子类。
It seems like too much effort so other answers are most welcome.
似乎付出了太多努力,因此非常欢迎其他答案。
-(void)viewDidLayoutSubviews
{
for (UIView *view in self.view.subviews)
{
CGPoint anchorPoint = view.layer.anchorPoint;
// We're only interested in views with a non-standard anchor point
if (!CGPointEqualToPoint(CGPointMake(0.5, 0.5),anchorPoint))
{
CGFloat xDifference = anchorPoint.x - 0.5;
CGFloat yDifference = anchorPoint.y - 0.5;
CGPoint currentPosition = view.layer.position;
// Use transforms if we can, otherwise manually calculate the frame change
// Assuming a transform is in use since we are changing the anchor point.
if (CATransform3DIsAffine(view.layer.transform))
{
CGAffineTransform current = CATransform3DGetAffineTransform(view.layer.transform);
CGAffineTransform invert = CGAffineTransformInvert(current);
currentPosition = CGPointApplyAffineTransform(currentPosition, invert);
currentPosition.x += (view.bounds.size.width * xDifference);
currentPosition.y += (view.bounds.size.height * yDifference);
currentPosition = CGPointApplyAffineTransform(currentPosition, current);
}
else
{
CGFloat transformXRatio = view.bounds.size.width / view.frame.size.width;
if (xDifference < 0)
transformXRatio = 1.0/transformXRatio;
CGFloat transformYRatio = view.bounds.size.height / view.frame.size.height;
if (yDifference < 0)
transformYRatio = 1.0/transformYRatio;
currentPosition.x += (view.bounds.size.width * xDifference) * transformXRatio;
currentPosition.y += (view.bounds.size.height * yDifference) * transformYRatio;
}
view.layer.position = currentPosition;
}
}
}
回答by jrturton
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:
无论如何,它对我的情况来说是一种享受。视图在 viewDidLoad 中设置:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *redView = [UIView new];
redView.translatesAutoresizingMaskIntoConstraints = NO;
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
self.redView = redView;
UIView *greenView = [UIView new];
greenView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
greenView.layer.anchorPoint = CGPointMake(1.0, 0.5);
greenView.frame = redView.bounds;
greenView.backgroundColor = [UIColor greenColor];
[redView addSubview:greenView];
self.greenView = greenView;
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = 0.005;
self.redView.layer.sublayerTransform = perspective;
}
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:
它似乎在设备旋转期间丢失了自身,因此我将其添加到 viewDidLayoutSubviews 方法中:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[CATransaction begin];
[CATransaction setDisableActions:YES];
CATransform3D transform = self.greenView.layer.transform;
self.greenView.layer.transform = CATransform3DIdentity;
self.greenView.frame = self.redView.bounds;
self.greenView.layer.transform = transform;
[CATransaction commit];
}
回答by John Estropia
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:
失去锚点/转换范式并尝试:
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:-somePadding]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:someViewWeDependTheWidthOn
attribute:NSLayoutAttributeWidth
multiplier:0.5f // because you want it to be half of someViewWeDependTheWidthOn
constant:-20.0f]]; // your 20pt offset from the left
The NSLayoutAttributeRight
constraint means exactly like anchorPoint = CGPointMake(1.0, 0.5)
, and the NSLayoutAttributeWidth
constraint is roughly equivalent to your previous code's NSLayoutAttributeLeft
.
该NSLayoutAttributeRight
约束装置完全一样anchorPoint = CGPointMake(1.0, 0.5)
,和NSLayoutAttributeWidth
约束大致相当于以前的代码的NSLayoutAttributeLeft
。
回答by Hans
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:
这个问题和答案启发了我用自动布局和缩放来解决我自己的问题,但使用滚动视图。我在 github 上创建了我的解决方案示例:
https://github.com/hansdesmedt/AutoLayout-scrollview-scale
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.
这是一个 UIScrollView 的示例,它具有完全在 AutoLayout 中制作的自定义分页,并且可以通过长按和点击进行缩放(CATransform3DMakeScale)进行缩放。iOS 6 和 7 兼容。
回答by plamkata__
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.
我从 XIB 看到了带有自动布局的视图。我想更新它的转换属性。将视图嵌入容器视图并不能解决我的问题,因为自动布局在容器视图上表现得很奇怪。这就是为什么我只是添加了第二个容器视图来包含包含我的视图并对其应用转换的容器视图。