Ios 使用自动布局时,如何调整CALayer的定位点?

Ios 使用自动布局时,如何调整CALayer的定位点?,ios,calayer,autolayout,Ios,Calayer,Autolayout,注意:自从提出这个问题以来,事情一直在向前发展;有关最近的良好概述,请参阅 在自动布局之前,可以通过存储帧、设置锚定点和恢复帧来更改视图图层的锚定点,而无需移动视图 在自动布局世界中,我们不再设置框架,但约束似乎无法将视图的位置调整回我们希望的位置。您可以修改约束以重新定位视图,但在旋转或其他调整大小事件中,这些约束将再次无效 以下好主意不起作用,因为它创建了“布局属性(左和宽)的无效配对”: 我在这里的意图是将带有调整定位点的视图layerView的左边缘设置为其宽度的一半加上20(我希望插

注意:自从提出这个问题以来,事情一直在向前发展;有关最近的良好概述,请参阅


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

在自动布局世界中,我们不再设置框架,但约束似乎无法将视图的位置调整回我们希望的位置。您可以修改约束以重新定位视图,但在旋转或其他调整大小事件中,这些约束将再次无效

以下好主意不起作用,因为它创建了“布局属性(左和宽)的无效配对”:

我在这里的意图是将带有调整定位点的视图
layerView
的左边缘设置为其宽度的一半加上20(我希望插入到superview左边缘的距离)

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


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

我当前的解决方案是在
viewdilayoutsubviews
中手动调整图层的位置。此代码也可以在视图子类的
layoutSubviews
中使用,但在我的示例中,我的视图是视图控制器中的顶级视图,因此这意味着我不必创建UIView子类

这似乎是太多的努力,所以其他答案是最受欢迎的

-(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;
        }

    }
}
-(void)ViewDidLayoutSubView
{
for(UIView*self.view.subview中的视图)
{
CGPoint anchorPoint=view.layer.anchorPoint;
//我们只对具有非标准定位点的视图感兴趣
如果(!CGPointEqualToPoint(CGPointMake(0.5,0.5),锚点))
{
CGFloat xDifference=锚点.x-0.5;
cGyDifference=锚点y-0.5;
CGPoint currentPosition=view.layer.position;
//如果可以,请使用变换,否则请手动计算帧更改
//假设正在使用变换,因为我们正在更改定位点。
if(CATTransferrM3Disaffine(view.layer.transform))
{
CGAffineTransform current=CATTransformM3DGetAffineTransform(view.layer.transform);
CGAffineTransform invert=CGAffineTransformInvert(当前);
currentPosition=CGPointApplyAffinetTransform(当前位置,反转);
currentPosition.x+=(view.bounds.size.width*xDifference);
currentPosition.y+=(view.bounds.size.height*y差异);
currentPosition=CGPointApplyAffinetTransform(当前位置,当前);
}
其他的
{
CGFloat transformXRatio=view.bounds.size.width/view.frame.size.width;
if(xd差<0)
transformXRatio=1.0/transformXRatio;
CGFloat transformYRatio=view.bounds.size.height/view.frame.size.height;
如果(Y差值<0)
transformYRatio=1.0/transformYRatio;
currentPosition.x+=(view.bounds.size.width*xDifference)*transformXRatio;
currentPosition.y+=(view.bounds.size.height*y差异)*transformYRatio;
}
view.layer.position=当前位置;
}
}
}

我认为你用这种方法破坏了自动布局的目的。您提到过宽度和右边缘取决于superview,那么为什么不沿着这条思路添加约束呢

失去主播点/转换范例,然后尝试:

[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

nslayouttributewidth
约束的意思与
anchorPoint=CGPointMake(1.0,0.5)
完全相同,如果使用自动布局,那么
nslayouttributewidth
约束大致相当于以前代码的
nslayouttributeft
,然后,我看不出手动设置位置在长期内会起到什么作用,因为最终自动布局会在计算自己的布局时破坏您设置的位置值

相反,需要修改布局约束本身,以补偿设置锚点所产生的更改。以下函数对未转换的视图执行此操作

/**
  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];
}
我承认这可能不是你所希望的一切,因为通常你想要修改主播点的唯一原因是设置一个变换。这将需要一个更复杂的函数来更新布局约束,以反映可能由变换特性本身引起的所有帧更改。这很棘手,因为变换可以对帧做很多事情。缩放或旋转变换会使帧变大,因此我们需要更新任何宽度或高度约束等


如果仅对临时动画使用变换,那么,上面的内容可能就足够了,因为我不认为自动布局会阻止飞行中的动画显示纯粹是暂时违反约束的图像。

tl:dr:您可以为其中一个约束创建一个出口,以便将其删除并重新添加


我创建了一个新项目,并在中心添加了一个固定大小的视图。约束如下图所示

接下来,我为将要旋转的视图和“中心x对齐”约束添加了一个出口

@property (weak, nonatomic) IBOutlet UIView *rotatingView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *xAlignmentContstraint;
稍后在
视图中出现
我计算新的定位点

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;
然后,我删除了我有一个出口的约束,创建一个新的是偏移,并再次添加回来。然后,我用改变了的结构告诉大家这个观点
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;
[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];
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"];
v.transform = CGAffineTransformMakeScale(0.5,0.5);
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]];
self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);
[UIView animateWithDuration:0.3 delay:0
                    options:UIViewAnimationOptionAutoreverse
                 animations:^{
    v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
    v.transform = CGAffineTransformIdentity;
}];
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];
- (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;
}
-(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];

}
#import <UIKit/UIKit.h>

@interface BugFixContainerView : UIView
@property(nonatomic,strong) IBOutlet UIImageView *knobImageView;
@end
#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
#Before changing anchor point to top-left
view.size == superview.size
view.center == superview.center
view.layer.anchorPoint = CGPointMake(0, 0)
view.size == superview.size
view.center == superview.topLeft                <----- L0-0K, center is now top-left
let oldFrame = layerView.frame
layerView.layer.anchorPoint = newAnchorPoint
layerView.frame = oldFrame
layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2