2

SWIFT - OSX

I have a bunch of imageViews set in my Main.storyboard. I am trying to make them spin when the app starts and i would like them to indefinitely. I came across the roateByAngle(angle: CGFloat), but this doesn't animate it, instead it just jumps to the new angle.

I would like to make two functions, spinClockwise() and spinAntiClockwise() so I can just call them in the viewDidLoad and they will just keep turning.

Ive been playing with CATransform3DMakeRotation but cannot seem to get my desired results

        let width = myImg.frame.width / 2
        let height = myImg.frame.height / 2
        myImg.layer?.transform = CATransform3DMakeRotation(180,  width, height, 1)

Let me know if i can be more specific. Thanks

Steve
  • 4,372
  • 26
  • 37

2 Answers2

2

You could add an extension of UIView or UIImageView like this:

extension UIView {

    ///The less is the timeToRotate, the more fast the animation is !
    func spinClockwise(timeToRotate: Double) {
        startRotate(CGFloat(M_PI_2), timeToRotate: timeToRotate)
    }

    ///The less is the timeToRotate, the more fast the animation is !
    func spinAntiClockwise(timeToRotate: Double) {
        startRotate(CGFloat(-M_PI_2), timeToRotate: timeToRotate)
    }

    func startRotate(angle: CGFloat, timeToRotate: Double) {
        UIView.animateWithDuration(timeToRotate, delay: 0.0, options:[UIViewAnimationOptions.CurveLinear, UIViewAnimationOptions.Repeat], animations: {
            self.transform = CGAffineTransformMakeRotation(angle)
            }, completion: nil)
        print("Start rotating")
    }

    func stopAnimations() {
        self.layer.removeAllAnimations()
        print("Stop rotating")
    }
}

So when you want to rotate your myImg, you just have to call:

myImg.spinClockwise(3)

And when you want to stop it:

myImg.stopAnimations()

NOTE: I added a playground just so you can test it out ;)

Cheers!

EDIT:

My bad, Here is the example for NSView:

extension NSView {

    ///The less is the timeToRotate, the more fast the animation is !
    func spinClockwise(timeToRotate: Double) {
        startRotate(CGFloat(-1 * M_PI * 2.0), timeToRotate: timeToRotate)
    }

    ///The less is the timeToRotate, the more fast the animation is !
    func spinAntiClockwise(timeToRotate: Double) {
        startRotate(CGFloat(M_PI * 2.0), timeToRotate: timeToRotate)
    }

    func startRotate(angle: CGFloat, timeToRotate: Double) {

        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
        rotateAnimation.fromValue = 0.0
        rotateAnimation.toValue = angle
        rotateAnimation.duration = timeToRotate
        rotateAnimation.repeatCount = .infinity

        self.layer?.addAnimation(rotateAnimation, forKey: nil)

        Swift.print("Start rotating")
    }

    func stopAnimations() {
        self.layer?.removeAllAnimations()
        Swift.print("Stop rotating")
    }
}

Important note: Now, after my tests, I noticed that you must set the anchor point of your NSView in the middle so that it can rotate around its center:

view.layer?.anchorPoint = CGPointMake(0.5, 0.5)

I added a new playground with the OSX example

Mindhavok
  • 457
  • 4
  • 19
  • 1
    Ah yes this is the sort of thing i am after except it needs to be NSView though NSView doesn't have `animateWithDuration` . Any ideas? – Steve Apr 17 '16 at 09:03
  • 1
    @Steve: I updated my answer, let me know if it works for you ! – Mindhavok Apr 17 '16 at 10:08
  • 1
    ohhh soo close, the image is spinning but i can't get it into the centre of the frame where it was placed originally, `myImg.layer?.anchorPoint = CGPointMake(0.5, 0.5)` but its sort of moved to the bottom left – Steve Apr 17 '16 at 10:53
  • 1
    Did you also set its `layer?.position` ? – Mindhavok Apr 17 '16 at 11:02
  • 1
    `let x = view.frame.width / 2 let y = view.frame.height / 2 imgEnergyBall.layer?.anchorPoint = CGPointMake(0.5, 0.5) imgEnergyBall.layer?.position = CGPointMake(x, y)` I did this which takes it to the middle but it starts rotating about the bottom left point again =/ – Steve Apr 17 '16 at 11:07
  • Are you adding the image programatically ? Like `view.addsubView(imgEnergyBall)` – Mindhavok Apr 17 '16 at 11:10
  • No, i put it on the main.storyboard with constraints. Ill see if i can try do it that way – Steve Apr 17 '16 at 11:12
  • What on earth! I just ran it again with that little bit of code i posted and its working! perhaps Xcode has been running a bit too long? haha Thanks soo much for putting the time into helping me solve this – Steve Apr 17 '16 at 11:14
  • I have the exact problem @Steve . On MacOS it is spinning around (0,0) which is bottom left. I moved the anchor point to (0.5, 0.5), but still no luck. I even moved layer's position but again is not working. Can you please tell me what else I could do, if you know? – Burak Oct 18 '17 at 08:23
  • @Burak Maybe with a sample of your code, we could try to find a solution. – Mindhavok Oct 18 '17 at 12:48
  • @Mindhavok Thank you. I found a workaround by applying this answer into my code: https://stackoverflow.com/a/1968425/4369441. So weird that MacOS changes the anchor point only if you update the view's frame. – Burak Oct 19 '17 at 13:38
  • @Burak Can you share your complete code? Mine is still rotating around the wrong point. – Clifton Labrum Oct 21 '17 at 05:41
  • @CliftonLabrum I shared my code as an answer, you can find below :) – Burak Oct 23 '17 at 07:30
1

For me, I could not change the anchor point. It was spinning around (0,0) which is bottom left. I moved the anchor point to (0.5, 0.5), but still no luck. Then I came accross with this answer. I modified my code like below, and it begins to rotate around itself. I observed a drawback though, the place of the view somehow shifted, but it can be fixed by trial and error, trying to get it to the right place.

extension NSView {

    func startRotating(duration: Double = 1) {

        let kAnimationKey = "rotation"

        //self.wantsLayer = true
        if layer?.animation(forKey: kAnimationKey) == nil {
            var oldFrame = self.frame
            self.layer?.anchorPoint = CGPoint(x: 1, y: 1)
            self.frame = oldFrame

            let animate = CABasicAnimation(keyPath: "transform.rotation")
            animate.duration = duration
            animate.repeatCount = Float.infinity
            animate.fromValue = 0.0
            animate.toValue = Double.pi * 2.0
            self.layer?.add(animate, forKey: kAnimationKey)

            oldFrame = self.frame
            self.layer?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
            self.frame = oldFrame
        }


     }
}
Burak
  • 525
  • 4
  • 24
  • Thank you. :) I just tried this and it still rotates around one of its corners. How are you positioning your `NSView`? Do you use auto layout? Is it wrapped inside another `NSView` of the same size? – Clifton Labrum Oct 23 '17 at 15:35
  • Yes I use auto layout. On storyboard my view (i.e. an NSImageView) is placed inside a stack view. – Burak Oct 23 '17 at 16:40
  • I had to also set `self.layer?.frame = CGRect(x: 0.5, y: 0.5, width: oldFrame.width, height: oldFrame.height)` to make it rotate correctly. (Actually setting the view frame was not necessary in my case.) – codingFriend1 Mar 28 '23 at 10:22