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

时间:2009-12-28 04:49:05

标签: iphone cocoa cocoa-touch uiview

我想更改anchorPoint,但请将视图保留在同一位置。 我已尝试NSLog - 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);

输出结果为:

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

11 个答案:

答案 0 :(得分:200)

我遇到了同样的问题。 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;
}

快速的等价物:

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
}
  

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
}

答案 1 :(得分:135)

核心动画编程指南的Layer Geometry and Transforms部分解释了CALayer的位置和anchorPoint属性之间的关系。基本上,层的位置是根据图层的anchorPoint的位置指定的。默认情况下,图层的anchorPoint为(0.5,0.5),位于图层的中心。设置图层的位置时,您将在其超级图层的坐标系中设置图层中心的位置。

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

您甚至可以通过将CGPointApplyAffineTransform()与UIView的CGAffineTransform一起使用来以这种方式进行轮换。

答案 2 :(得分:44)

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

Swift 2

let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(1, 1)
self.frame = oldFrame

Swift 3

let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 1, y: 1)
self.frame = oldFrame

然后我进行调整大小,从anchorPoint扩展。 然后我必须恢复旧的anchorPoint;

Swift 2

let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(0.5,0.5)
self.frame = oldFrame

Swift 3

let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.frame = oldFrame

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

答案 3 :(得分:26)

对于我来说,当我开始将其与我对UIView中frame.origin的理解进行比较时,理解positionanchorPoint是最简单的。带有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),因此距离计算点位于图层的中心。下图将有助于澄清我的观点:

enter image description here

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

答案 4 :(得分:16)

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

- (void) setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view {
   CGPoint oldOrigin = view.frame.origin;
   view.layer.anchorPoint = anchorPoint;
   CGPoint newOrigin = view.frame.origin;

   CGPoint transition;
   transition.x = newOrigin.x - oldOrigin.x;
   transition.y = newOrigin.y - oldOrigin.y;

   view.center = CGPointMake (view.center.x - transition.x, view.center.y - transition.y);
}

Swift版本:

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

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

   view.center = CGPoint(x: view.center.x - transition.x, y: view.center.y - transition.y)
}

答案 5 :(得分:10)

对于那些需要它的人来说,这是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
}

答案 6 :(得分:5)

这是在OS X上针对NSView进行了user945711's answer调整。除了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)
}

答案 7 :(得分:5)

编辑并在故事板上看到UIView的锚点(Swift 3)

这是一种替代解决方案,允许您通过“属性”检查器更改锚点,并具有另一个属性以查看锚点以进行确认。

创建要包含在项目中的新文件

import UIKit

@IBDesignable
class UIViewAnchorPoint: UIView {

    @IBInspectable var showAnchorPoint: Bool = false
    @IBInspectable var anchorPoint: CGPoint = CGPoint(x: 0.5, y: 0.5) {
        didSet {
            setAnchorPoint(anchorPoint: anchorPoint)
        }
    }

    override func draw(_ rect: CGRect) {
        if showAnchorPoint {
            let anchorPointlayer = CALayer()
            anchorPointlayer.backgroundColor = UIColor.red.cgColor
            anchorPointlayer.bounds = CGRect(x: 0, y: 0, width: 6, height: 6)
            anchorPointlayer.cornerRadius = 3

            let anchor = layer.anchorPoint
            let size = layer.bounds.size

            anchorPointlayer.position = CGPoint(x: anchor.x * size.width, y: anchor.y * size.height)
            layer.addSublayer(anchorPointlayer)
        }
    }

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

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

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

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

        layer.position = position
        layer.anchorPoint = anchorPoint
    }
}

将视图添加到故事板并设置自定义类

Custom Class

现在为UIView设置新锚点

Demonstration

启用“显示锚点”将显示一个红点,以便您可以更好地查看锚点在视觉上的位置。您可以随时关闭它。

在规划UIViews的变换时,这确实对我有帮助。

答案 8 :(得分:4)

如果你改变了anchorPoint,它的位置也会改变,除非你的原点是零点CGPointZero

position.x == origin.x + anchorPoint.x;
position.y == origin.y + anchorPoint.y;

答案 9 :(得分:2)

对于Swift 3:

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
}

答案 10 :(得分:1)

扩展马格努斯的伟大和优秀;彻底的答案,我已经创建了一个适用于子图层的版本:

-(void)setAnchorPoint:(CGPoint)anchorPoint forLayer:(CALayer *)layer
{
    CGPoint newPoint = CGPointMake(layer.bounds.size.width * anchorPoint.x, layer.bounds.size.height * anchorPoint.y);
    CGPoint oldPoint = CGPointMake(layer.bounds.size.width * layer.anchorPoint.x, layer.bounds.size.height * layer.anchorPoint.y);
    CGPoint position = layer.position;
    position.x -= oldPoint.x;
    position.x += newPoint.x;
    position.y -= oldPoint.y;
    position.y += newPoint.y;
    layer.position = position;
    layer.anchorPoint = anchorPoint;
}