prosource

내 CALayer의 앵커포인트를 변경하면 보기가 이동합니다.

probook 2023. 6. 2. 20:38
반응형

내 CALayer의 앵커포인트를 변경하면 보기가 이동합니다.

변경하고 싶습니다.anchorPoint하지만 같은 장소에서 경치를 유지합니다.해습다봤니다니.NSLogself.layer.position그리고.self.center앵커포인트 변경에 상관없이 둘 다 동일하게 유지됩니다.하지만 내 시야는 움직여요!

이거 하는 방법에 대한 조언이 있나요?

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

저도 같은 문제가 있었습니다.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
}

Core Animation Programming Guide의 Layer Geometry and Transforms 섹션에서는 CALayer의 위치와 AnchorPoint 속성 간의 관계를 설명합니다.기본적으로 도면층의 위치는 도면층의 앵커포인트 위치로 지정됩니다.기본적으로 도면층의 앵커포인트는 (0.5, 0.5)이며, 도면층의 중심에 위치합니다.도면층의 위치를 설정하면 수퍼 도면층의 좌표계에서 도면층 중심의 위치를 설정하는 것입니다.

위치는 도면층의 앵커포인트에 상대적이므로 동일한 위치를 유지하면서 앵커포인트를 변경하면 도면층이 이동합니다.이 이동을 방지하려면 새 앵커포인트를 고려하도록 도면층의 위치를 조정해야 합니다.한 가지 방법은 도면층의 경계를 잡고, 경계의 너비와 높이에 기존 및 새 앵커포인트의 정규화된 값을 곱한 다음, 두 앵커포인트의 차이를 가져와 도면층의 위치에 적용하는 것입니다.

이러한 방식으로 회전을 설명할 수도 있습니다.CGPointApplyAffineTransform()UIView의 CGAfine Transform을 사용합니다.

이 문제를 해결하기 위한 열쇠는 프레임 속성을 사용하는 것이었는데, 이상하게도 유일하게 변경되는 것입니다.

스위프트 2

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

스위프트 3

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

그런 다음 크기를 조정하여 앵커포인트에서 크기를 조정합니다.그럼 예전 앵커포인트를 복구해야 합니다.

스위프트 2

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

스위프트 3

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

편집: 보기가 회전하면 프레임 속성이 정의되지 않으므로 CGAfineTransform이 적용된 경우 이 조각이 표시됩니다.

는 이해하기 위해 를나위이해한해이.position그리고.anchorPointframeUIView frame.origin에 했을 때 .frameframe = (이 있는 , view.frame 합니다.view = (20,30)가 있는 UIView는 UIView가 왼쪽에서 20포인트, 상위 뷰에서 30포인트임을 의미합니다.이 거리는 UI 뷰의 어느 지점에서 계산됩니까?이 값은 UI 보기의 왼쪽 상단 모서리에서 계산됩니다.

인이어에서anchorPoint이 거리가 계산되는 지점(예: 0 ~ 1)을 표시합니다. 예: 레이어. 위치 = (20, 30)은 레이어를 의미합니다.anchorPoint왼쪽에서 20점, 상위 레이어에서 30점입니다.기본적으로 도면층 앵커포인트는 (0.5, 0.5)이므로 거리 계산점은 도면층의 오른쪽 중앙에 있습니다.다음 그림은 제 요점을 명확히 하는 데 도움이 될 것입니다.

여기에 이미지 설명 입력

anchorPoint또한 레이어에 변환을 적용할 경우 회전이 발생하는 점이 됩니다.

이렇게 간단한 해결책이 있습니다.이것은 케니의 대답을 바탕으로 한 것입니다.하지만 이전 프레임을 적용하는 대신, 그것의 원점과 새로운 프레임을 사용하여 번역을 계산하고 그 번역을 중앙에 적용합니다.회전된 보기에서도 작동합니다!다른 솔루션보다 훨씬 간단한 코드는 다음과 같습니다.

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)
}

필요한 사용자를 위해 Swift에 있는 Magnus의 솔루션은 다음과 같습니다.

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
}

OS X에서 NSView용으로 조정된 user945711의 답변입니다.NSView가 없는 것 외에도.center속성. NSView의 프레임은 변경되지 않지만(기본적으로 NSView에는 CALayer가 함께 제공되지 않기 때문일 수 있음) 앵커포인트가 변경되면 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)
}

anchorPoint가 아닌 한 됩니다.CGPointZero.

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

스토리보드에서 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
    }
}

스토리보드에 보기 추가 및 사용자 지정 클래스 설정

사용자 지정 클래스

이제 UIView의 새 앵커 포인트를 설정합니다.

시연

앵커 포인트 표시를 켜면 빨간색 점이 표시되므로 앵커 포인트의 위치를 시각적으로 더 잘 볼 수 있습니다.나중에 언제든지 끌 수 있습니다.

이것은 UIViews에서 변환을 계획할 때 도움이 되었습니다.

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
}

Magnus의 훌륭하고 철저한 답변을 확장하여 하위 계층에서 작동하는 버전을 만들었습니다.

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

a를 앵커포인트 좌표(0.5, 0.5)의 관점에서 도면층의 중심으로 합니다.금속/Vulkan/OpenGL/DirectX에 익숙한 경우 단위 좌표/또는 UV 좌표를 생각합니다.n을 새 앵커 위치(x, y)로 합니다.레이어 경계의 치수(폭, 높이)로 합니다.

그런 다음 도면층 앵커포인트 위치 변경 후 도면층을 원래 위치로 다시 이동하려면 t에 d: v = d(n-a)를 곱한 값에서 오프셋 벡터를 계산합니다.그런 다음 레이어를 원래 위치로 되돌리려면 레이어.position: 레이어.position += v에 오프셋 벡터를 추가합니다.

스위프트/iOS

let vx = (layer.anchorPoint.x - 0.5) * layer.bounds.width
let vy = (layer.anchorPoint.y - 0.5) * layer.bounds.height
layer.position.x += vx
layer.position.y += vy

도면층이 뷰의 지원 도면층인 경우 도면층.위치가 변경될 때 view.center가 이동합니다(또는 그 반대의 경우도 마찬가지입니다.하위 레이어.position이면 view.center가 이동하지 않습니다.따라서 위의 코드에서 레이어가 뷰의 지원 레이어였다면 레이어.position 대신 view.center를 오프셋할 수 있었을 것입니다. 둘 다 작동했을 것입니다.

언급URL : https://stackoverflow.com/questions/1968017/changing-my-calayers-anchorpoint-moves-the-view

반응형