Vraag Door het wijzigen van de ankerpunt van mijn CALayer wordt de weergave verplaatst


Ik wil de veranderen anchorPoint, maar houd het beeld op dezelfde plaats. Ik heb geprobeerd NSLog-ing self.layer.position en self.center en ze blijven allebei hetzelfde ongeacht veranderingen in de anchorPoint. Toch beweegt mijn mening!

Tips over hoe dit te doen?

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

De uitvoer is:

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

118
2017-12-28 04:49


oorsprong


antwoorden:


De Laaggeometrie en transformaties In het gedeelte van de Core Programming Guide voor animaties wordt de relatie uitgelegd tussen de positie van een CALayer en anchorPoint-eigenschappen. In principe wordt de positie van een laag gespecificeerd in termen van de locatie van de anchorPoint van de laag. Standaard is de ankerpunt van een laag (0,5, 0,5), die in het midden van de laag ligt. Wanneer u de positie van de laag instelt, stelt u de locatie van het midden van de laag in het coördinatensysteem van de superlaag in.

Omdat de positie relatief is ten opzichte van de anchorPoint van de laag, wordt de laag verplaatst door dat ankerpunt te veranderen terwijl dezelfde positie wordt behouden. Om deze beweging te voorkomen, moet u de positie van de laag aanpassen om rekening te houden met de nieuwe anchorPoint. Eén manier waarop ik dit heb gedaan is door de grenzen van de laag te halen, de grenzen en de hoogte van de grenzen te vermenigvuldigen met de genormaliseerde waarden van de oude en nieuwe anchorPoint, het verschil te maken tussen de twee ankerpunten en dat verschil toe te passen op de positie van de laag.

Je zou zelfs in staat kunnen zijn om rotatie op deze manier te verklaren door te gebruiken CGPointApplyAffineTransform() met de CGAffineTransform van uw UIView.


129
2017-12-28 21:44



Ik had hetzelfde probleem. De oplossing van Brad Larson werkte prima, zelfs als het beeld is gedraaid. Hier is zijn oplossing vertaald in code.

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

En het snelle equivalent:

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
}

186
2018-04-14 16:28



De sleutel om dit op te lossen was om de eigenschap frame te gebruiken, wat vreemd genoeg het enige is dat verandert.

Snel 2

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

Snel 3

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

Dan doe ik mijn grootte, waar het schalen van de anchorPoint. Dan moet ik het oude ankerpunt herstellen;

Snel 2

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

Snel 3

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

EDIT: deze vloeit uit als het beeld geroteerd wordt, omdat de eigenschap frame niet gedefinieerd is als een CGAffineTransform is toegepast.


41
2017-12-28 07:41



Voor mij begrip position en anchorPoint was het gemakkelijkst toen ik het begon te vergelijken met mijn kennis van frame.origin in UIView. Een UIView met frame.origin = (20,30) betekent dat de UIView 20 punten van links en 30 punten vanaf de bovenkant van de bovenliggende weergave is. Deze afstand wordt berekend vanaf welk punt van een UIView? Het wordt berekend vanaf de linkerbovenhoek van een UIView.

In laag anchorPoint markeert het punt (in genormaliseerde vorm, d.w.z. 0 tot 1) van waaruit deze afstand wordt berekend, b.v. layer.position = (20, 30) betekent dat de laag anchorPoint is 20 punten van links en 30 punten van de top van de bovenliggende laag. Standaard is een laagankerpunt (0,5, 0,5), dus het afstandsberekeningspunt bevindt zich precies in het midden van de laag. De volgende afbeelding zal mijn punt verduidelijken:

enter image description here

anchorPoint is ook het punt waarrond rotatie zal plaatsvinden in het geval u een transformatie op de laag toepast.


24
2018-03-05 04:18



Er is zo'n eenvoudige oplossing. Dit is gebaseerd op Kenny's antwoord. Maar in plaats van het oude kader toe te passen, gebruikt u de oorsprong en de nieuwe om de overgang te berekenen en vervolgens die overgang naar het midden toe te passen. Het werkt ook met geroteerde weergave! Hier is de code, een stuk eenvoudiger dan andere oplossingen:

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

En de Swift-versie:

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

14
2017-08-12 14:35



Voor degenen die het nodig hebben, hier is de oplossing van Magnus in 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
}

8
2017-10-27 22:55



Bewerk en bekijk het ankerpunt van UIView direct op het storyboard (Swift 3)

Dit is een alternatieve oplossing waarmee u het ankerpunt via het hulpprogramma Kenmerken inspecteren kunt wijzigen en een andere eigenschap heeft om het ankerpunt ter bevestiging te bekijken.

Maak een nieuw bestand om op te nemen in uw project

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

Voeg View to Storyboard toe en stel de Custom Class in

Custom Class

Stel nu het nieuwe ankerpunt voor de UIView in

Demonstration

Als u het ankerpunt weergeven inschakelt, wordt een rode stip weergegeven, zodat u beter kunt zien waar het ankerpunt visueel zal zijn. Je kunt het later altijd uitschakelen.

Dit heeft me echt geholpen bij het plannen van transformaties op UIViews.


5
2018-01-16 11:41



Hier is user945711's antwoord aangepast voor NSView op OS X. Daarnaast heeft NSView geen .center eigenschap, het frame van de NSView verandert niet (waarschijnlijk omdat NSViews standaard niet met een CALayer wordt geleverd) maar de oorsprong van het CALayer-frame verandert wanneer het ankerpunt wordt gewijzigd.

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

5
2018-03-26 18:25