Изменение моего привязки 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
Ответы
Ответ 1
Раздел Layer Geometry and Transforms в Руководстве по программированию основной анимации объясняет взаимосвязь между позицией CALayer и свойствами anchorPoint. В принципе, положение слоя определяется с точки зрения местоположения слоя anchorPoint. По умолчанию уровень anchorPoint слоя (0.5, 0.5), который лежит в центре слоя. Когда вы устанавливаете положение слоя, вы затем устанавливаете расположение центра слоя в своей системе координат суперслоя.
Поскольку позиция относительно привязанного уровня слоя, изменение этого anchorPoint при сохранении одной и той же позиции перемещает слой. Чтобы предотвратить это движение, вам нужно будет отрегулировать позицию слоя для учета нового anchorPoint. Один из способов, которым я это сделал, - захватить границы слоев, умножить ширину и высоту границ на старые и новые нормализованные значения anchorPoint, различить два якорных точки и применить эту разницу к положению слоя.
Возможно, вы даже сможете учитывать ротацию таким образом, используя CGPointApplyAffineTransform()
с вашим UIView CGAffineTransform.
Ответ 2
У меня такая же проблема. Решение Брэда Ларсона отлично работало даже при повороте вида. Вот его решение, переведенное в код.
-(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
}
Ответ 3
Ключом к решению этой проблемы было использование свойства 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
ОБНОВЛЕНИЕ: это исчезает, если представление поворачивается, поскольку свойство кадра не определено, если применено преобразование CGAffineTransform.
Ответ 4
Для меня понимание 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), поэтому точка вычисления расстояния находится прямо в центре слоя. Следующий рисунок поможет прояснить мою мысль:
![enter image description here]()
anchorPoint
также оказывается точкой, вокруг которой произойдет поворот, если вы примените преобразование к слою.
Ответ 5
Есть такое простое решение. Это основано на ответе Кенни. Но вместо применения старого фрейма используйте его origin и новый для вычисления перевода, а затем примените этот перевод к центру. Работает с повернутым видом тоже! Вот код, намного проще, чем другие решения:
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)
}
Ответ 6
Для тех, кому это нужно, вот решение Магнуса в 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
}
Ответ 7
Вот user945711 ответ, настроенный для 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)
}
Ответ 8
Отредактируйте и увидите опорную точку 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]()
Включение "Показать опорную точку" покажет красную точку, чтобы вы могли лучше видеть, где опорная точка будет визуально. Вы всегда можете выключить его позже.
Это действительно помогло мне при планировании преобразований в UIViews.
Ответ 9
Если вы измените anchorPoint, его позиция тоже изменится, ЕСЛИ вы не являетесь нулевой точкой CGPointZero
.
position.x == origin.x + anchorPoint.x;
position.y == origin.y + anchorPoint.y;
Ответ 10
Для 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
}
Ответ 11
Развернувшись на большом и тщательном ответе Магнуса, я создал версию, которая работает на подслоях:
-(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;
}