Изменение точки привязки моего CALayer перемещает представление



Я хочу изменить anchorPoint, но держите вид в том же месте.
Я пробовал NSLog - ing self.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
714   11  

11 ответов:

на геометрия слоя и преобразования раздел руководства по программированию основной анимации объясняет связь между положением CALayer и свойствами точки привязки. В основном, положение слоя определяется с точки зрения расположения точки привязки слоя. По умолчанию точкой привязки слоя является (0.5, 0.5), которая находится в центре слоя. Когда вы устанавливаете положение слоя, вы затем устанавливаете местоположение центра слоя в его система координат суперслоя.

поскольку положение относительно точки привязки слоя, изменение этой точки привязки при сохранении того же положения перемещает слой. Чтобы предотвратить это перемещение, вам нужно будет настроить положение слоя для учета новой точки привязки. Один из способов, которым я это сделал, - захватить границы слоя, умножить ширину и высоту границ на нормализованные значения старой и новой опорной точки, взять разницу между ними точки привязки и применить эту разницу к положению слоя.

вы даже можете быть в состоянии учитывать вращение таким образом, используя CGPointApplyAffineTransform() с помощью CGAffineTransform вашего UIView.

у меня была та же проблема. Решение Брэда Ларсона отлично работало даже при повороте вида. Вот его решение переведено в код.

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

ключом к решению этой проблемы было использование свойства 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

затем я делаю свой размер, где он масштабируется от точки привязки. Затем я должен восстановить старую точку привязки;

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: это отслаивается, если вид повернут, поскольку свойство frame не определено, если была применена CGAffineTransform.

для меня понимание position и anchorPoint было проще всего, когда я начал сравнивать его с моим пониманием фрейма.происхождение в UIView. UIView с рамкой.origin = (20,30) означает, что UIView-это 20 точек слева и 30 точек сверху родительского вида. Это расстояние вычисляется из какой точки UIView? Его вычисляют из верхнего левого угла UIView.

в слое anchorPoint обозначает точку (в нормализованном виде, т. е. от 0 до 1), из которой вычисляется это расстояние так, например, слой.позиция = (20, 30) означает, что слой anchorPoint - это 20 точек слева и 30 точек сверху родительского слоя. По умолчанию точка привязки слоя равна (0.5, 0.5), поэтому точка расчета расстояния находится прямо в центре слоя. Следующий рисунок поможет прояснить мою точку зрения:

enter image description here

anchorPoint также является точкой, вокруг которой произойдет вращение, если вы примените преобразование к слою.

есть такое простое решение. Это основано на ответе Кенни. Но вместо того, чтобы применять старый кадр, используйте его происхождение и новые для вычисления перехода, а затем примените этот переход к центру. Он работает с повернутым видом тоже! Вот код, намного проще, чем другие решения:

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

для тех, кому это нужно, вот решение Магнуса в 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
}

редактировать и видеть точку привязки 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

включение точки привязки Show покажет красную точку, чтобы вы могли лучше видеть, где точка привязки будет визуально. Вы всегда можете отключить его позже.

Это действительно помогло мне при планировании преобразований на UIViews.

здесь user945711 это С поправкой на NSView на OS X. Кроме того, NSView не имеет .center свойство, кадр NSView не изменяется (вероятно, потому, что NSViews не поставляются с 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)
}

Если вы измените точку привязки, ее положение тоже изменится, если вы не будете исходить из нулевой точки CGPointZero.

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

Для 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
}

расширяя великий и тщательный ответ Магнуса, я создал версию, которая работает на подслоях:

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

Comments

    Ничего не найдено.