如何在使用自动布局时调整CALayer的锚点?

注意 :自从这个问题被问到以后,事情就发生了; 在这里看到一个很好的最近的概述。


在自动布局之前,您可以通过存储框架,设置锚点和恢复框架来更改视图图层的锚点,而无需移动视图。

在自动布局世界中,我们不再设置框架,但是约束似乎不适合将视图的位置调整回我们想要的位置。 您可以破解约束来重新定位您的视图,但是在旋转或其他调整大小事件时,这些事件会再次失效。

以下明智的想法不起作用,因为它会创建一个“无效的布局属性配对(左和宽)”:

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]];

我的意图是将layerView的左边缘,带有调整后的锚点的视图设置为其宽度的一半加上20(我想要从layerView视角的左边缘插入的距离)。

是否可以在不改变视图位置的情况下,在自动布局布局的视图中更改锚点? 我是否需要使用硬编码值并在每次旋转时编辑约束? 我不希望。

我需要更改锚点,以便在对视图应用转换时,我可以获得正确的视觉效果。


[编辑:警告:整个接下来的讨论可能会过时或至少严重缓解的iOS 8,这可能不会再在应用视图变换时触发布局的错误。]

Autolayout与视图变换

Autolayout在视图转换方面效果不佳。 据我所知,原因是你不应该混淆具有变换的视图框架(除了默认的标识转换) - 但这正是autolayout所做的。 自动布局的工作方式是,在layoutSubviews ,运行时将冲入所有约束并相应地设置所有视图的帧。

换句话说,约束不是魔法; 他们只是一个待办事项清单。 layoutSubviews是待办事项完成的地方。 它通过设置框架来完成。

我无法将此视为一个错误。 如果我将这个转换应用于视图:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

我期望看到这个视图的中心位置与之前一样,只有一半。 但根据它的限制,这可能不是我所看到的。

[其实,这里还有一个惊喜:对视图应用转换会立即触发布局。 这在我看来是另一个bug。 或者也许这是第一个bug的核心。 我期望的是能够至少在布局时间之前完成转换,例如设备旋转 - 就像我可以在布局时间之前摆脱帧动画一样。 但实际上布局时间是直接的,这似乎是错误的。]

解决方案1:没有约束

目前的一个解决方案是,如果我要将半永久变换应用于视图(而不是仅仅暂时摆动它),以消除影响它的所有约束。 不幸的是,这通常会导致视图从屏幕消失,因为自动布局仍然存在,现在没有限制来告诉我们在哪里放置视图。 因此,除了删除约束之外,我还将视图的translatesAutoresizingMaskIntoConstraints设置为YES。 该视图现在以旧方式工作,实际上不受自动布局的影响。 (显然,它受自动布局的影响,但隐式自动调整掩码约束会导致其行为与自动布局之前的行为相同。)

解决方案2:只使用适当的约束

如果这看起来有些激烈,另一个解决方案是设置约束以便在预期的转换中正常工作。 例如,如果视图的大小完全由其内部的固定宽度和高度来确定,并且纯粹由其中心定位,则我的缩放转换将按我的预期工作。 在这段代码中,我删除了子视图( 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]];

结果是,如果没有影响视图框架的约束条件,则自动布局不会触及视图的框架 - 这正是涉及转换时的情况。

解决方案3:使用子视图

上述两个解决方案的问题在于我们失去了限制我们观点的好处。 所以这里有一个解决方案来解决这个问题。 从一个看不见的视角开始,其工作仅仅是充当主持人,并使用约束来定位它。 在里面,把真实的视图作为子视图。 使用约束来定位主视图中的子视图,但将这些约束限制为在我们应用变换时不会反击的约束。

这里有一个例子:

在这里输入图像描述

白色视图是主机视图; 你应该假装它是透明的,因此是不可见的。 红色视图是其子视图,通过将其中心固定到主视图的中心进行定位。 现在我们可以在没有任何问题的情况下围绕其中心缩放和旋转红色视图,并且实际上插图显示我们已经这样做了:

self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);

与此同时,当我们旋转设备时,主机视图上的限制使它保持在正确的位置。

解决方案4:改为使用图层转换

不使用视图变换,而是使用图层变换,它不会触发布局,因此不会与约束立即发生冲突。

例如,这个简单的“悸动”视图动画可能会在自动布局下崩溃:

[UIView animateWithDuration:0.3 delay:0
                    options:UIViewAnimationOptionAutoreverse
                 animations:^{
    v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
    v.transform = CGAffineTransformIdentity;
}];

尽管最终视图的大小没有变化,但仅仅通过设置其transform会导致布局发生,并且约束会使视图跳转。 (这是否觉得像一个错误或什么?)但是,如果我们与核心动画(使用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];

我有一个类似的Isuue,刚刚从Apple的Autolayout团队听到了回来。 他们建议使用容器视图方法暗示建议,但他们创建一个UIView的子类来覆盖layoutSubviews并应用自定义布局代码 - 它的作用就像一个魅力

头文件看起来像这样,以便您可以直接从Interface Builder链接您的子视图

#import <UIKit/UIKit.h>

@interface BugFixContainerView : UIView
@property(nonatomic,strong) IBOutlet UIImageView *knobImageView;
@end

和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

正如你所看到的,它在第一次调用时抓住视图的中心点,并在进一步调用中重新使用该位置以相应地放置视图。 这会覆盖Autolayout Code,它会在[super layoutSubviews]之后发生; 其中包含自动布局代码。

就像那样,不再需要避免Autolayout,但是当默认行为不再合适时,您可以创建自己的自动布局。 当然,你可以申请比这个例子更复杂的东西,但这是我所需要的,因为我的应用程序只能使用肖像模式。


我找到一个简单的方法。 它适用于iOS 8和iOS 9。

像使用基于框架的布局时调整anchorPoint一样:

let oldFrame = layerView.frame
layerView.layer.anchorPoint = newAnchorPoint
layerView.frame = oldFrame

当您使用自动布局调整视图的锚点时,您会以约束的方式执行相同的操作。 当anchorPoint从(0.5,0.5)变为(1,0.5)时,layerView将向左移动一段距离,视图宽度的一半,因此您需要对此进行补偿。

我不明白在问题中的约束。所以,假设你添加一个centerX约束相对于superView centerX与一个常量:layerView.centerX = superView.centerX +常量

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2
链接地址: http://www.djcxy.com/p/28279.html

上一篇: How do I adjust the anchor point of a CALayer, when Auto Layout is being used?

下一篇: How do I set the height of tableHeaderView (UITableView) with autolayout?