更改我的CALayer的anchorPoint会移动视图

Ken*_*ker 127 iphone cocoa cocoa-touch uiview

我想改变anchorPoint,但保持视图在同一个地方.我试过NSLog-ing分词self.layer.positionself.center他们都保持不变,无论更改anchorPoint的.然而我的观点却在变化

关于如何做到这一点的任何提示?

self.layer.anchorPoint = CGPointMake(0.5, 0.5);
NSLog(@"center point: %f %f", self.layer.position.x, self.layer.position.y);
self.layer.anchorPoint = CGPointMake(1, 1);
NSLog(@"center point: %f %f", self.layer.position.x, self.layer.position.y);
Run Code Online (Sandbox Code Playgroud)

输出是:

2009-12-27 20:43:24.161 Type[11289:207] center point: 272.500000 242.500000
2009-12-27 20:43:24.162 Type[11289:207] center point: 272.500000 242.500000
Run Code Online (Sandbox Code Playgroud)

小智 203

我有同样的问题.Brad Larson的解决方案即使在旋转视图时也能很好地工作.这是他的解决方案翻译成代码.

-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
    CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, 
                                   view.bounds.size.height * anchorPoint.y);
    CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, 
                                   view.bounds.size.height * view.layer.anchorPoint.y);

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);

    CGPoint position = view.layer.position;

    position.x -= oldPoint.x;
    position.x += newPoint.x;

    position.y -= oldPoint.y;
    position.y += newPoint.y;

    view.layer.position = position;
    view.layer.anchorPoint = anchorPoint;
}
Run Code Online (Sandbox Code Playgroud)

而快速的等价物:

func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
    var newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
    var oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}
Run Code Online (Sandbox Code Playgroud)

SWIFT 4.x

func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
    var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x,
                           y: view.bounds.size.height * anchorPoint.y)


    var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x,
                           y: view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = newPoint.applying(view.transform)
    oldPoint = oldPoint.applying(view.transform)

    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}
Run Code Online (Sandbox Code Playgroud)

  • FWIW,我不得不将setAnchor方法包装在dispatch_async块中以使其工作.不知道为什么我在viewDidLoad中调用它.viewDidLoad是否不再在主线程队列上? (4认同)
  • 一旦我能在这里看到你的例子,那就完美而且完全有道理.谢谢你! (2认同)
  • 我在我的awakeFromNib/initWithFrame方法中使用此代码,但它仍然不起作用,我是否需要更新显示?编辑:setNeedsDisplay不起作用 (2认同)
  • 为什么要使用 view.bounds?你不应该使用 layer.bounds 吗? (2认同)
  • 伟大的代码,它对我来说很完美 (2认同)

Bra*_*son 137

Core Animation Programming Guide 的Layer Geometry and Transforms部分解释了CALayer的position和anchorPoint属性之间的关系.基本上,层的位置是根据图层的anchorPoint的位置指定的.默认情况下,图层的anchorPoint为(0.5,0.5),位于图层的中心.设置图层的位置时,您将在其超层的坐标系中设置图层中心的位置.

因为位置是相对于图层的anchorPoint,所以在保持相同位置的同时更改该anchorPoint会移动图层.为了防止这种移动,您需要调整图层的位置以考虑新的anchorPoint.我这样做的一种方法是抓取图层的边界,将边界的宽度和高度乘以旧的和新的anchorPoint的标准化值,取两个anchorPoints的差值,并将该差值应用于图层的位置.

您甚至可以通过使用CGPointApplyAffineTransform()UIView的CGAffineTransform以这种方式考虑轮换.

  • @ ss1271 - 如上所述,图层的anchorPoint是其坐标系的基础.如果设置为(0.5,0.5),则在设置图层的位置时,您将设置其中心位于其超层的坐标系中的位置.如果将anchorPoint设置为(0.0,0.5),则设置位置将设置其左边缘的中心位置.再次,Apple在上面链接的文档中有一些很好的图像(我已经更新了参考文献). (6认同)
  • 请参阅Magnus的示例函数,了解Brad的答案如何在Objective-C中实现. (4认同)

Ken*_*ker 44

解决这个问题的关键是使用frame属性,这是奇怪的唯一改变.

斯威夫特2

let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(1, 1)
self.frame = oldFrame
Run Code Online (Sandbox Code Playgroud)

斯威夫特3

let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 1, y: 1)
self.frame = oldFrame
Run Code Online (Sandbox Code Playgroud)

然后我做我的调整大小,它从anchorPoint扩展.然后我必须恢复旧的anchorPoint;

