将NSImageView以其中心旋转,使其旋转

4

Swift 4,macOS 10.13

我已经看过很多SO上的答案,但仍然无法让NSImageView在其中心旋转而不是在其中一个角落。

现在,图像看起来像这样(视频):http://d.pr/v/kwiuwS

这是我的代码:

//`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)

有任何想法我仍然缺少什么吗?

你找到解决方案了吗? - Witterquick
@Roee84 不,我想我最终放弃了。 :) - Clifton Labrum
LOL :) 如果我找到了什么,我会更新的。 - Witterquick
@CliftonLabrum 我更新了我的答案,现在应该可以工作了。 - brianLikeApple
1
我说过我会更新的 - @CliftonLabrum 的答案非常好,只要确保将其写在“ViewWill...”或“ViewDid...”函数之外即可。 - Witterquick
3个回答

11

我刚刚创建了一个简单的演示,其中包含方便的setAnchorPoint扩展,适用于所有视图。

你看到角度是从某个角落开始旋转的主要原因是你的锚点在某种程度上被重置为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
    }
}
}

输入图像描述


1
@Roee84 只需创建新项目并复制我的代码,它应该像 gif 中所示一样正常工作。 - brianLikeApple
1
@Roee84 我更新了如何使用这个扩展的代码。现在应该可以工作了。我不知道这里发生了什么事,之前它是可以工作的...但无论如何,这个扩展是正确的,并且在我的软件中使用没有任何问题。谢谢。 - brianLikeApple
2
@Roee84 我不确定...新版本只是将图像初始化代码和旋转代码分开,但仍使用相同的扩展名。如果你初始化视图,有时图层的锚点会重置为0,0,即使你将其设置为0.5,0.5。因此,在整个布局安定下来后,我们需要使用扩展代码。 - brianLikeApple
1
太棒了!我猜你的评论是正确的,因为当我将它移到另一个函数时,它就可以工作了。谢谢! - Witterquick
1
谢谢!这帮了很多忙。所以设置锚点必须在动画之前发生。 - Tualatrix Chou
显示剩余8条评论

2

我自己也曾经多次思考这个问题,下面是我自己的简单方法来旋转任何NSView。我也将其发布为自我提醒。如果需要,可以在类别中定义它。

这是一个简单的旋转,不是连续的动画。应该应用于具有wantsLayer = YES的NSView实例。

Translated:

我自己也曾经多次思考这个问题,下面是我自己的简单方法来旋转任何NSView。我也将其发布为自我提醒。如果需要,可以在类别中定义它。

这是一个简单的旋转,不是连续的动画。应该应用于具有wantsLayer = YES的NSView实例。

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

谢谢!这确实是一个简单而非常有帮助的解决方案! - VYT

1
这是布局过程中重置视图层为默认属性的结果。如果您检查您的层的anchorPoint,您会发现它可能被重置为0, 0
一个简单的解决方案是在viewDidLayout()中不断设置所需的层属性,如果您在一个视图控制器中。基本上,在每个布局过程中执行框架、anchorPoint和位置操作,就像您在初始设置中所做的那样。如果您子类化了NSImageView,您可能可以将该逻辑包含在该视图中,这比将该逻辑放在包含视图控制器中要好得多。
可能有更好的解决方案,例如覆盖支持层或滚动自己的NSView子类,但我需要进行实验才能给出明确的答案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接