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

Я хочу изменить anchorPoint , но сохраняю представление в том же месте. Я пробовал NSLog -ing self.layer.position и self.center и они оба остаются неизменными независимо от изменений в anchorPoint. Но мой взгляд движется!

Любые советы о том, как это сделать?

 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 

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

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

Вы даже можете учесть ротацию таким образом, используя 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 

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

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

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

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

введите описание изображения здесь

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

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

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

Для тех, кто в ней нуждается, вот решение Магнуса в Свифт:

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

Добавить изображение в раскадровку и установить пользовательский class

Пользовательский класс

Теперь установите новую точку привязки для UIView

демонстрация

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

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

Вот ответ пользователя945711, настроенный для NSView в OS X. Помимо NSView, не имеющего свойства .center, кадр NSView не изменяется (вероятно, потому, что NSViews не имеют CALayer по умолчанию), но исходный кадр CALayer изменяется, когда anchorPoint изменилось.

 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; 

Для 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; } 
  • Как добавить представление UIViewController в качестве подзадачи
  • «Неверный» размер кадра / windows после переориентации в iPhone
  • Лучший способ выполнить несколько последовательных анимаций UIView?
  • UIView animateWithDuration не изменяет параметр cornerRadius
  • Разница между addSubview и insertSubview в classе UIView
  • Подclass UIView со своим XIB
  • Округленный UIView с использованием CALayers - только некоторые углы - как?
  • Какая связь между установками UIViewNeedsLayout, layoutIfNeeded и layoutSubviews?
  • Ориентация в UIView добавлена ​​в UIWindow
  • Как я могу имитировать нижний лист из приложения «Карты»?
  • Как сделать круговой UIView
  • Давайте будем гением компьютера.