Continuous Rotation of NSImageView (so it appears to be animated)
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
How do I achieve continuous rotation of an NSImage inside NSImageView?
Getting an object to rotate more than 180 degrees is actually a little bit tricky. The problem is that you specify a transformation matrix for the ending rotation, and the system decides to rotate in the other direction.
What I've done is to create a CABasicAnimation of less than 180 degrees, set up to be additive , and with a repeat count. Each step in the animation animates the object more.
The following code is taken from an iOS application, but the technique is identical in Mac OS.
CABasicAnimation* rotate = [CABasicAnimation animationWithKeyPath: @"transform.rotation.z"];
rotate.removedOnCompletion = FALSE;
rotate.fillMode = kCAFillModeForwards;
//Do a series of 5 quarter turns for a total of a 1.25 turns
//(2PI is a full turn, so pi/2 is a quarter turn)
[rotate setToValue: [NSNumber numberWithFloat: -M_PI / 2]];
rotate.repeatCount = 11;
rotate.duration = duration/2;
rotate.beginTime = start;
rotate.cumulative = TRUE;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
CAAnimation objects operate on layers, so for Mac OS, you'll need to set the "wants layer" property in interface builder, and then add the animation to your view's layer.
To make your view rotate forever, you'd set repeat count to some very large number like 1e100.
Once you've created your animation, you'd add it to your view's layer with code something like this:
[myView.layer addAnimation: rotate forKey: @"rotateAnimation"];
That's about all there is to it.
Rotate NSImageView at its Center to Make it Spin
I just created a simple demo which contains the handy setAnchorPoint
extension for all views.
The main reason you see your rotation from a corner is that your anchor point is somehow reset to 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
}
}
}
Animate NSImageView translation
If you are using auto layout you can add an outlet
to one of your constraints and change the constant
value through the animator
.
Example
Here I have a view with a label and a button
The label has two constraints.
- It is centred horizontally in the view
- It has a distance of 80 to the bottom of the view
I can now find the "distance to bottom" outlet in the Document Outline
And ctrl drag it to my corresponding NSViewController
where I create an outlet
for it.
@IBOutlet weak var distanceToBottom: NSLayoutConstraint!
I can verify that the outlet
is set by looking at the Connections Inspector in the right side of the screen
Last step is to animate the view. Here I'm just using an IBAction
on a NSButton
like so:
@IBAction func animate(_ sender: NSButton) {
distanceToBottom.animator().constant = 5
}
If I run that my label animates nicely to its new position 5 pixels from the bottom.
Hope that helps you.
CoreAnimation setFrameCenterRotation Doesn't Animate Properly
First make sure NSView layer backed (Core Animation enabled), animator proxy would otherwise animate without CA. Do this with -setWantsLayer:YES, or by checking the corresponding option for that view in IB.
Second, set the layer anchor point to 0.5, 0.5:
view.layer.anchorPoint = CGPointMake(0.5,0.5);
Third, you may need direct layer interaction if you can't get what you want, but first try with AppKit API's, as it preserves mouse events, etc.
NSImage rotation in NSView is not working
For any future visitors, The code works perfectly and you may use it if you want except make sure before you add layer inside nsview we have to set that nsview wants layer.
so, the changes will be...
- (void)awakeFromNib {
NSImage *image = [NSImage imageNamed:@"SyncRing.png"];
//init layer
syncFirst = [CALayer layer];
//animatated content size init
syncFirst.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
syncFirst.position = CGPointMake(10, 10);
syncFirst.contents = (id)[self convertToCGImageFromNasImage:image];
/*HERE SETWANTSLAYER TO TRUE IN ORDER TO FIX*/
[nsview setWantsLayer:YES];
[nsview.layer addSublayer:syncFirst];
}
Before using this code
Please refer to the discussion in comment. There are some useful tips, Which I have overlooked while solving my problem. Credit goes to - Peter Hosey
Related Topics
Proper Way of Editing a Cocoapod Library
Siblings Relationship Between Same Models in Vapor
Avaudiopcmbuffer Built Programmatically, Not Playing Back in Stereo
How to Detect Touches on UIimageview of UItableviewcell in Swift
Leaving Equal Width Gap Spacing Before and Between Menu Buttons
Swiftui Previews - Unexpected Data
Swiftui Multiple Animations for Same Element
Exc Bad Access While Creating a New Characterset
Why Do I Have to Unwrap a Weak Self
How to Prevent SQL Injections with User-Search-Terms in Vapor 4 (Fluent 4)
How to Reconstruct Grayscale Image from Intensity Values
I Opened My App in Xcode 10 and Now I Have Errors in 9.4.1: Sdkapplicationdelegate (Facebookcore)
Weird Toolbar with Nested Conditionals Behavior
Swift Xcode 7 Beta 5 Type Cannot Refer to Itself as a Requirement
Dictionary Becomes Nil When Trying to Pass Back Cllocation Object to iOS App Extension
Force Refresh on Another Viewcontroller Component with Swift