Zmiana punktu kotwiczenia mojego Calayera przesuwa widok

Chcę zmienić anchorPoint, ale zachować widok w tym samym miejscu. Próbowałem NSLog - ing self.layer.position i self.center i oba pozostają takie same, niezależnie od zmian w punkcie kontrolnym. A jednak mój Widok się porusza!

Jakieś wskazówki jak to zrobić?

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

Wyjście to:

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
Author: rounak, 2009-12-28

11 answers

Sekcja Geometria warstwy i transformacje podręcznika programowania animacji rdzenia wyjaśnia związek między pozycją Caleyera a właściwościami punktu kotwiczenia. Zasadniczo, położenie warstwy jest określone w kategoriach położenia punktu kotwiczenia warstwy. Domyślnie punktem kontrolnym warstwy jest (0.5, 0.5), który znajduje się na środku warstwy. Po ustawieniu położenia warstwy ustawia się położenie środka warstwy w jej superlayer ' s układ współrzędnych.

Ponieważ pozycja jest względem punktu kontrolnego warstwy, zmiana tego punktu kontrolnego przy zachowaniu tej samej pozycji przesuwa warstwę. Aby zapobiec temu ruchowi, należy dostosować położenie warstwy, aby uwzględnić nowy punkt kontrolny. Jednym ze sposobów, w jaki to zrobiłem, jest pobranie obramowań warstwy, pomnożenie szerokości i wysokości obramowań przez znormalizowane wartości Starego i nowego punktu kotwiczenia, wzięcie różnicy dwóch punktów kotwiczenia i zastosowanie tego różnica w stosunku do położenia warstwy.

Możesz nawet być w stanie rozliczyć rotację w ten sposób, używając CGPointApplyAffineTransform() z CGAffineTransform UIView.

 140
Author: Brad Larson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-11 16:14:41

Miałem ten sam problem. Rozwiązanie Brada Larsona działało świetnie, nawet gdy widok jest obracany. Oto jego rozwiązanie przetłumaczone na kod.

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

I odpowiednik swift:

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
}
 205
Author: Magnus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-22 12:34:39

Kluczem do rozwiązania tego problemu było użycie właściwości frame, która jest dziwną jedyną rzeczą, która się zmienia.

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

Potem zmieniam Rozmiar, gdzie skaluje się od punktu kontrolnego. W takim razie muszę przywrócić stary punkt kontrolny;

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

EDIT: ta opcja jest wyłączona, jeśli widok jest obrócony, ponieważ właściwość frame jest niezdefiniowana, jeśli CGAffineTransform został zastosowany.

 47
Author: Kenny Winker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-03 15:13:27

Dla mnie zrozumienie position i anchorPoint było najłatwiejsze, kiedy zacząłem porównywać je z moim zrozumieniem kadru.pochodzenie w UIView. UIView z ramką.origin = (20,30) oznacza, że UIView ma 20 punktów od lewej i 30 punktów od góry widoku nadrzędnego. Odległość ta jest obliczana z którego punktu UIView? Jego obliczona od lewego górnego rogu Ujazdu.

W warstwie anchorPoint oznacza punkt (w postaci znormalizowanej tj. 0 do 1), od którego odległość ta jest obliczana tak np. warstwa.position = (20, 30) oznacza, że warstwa anchorPoint znajduje się 20 punktów od lewej i 30 punktów od góry jej warstwy nadrzędnej. Domyślnie punktem kontrolnym warstwy jest (0.5, 0.5), więc punkt obliczania odległości znajduje się na środku warstwy. Poniższy rysunek pomoże wyjaśnić mój punkt:

Tutaj wpisz opis obrazka

anchorPoint zdarza się również, że punkt, wokół którego nastąpi obrót, jeśli zastosujesz transformację do warstwy.

 28
Author: 2cupsOfTech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-05 04:18:07

Jest takie proste rozwiązanie. Opiera się to na odpowiedzi Kenny ' ego. Ale zamiast nakładać starą ramkę, użyj jej pochodzenia i Nowej, aby obliczyć tłumaczenie, a następnie zastosuj to tłumaczenie do środka. Działa również z obracanym widokiem! Oto kod, dużo prostszy od innych rozwiązań:

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)
}
 17
Author: Fried Rice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-21 01:12:37

Dla tych, którzy tego potrzebują, oto rozwiązanie Magnusa w języku 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
}
 16
Author: Corey Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-27 22:55:02

Oto odpowiedź user945711 dostosowana dla NSView na OS X. poza tym, że nsview nie ma właściwości .center, ramka NSView nie zmienia się (prawdopodobnie dlatego, że nsview nie jest domyślnie wyposażona w funkcję Kalayer), Ale pochodzenie Ramki kalayer zmienia się po zmianie punktu kotwiczenia.

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
Author: iMaddin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-20 18:50:49

Jeśli zmienisz punkt kontrolny, jego pozycja również się zmieni, chyba że masz punkt zerowy CGPointZero.

position.x == origin.x + anchorPoint.x;
position.y == origin.y + anchorPoint.y;
 4
Author: user3156083,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 06:00:17

Edytuj i zobacz punkt kontrolny UIView tuż na Storyboardzie (Swift 3)

Jest to alternatywne rozwiązanie, które pozwala zmienić punkt kontrolny za pomocą Inspektora atrybutów i ma inną właściwość, aby wyświetlić punkt kontrolny w celu potwierdzenia.

Utwórz nowy plik do umieszczenia w projekcie

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

Dodaj widok do Storyboard i ustaw klasę niestandardową

Klasa Niestandardowa

Teraz ustaw nowy punkt kontrolny dla UIView

Demonstracja

Włączenie Pokaż punkt kontrolny pokaże czerwoną kropkę, dzięki czemu można lepiej zobaczyć, gdzie punkt kontrolny będzie wizualnie. Zawsze możesz go wyłączyć później.

To naprawdę pomogło mi podczas planowania przekształceń w UIViews.

 4
Author: Mark Moeykens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-16 12:19:15

Dla 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
}
 1
Author: AJ9,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-26 15:43:15

Rozbudowując świetną i dokładną odpowiedź Magnusa, stworzyłem wersję, która działa na podwarstwach:

-(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
Author: Charles Robertson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-23 17:56:48