Swift 4, macOS 10.13

He leído una variedad de respuestas sobre SO y todavía no puedo obtener un NSImageView para girar en su centro en lugar de una de sus esquinas.

En este momento, la imagen se ve así ( video ): http://d.pr / v / kwiuwS

Aquí está mi código:

//`loader` is an NSImageView on my storyboard positioned with auto layout

loader.wantsLayer = true

let oldFrame = loader.layer?.frame
loader.layer?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
loader.layer?.position = CGPoint(x: 0.5, y: 0.5)
loader.layer?.frame = oldFrame!

let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(-1 * .pi * 2.0)
rotateAnimation.duration = 2
rotateAnimation.repeatCount = .infinity

loader.layer?.add(rotateAnimation, forKey: nil)

¿Alguna idea de lo que aún me falta?

1
Clifton Labrum 22 oct. 2017 a las 09:15

3 respuestas

La mejor respuesta

Acabo de crear una demostración simple que contiene la práctica extensión setAnchorPoint para todas las vistas.

La razón principal por la que ve su rotación desde una esquina es que su punto de anclaje se restablece de alguna manera a 0,0.

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!
var imageView: NSImageView!

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Insert code here to initialize your application

    // Create red NSImageView
    imageView = NSImageView(frame: NSRect(x: 100, y: 100, width: 100, height: 100))
    imageView.wantsLayer = true
    imageView.layer?.backgroundColor = NSColor.red.cgColor

    window.contentView?.addSubview(imageView)
}

func applicationWillTerminate(_ aNotification: Notification) {
    // Insert code here to tear down your application
}

func applicationDidBecomeActive(_ notification: Notification) {
    // Before animate, reset the anchor point
    imageView.setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 0.5))
    // Start animation
    if imageView.layer?.animationKeys()?.count == 0 || imageView.layer?.animationKeys() == nil {
        let rotate = CABasicAnimation(keyPath: "transform.rotation")
        rotate.fromValue = 0
        rotate.toValue = CGFloat(-1 * .pi * 2.0)
        rotate.duration = 2
        rotate.repeatCount = Float.infinity

        imageView.layer?.add(rotate, forKey: "rotation")
    }
}
}

extension NSView {
    func setAnchorPoint(anchorPoint:CGPoint) {
        if let layer = self.layer {
            var newPoint = NSPoint(x: self.bounds.size.width * anchorPoint.x, y: self.bounds.size.height * anchorPoint.y)
            var oldPoint = NSPoint(x: self.bounds.size.width * layer.anchorPoint.x, y: self.bounds.size.height * layer.anchorPoint.y)

        newPoint = newPoint.applying(layer.affineTransform())
        oldPoint = oldPoint.applying(layer.affineTransform())

        var position = layer.position

        position.x -= oldPoint.x
        position.x += newPoint.x

        position.y -= oldPoint.y
        position.y += newPoint.y


        layer.anchorPoint = anchorPoint
        layer.position = position
    }
}
}

enter image description here

7
brianLikeApple 31 oct. 2018 a las 10:34

Como me preguntaba muchas veces sobre esta pregunta, aquí está mi método simple para rotar cualquier NSView. Lo publico también como recordatorio. Se puede definir en una categoría si es necesario.

Esta es una rotación simple, no una animación continua. Debe aplicarse a una instancia de NSView con wantsLayer = YES.

- (void)rotateByNumber:(NSNumber*)angle {
    self.layer.position = CGPointMake(NSMidX(self.frame), NSMidY(self.frame));
    self.layer.anchorPoint = CGPointMake(.5, .5);
    self.layer.affineTransform = CGAffineTransformMakeRotation(angle.floatValue);
}
1
Max_B 2 jun. 2019 a las 10:13

Este es el resultado de un pase de diseño que restablece la capa de su vista a las propiedades predeterminadas. Si marca el anchorPoint de su capa, por ejemplo, probablemente se restablezca a 0, 0.

Una solución simple es establecer continuamente las propiedades de capa deseadas en viewDidLayout() si está en un controlador de vista. Básicamente, haciendo el frame, anchorPoint y el baile de posición que haces en tu configuración inicial en cada pase de diseño. Si subclasificó NSImageView, probablemente podría contener esa lógica dentro de esa vista, lo que sería mucho mejor que poner esa lógica en un controlador de vista que contiene.

Es probable que haya una mejor solución anulando la capa de respaldo o rodando su propia subclase NSView que utiliza updateLayer, pero tendría que experimentar allí para dar una respuesta definitiva.

0
Lucas Derraugh 22 oct. 2017 a las 18:53