斯威夫特2

let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(0.5,0.5)
self.frame = oldFrame
Run Code Online (Sandbox Code Playgroud)

斯威夫特3

let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.frame = oldFrame
Run Code Online (Sandbox Code Playgroud)

编辑:如果视图被旋转,这会剥落,因为如果已应用CGAffineTransform,则帧属性未定义.

  • 只是想添加为什么这有效并且似乎是最简单的方法:根据文档,frame属性是一个"组合"属性:*"frame的值是从bounds,anchorPoint和position属性派生的."*那是也为什么"框架"属性不可动画. (9认同)
  • 老实说,这是我的首选方法。如果您不尝试独立操作边界和位置或为它们中的任何一个设置动画,只需在更改锚点之前捕获帧通常就足够了。不需要数学。 (2认同)

2cu*_*ech 27

当我开始将它与我对UIView中的frame.origin的理解进行比较时,对我来说理解position并且anchorPoint是最简单的.带有frame.origin =(20,30)的UIView意味着UIView距左侧20点,距其父视图顶部30点.这个距离是从UIView的哪个点算出的?它是从UIView的左上角计算出来的.

在层中anchorPoint标记计算该距离的点(以标准化形式,即0到1),例如,layer.position =(20,30)表示该层anchorPoint距离其父层的左侧20个点和顶部30个点.默认情况下,图层anchorPoint为(0.5,0.5),因此距离计算点位于图层的中心.下图将有助于澄清我的观点:

在此输入图像描述

anchorPoint 也恰好是在将变换应用于图层时将发生旋转的点.


Fri*_*ice 16

有这么简单的解决方案.这是基于Kenny的回答.但是,不是应用旧框架,而是使用它的原点和新框架来计算过渡,然后将该过渡应用到中心.它也适用于旋转视图!这是代码,比其他解决方案简单得多:

func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
   let oldOrigin = view.frame.origin
   view.layer.anchorPoint = anchorPoint
   let newOrigin = view.frame.origin

   let translation = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)

   view.center = CGPoint(x: view.center.x - translation.x, y: view.center.y - translation.y)
}
Run Code Online (Sandbox Code Playgroud)

和Swift版本:

func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
   let oldOrigin = view.frame.origin
   view.layer.anchorPoint = anchorPoint
   let newOrigin = view.frame.origin

   let translation = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)

   view.center = CGPoint(x: view.center.x - translation.x, y: view.center.y - translation.y)
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案太好了,我现在患有糖尿病。 (3认同)
  • 漂亮的小方法...只需几个小修复就可以了:第3行:anchorPoint中的大写字母P. 第8行:TRANS.Y = NEW - OLD (2认同)
  • 我仍然困惑如何使用它.我现在面临同样的问题,因为用uigesturerotation旋转我的观点.我很奇怪,我应该在哪里调用这种方法.如果我在手势识别器处理程序中调用.它使视图消失. (2认同)

小智 13

对于那些需要它的人来说,这是Magnus在Swift中的解决方案:

func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
    var newPoint: CGPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
    var oldPoint: CGPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

    var position: CGPoint = view.layer.position

    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.setTranslatesAutoresizingMaskIntoConstraints(true)     // Added to deal with auto layout constraints
    view.layer.anchorPoint = anchorPoint
    view.layer.position = position
}
Run Code Online (Sandbox Code Playgroud)

  • 赞成`view.setTranslatesAutoresizingMaskIntoConstraints(true)`。谢啦 (2认同)
  • `view.setTranslatesAutoresizingMaskIntoConstraints(true)` 在使用自动布局约束时是必要的。 (2认同)
  • 非常感谢你做的这些!`translatesAutoresizingMaskIntoConstraints` 正是我所缺少的,现在可以学习了 (2认同)

iMa*_*din 5

这是针对OS X上的NSView调整的user945711的答案.除了NSView没有.center属性之外,NSView的框架不会改变(可能是因为NSView默认情况下不带CALayer)但是当更改anchorPoint时CALayer框架原点会发生变化.

func setAnchorPoint(anchorPoint: NSPoint, view: NSView) {
    guard let layer = view.layer else { return }

    let oldOrigin = layer.frame.origin
    layer.anchorPoint = anchorPoint
    let newOrigin = layer.frame.origin

    let transition = NSMakePoint(newOrigin.x - oldOrigin.x, newOrigin.y - oldOrigin.y)
    layer.frame.origin = NSMakePoint(layer.frame.origin.x - transition.x, layer.frame.origin.y - transition.y)
}
Run Code Online (Sandbox Code Playgroud)