wake-up-neo.com

Wenn Sie den Ankerpunkt meines CALayers ändern, wird die Ansicht verschoben

Ich möchte die anchorPoint ändern, aber die Ansicht an derselben Stelle belassen. __ Ich habe NSLoging self.layer.position und self.center ausprobiert, und beide bleiben unabhängig von Änderungen am anchorPoint gleich. Doch meine Sicht bewegt sich!

Irgendwelche Tipps, wie das geht?

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

Die Ausgabe ist:

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
124
Kenny Winker

Im Abschnitt Layer Geometry and Transforms des Programmierhandbuchs für Core-Animation wird die Beziehung zwischen der Position eines CALayers und den Ankerpunkt-Eigenschaften erläutert. Grundsätzlich wird die Position einer Ebene als Position des Ankerpunkts der Ebene angegeben. Standardmäßig ist der Ankerpunkt einer Ebene (0,5, 0,5) und liegt in der Mitte der Ebene. Wenn Sie die Position der Ebene festlegen, legen Sie die Position des Mittelpunkts der Ebene im Koordinatensystem des übergeordneten Layers fest.

Da sich die Position relativ zum Ankerpunkt der Ebene befindet, wird durch Ändern des Ankerpunkts bei gleicher Position die Ebene verschoben. Um diese Bewegung zu verhindern, müssen Sie die Position der Ebene anpassen, um den neuen Ankerpunkt zu berücksichtigen. Eine Möglichkeit, dies zu tun, besteht darin, die Grenzen der Ebene zu erfassen, die Breite und Höhe der Grenzen mit den normalisierten Werten des alten und des neuen Ankerpunkts zu multiplizieren, die Differenz der beiden Ankerpunkte zu nehmen und diese Differenz auf die Position der Ebene anzuwenden. 

Sie können sogar die Rotation auf diese Weise berücksichtigen, indem Sie CGPointApplyAffineTransform() mit der CGAffineTransform Ihrer UIView verwenden.

133
Brad Larson

Ich hatte das gleiche Problem. Die Lösung von Brad Larson hat auch dann gut funktioniert, wenn die Ansicht gedreht wird. Hier ist seine Lösung in Code übersetzt.

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

Und das schnelle Äquivalent:

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

Der Schlüssel zur Lösung dieses Problems bestand in der Verwendung der Frame-Eigenschaft, die sich seltsamerweise nur ändern kann.

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

Dann mache ich meine Größe, wo sie vom Ankerpunkt aus skaliert ... __ Dann muss ich den alten Ankerpunkt wiederherstellen;

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

BEARBEITEN: Blättert aus, wenn die Ansicht gedreht wird, da die Frame-Eigenschaft undefiniert ist, wenn eine CGAffineTransform angewendet wurde.

43
Kenny Winker

Für mich war das Verstehen von position und anchorPoint am einfachsten, als ich es mit meinem Verständnis von frame.Origin in UIView zu vergleichen begann. Ein UIView mit frame.Origin = (20,30) bedeutet, dass der UIView 20 Punkte von links und 30 Punkte von oben in der übergeordneten Ansicht ist. Diese Entfernung wird ab welchem ​​Punkt einer UIView berechnet? Es wird aus der oberen linken Ecke eines UIView berechnet. 

In der Schicht anchorPoint wird der Punkt (in normalisierter Form, d. H. 0 bis 1) markiert, von dem aus dieser Abstand berechnet wird, z. layer.position = (20, 30) bedeutet, dass die Ebene anchorPoint 20 Punkte von links und 30 Punkte von der obersten Ebene der übergeordneten Ebene entfernt ist. Standardmäßig ist ein Ankerpunkt der Ebene (0,5, 0,5). Der Entfernungsberechnungspunkt befindet sich also genau in der Mitte der Ebene. Die folgende Abbildung hilft, meinen Punkt zu verdeutlichen:

enter image description here

anchorPoint ist auch der Punkt, um den die Rotation ausgeführt wird, falls Sie eine Transformation auf die Ebene anwenden.

26
2cupsOfTech

Es gibt so eine einfache Lösung. Dies basiert auf Kennys Antwort. Verwenden Sie statt des alten Rahmens jedoch Origin und die neuen, um den Übergang zu berechnen, und wenden Sie diesen Übergang dann auf die Mitte an. Es funktioniert auch mit gedrehter Ansicht! Hier ist der Code, viel einfacher als andere Lösungen:

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

Und die Swift-Version:

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)
}
15
Fried Rice

Für diejenigen, die es brauchen, hier die Lösung von 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
}
9
Corey Davis

Bearbeiten und sehen Sie den Ankerpunkt von UIView direkt im Storyboard (Swift 3)

Dies ist eine alternative Lösung, mit der Sie den Ankerpunkt über den Attribut-Inspektor ändern können. Mit einer anderen Eigenschaft können Sie den Ankerpunkt zur Bestätigung anzeigen.

Erstellen Sie eine neue Datei, die in Ihr Projekt aufgenommen werden soll

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

Fügen Sie dem Storyboard eine Ansicht hinzu, und legen Sie die benutzerdefinierte Klasse fest

 Custom Class

Legen Sie nun den neuen Ankerpunkt für die UIView fest

 Demonstration

Wenn Sie den Ankerpunkt anzeigen aktivieren, wird ein roter Punkt angezeigt, damit Sie besser sehen können, wo der Ankerpunkt visuell sein wird. Sie können es später jederzeit ausschalten.

Das hat mir wirklich bei der Planung von Transformationen auf UIViews geholfen.

5
Mark Moeykens

Hier ist die Antwort von user945711 für NSView unter OS X angepasst. Abgesehen davon, dass NSView keine .center -Eigenschaft hat, ändert sich der Rahmen von NSView nicht (wahrscheinlich, weil NSViews standardmäßig nicht mit CALayer geliefert wird), aber der Ursprung von CALayer ändert sich Der Ankerpunkt wird geändert.

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
iMaddin

Wenn Sie den Ankerpunkt ändern, ändert sich auch seine Position. UNSERE Herkunft ist der Nullpunkt CGPointZero.

position.x == Origin.x + anchorPoint.x;
position.y == Origin.y + anchorPoint.y;
4
user3156083

Für 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
}
2
AJ9

Um Magnus 'großartige und gründliche Antwort zu erweitern, habe ich eine Version erstellt, die auf Unterebenen funktioniert:

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