UISlider will not move if two transform are made - swift5

I have a UISlider that was implemented through Storyboard. I added a Keypath layer.transform.rotation.z with a value of 1.57 in order to make the UISlider vertical.
I also have a MoveRight UIButton that controls 3 elements (Two UIImageViews and One UISlider) to move in unison. The code for the transform is shown below
#IBAction func MoveRight(_ sender: Any) {
// Used to Show Reset Button for Right
ResetOrigin.isHidden = false
let yPosition1 = myslider.frame.origin.y
let yPosition2 = ImageView.frame.origin.y
let yPosition3 = ImageView2.frame.origin.y
UIView.animate(withDuration: 0.0, delay: 0.0, options: [], animations: {
self.myslider.transform = CGAffineTransform(translationX: 0.001, y: yPosition1)
self.ImageView.transform = CGAffineTransform(translationX: 0.001, y: yPosition2)
self.ImageView2.transform = CGAffineTransform(translationX: 0.001, y: yPosition3)
}, completion: nil)
}
When the code is like this the movement works great although the UISlider is no longer Vertical after the transform. And when I try to add this line of code to the animation
self.myslider.transform = CGAffineTransform(rotationAngle: (CGFloat.pi / 2))
The slider will no longer move while the other 2 Elements are affected by the transform. I tried doing a separate .animate to see if that would resolve the problem but the same result is given. Any ideas on how to solve this issue would be greatly appreciated

Related

Animate view to hide/scale into its bottom right corner

I'm trying to get a view to shrink down to nothing in the bottom right corner when its cancel button is pressed. Currently when i use:
UIView.animate(withDuration: 0.7, delay: 0, options: .beginFromCurrentState, animations: {
self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
})
the view shrinks into its center.
I understand that I should be setting the anchor point of the layer so it shrinks to that point but when I do this it moves the view center to that point then shrinks:
UIView.animate(withDuration: 0.7, delay: 0, options: .beginFromCurrentState, animations: {
self.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
})
Any ideas?
Edit: More info would be that im using cartography to set the view's constraints to the viewController.view edges, I'm wondering if this has something to do with it?
Frame of UIView or CALayer are actually derived properties which are calculated from layers position and anchorpoint. So, when you change either one of these, the layer frame also changes and hence you see the effect of view movement.
When you change the anchor point of the CALayer, you could also immediately set the position of the layer to compromise for the change. You can offset the view position with amount of anchor point changes relative to views bounds.
Here is a simple extension on UIView, which you could use to change the layers anchor point without actually changing the position.
extension UIView {
func set(anchorPoint: CGPoint) {
let originalPosition = CGPoint(x: frame.midX, y: frame.midY)
let width = bounds.width
let height = bounds.height
let newXPosition = originalPosition.x + (anchorPoint.x - 0.5) * width
let newYPosition = originalPosition.y + (anchorPoint.y - 0.5) * height
layer.anchorPoint = anchorPoint
layer.position = CGPoint(x: newXPosition,
y: newYPosition)
}
}
The code above changes position of CALayer as soon as you make change to the anchorPoint. You could as well do this without having to change the position.
Whenever you set the anchorPoint, simply set back previous frame,
let originalFrame = view.frame
view.layer.anchorPoint = CGPoint(x: 0, y: 0)
view.frame = originalFrame
This will prevent your view from moving and stay at the same position, despite of the change in anchorPoint.
Also, for your particular case, setting anchorPoint to layer triggers intrinsic animation. So, if you want to make it like shrink to lower or upper edge, perform anchorPoint change before animation like so,
UIView.performWithoutAnimation {
self.set(anchorPoint: CGPoint(x: 0, y: 0))
}
UIView.animate(withDuration: 0.7, delay: 0, options: .beginFromCurrentState, animations: {
self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
})

Custom transition in Swift 3 does not translate correctly

I've implemented a Navigation controller to incorporate an rotating-disc type of layout (imagine each VC laid out in a circle, that rotates as a whole, into view sequentially. The navigation controller is configured to use a custom transition class, as below :-
import UIKit
class RotaryTransition: NSObject, UIViewControllerAnimatedTransitioning {
let isPresenting :Bool
let duration :TimeInterval = 0.5
let animationDuration: TimeInterval = 0.7
let delay: TimeInterval = 0
let damping: CGFloat = 1.4
let spring: CGFloat = 6.0
init(isPresenting: Bool) {
self.isPresenting = isPresenting
super.init()
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
//Get references to the view hierarchy
let fromViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let toViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let sourceRect: CGRect = transitionContext.initialFrame(for: fromViewController)
let containerView: UIView = transitionContext.containerView
if self.isPresenting { // Push
//1. Settings for the fromVC ............................
// fromViewController.view.frame = sourceRect
fromViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
fromViewController.view.layer.position = CGPoint(x: fromViewController.view.frame.size.width/2, y: fromViewController.view.frame.size.height * 3);
//2. Setup toVC view...........................
containerView.insertSubview(toViewController.view, belowSubview:fromViewController.view)
toViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
toViewController.view.layer.position = CGPoint(x: toViewController.view.frame.size.width/2, y: toViewController.view.frame.size.height * 3);
toViewController.view.transform = CGAffineTransform(rotationAngle: 15 * CGFloat(M_PI / 180));
//3. Perform the animation...............................
UIView.animate(withDuration: animationDuration, delay:delay, usingSpringWithDamping: damping, initialSpringVelocity: spring, options: [], animations: {
fromViewController.view.transform = CGAffineTransform(rotationAngle: -15 * CGFloat(M_PI / 180));
toViewController.view.transform = CGAffineTransform(rotationAngle: 0);
}, completion: {
(animated: Bool) -> () in transitionContext.completeTransition(true)
})
} else { // Pop
//1. Settings for the fromVC ............................
fromViewController.view.frame = sourceRect
fromViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
fromViewController.view.layer.position = CGPoint(x: fromViewController.view.frame.size.width/2, y: fromViewController.view.frame.size.height * 3);
//2. Setup toVC view...........................
// toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
toViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
toViewController.view.layer.position = CGPoint(x: toViewController.view.frame.size.width/2, y: toViewController.view.frame.size.height * 3);
toViewController.view.transform = CGAffineTransform(rotationAngle: -15 * CGFloat(M_PI / 180));
containerView.insertSubview(toViewController.view, belowSubview:fromViewController.view)
//3. Perform the animation...............................
UIView.animate(withDuration: animationDuration, delay:delay, usingSpringWithDamping: damping, initialSpringVelocity: spring, options: [], animations: {
fromViewController.view.transform = CGAffineTransform(rotationAngle: 15 * CGFloat(M_PI / 180));
toViewController.view.transform = CGAffineTransform(rotationAngle: 0);
}, completion: {
//When the animation is completed call completeTransition
(animated: Bool) -> () in transitionContext.completeTransition(true)
})
}
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration;
}
}
A representation of how the views move is show in the illustration below... The two red areas are the problems, as explained later.
The presenting (push) translation works fine - 2 moves to 1 and 3 moves to 2. However, the dismissing (pop) translation does not, whereby the dismissing VC moves out of view seemingly correctly (2 moving to 3), but the presenting (previous) VC arrives either in the wrong place, or with an incorrectly sized frame...
With the class as-is, the translation results in 2 moving to 3 (correctly) but 1 then moves to 4, the view is correctly sized, yet seems offset, by a seemingly arbitrary distance, from the intended location. I have since tried a variety of different solutions.
In the pop section I tried adding the following line (commented in the code) :-
toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
...but the VC now ends up being shrunk (1 moves to 5). I hope someone can see the likely stupid error I'm making. I tried simply duplicating the push section to the pop section (and reversing everything), but it just doesn't work!
FYI... Those needing to know how to hookup the transition to a UINavigationController - Add the UINavigationControllerDelegate to your nav controller, along with the following function...
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let transition: SwingTransition = SwingTransition.init(isPresenting: ( operation == .push ? true : false ))
return transition;
}
The diagram below shows how all views would share the same originating point (for the translation). The objective is to give the illusion of a revolver barrel moving each VC into view. The top centre view represents the viewing window, showing the third view in the stack. Apologies for the poor visuals...
The problem is that one of the properties in the restored view controller's view isn't getting reset properly. I'd suggest resetting it when the animation is done (you probably don't want to keep the non-standard transform and anchorPoint in case you do other animations later that presume the view is not transformed). So, in the completion block of the animation, reset the position, anchorPoint and transform of the views.
class RotaryTransition: NSObject, UIViewControllerAnimatedTransitioning {
let isPresenting: Bool
let duration: TimeInterval = 0.5
let delay: TimeInterval = 0
let damping: CGFloat = 1.4
let spring: CGFloat = 6
init(isPresenting: Bool) {
self.isPresenting = isPresenting
super.init()
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let from = transitionContext.viewController(forKey: .from)!
let to = transitionContext.viewController(forKey: .to)!
let frame = transitionContext.initialFrame(for: from)
let height = frame.size.height
let width = frame.size.width
let angle: CGFloat = 15.0 * .pi / 180.0
let rotationCenterOffset: CGFloat = width / 2 / tan(angle / 2) / height + 1 // use fixed value, e.g. 3, if you want, or use this to ensure that the corners of the two views just touch, but don't overlap
let rightTransform = CATransform3DMakeRotation(angle, 0, 0, 1)
let leftTransform = CATransform3DMakeRotation(-angle, 0, 0, 1)
transitionContext.containerView.insertSubview(to.view, aboveSubview: from.view)
// save the anchor and position
let anchorPoint = from.view.layer.anchorPoint
let position = from.view.layer.position
// prepare `to` layer for rotation
to.view.layer.anchorPoint = CGPoint(x: 0.5, y: rotationCenterOffset)
to.view.layer.position = CGPoint(x: width / 2, y: height * rotationCenterOffset)
to.view.layer.transform = self.isPresenting ? rightTransform : leftTransform
//to.view.layer.opacity = 0
// prepare `from` layer for rotation
from.view.layer.anchorPoint = CGPoint(x: 0.5, y: rotationCenterOffset)
from.view.layer.position = CGPoint(x: width / 2, y: height * rotationCenterOffset)
// rotate
UIView.animate(withDuration: duration, delay: delay, usingSpringWithDamping: damping, initialSpringVelocity: spring, animations: {
from.view.layer.transform = self.isPresenting ? leftTransform : rightTransform
to.view.layer.transform = CATransform3DIdentity
//to.view.layer.opacity = 1
//from.view.layer.opacity = 0
}, completion: { finished in
// restore the layers to their default configuration
for view in [to.view, from.view] {
view?.layer.transform = CATransform3DIdentity
view?.layer.anchorPoint = anchorPoint
view?.layer.position = position
//view?.layer.opacity = 1
}
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
}
I did a few other sundry changes, while I was here:
eliminated the semicolons;
eliminated one of the duration properties;
fixed the name of the parameter of the completion closure of animate method to finished rather than animated to more accurately reflect what it's real purpose is ... you could use _, too;
set the completeTransition based upon whether the animation was canceled or not (because if you ever make this interactive/cancelable, you don't want to always use true);
use .pi rather than M_PI;
I commented out my adjustments of opacity, but I generally do that to give the effect a touch more polish and to ensure that if you tweak angles so the views overlap, you don't get any weird artifacts of the other view just as the animation starts or just as its finishing; I've actually calculated the parameters so there's no overlapping, regardless of screen dimensions, so that wasn't necessary and I commented out the opacity lines, but you might consider using them, depending upon the desired effect.
Previously I showed how to simplify the process a bit, but the resulting effect wasn't exactly what you were looking for, but see the previous rendition of this answer if you're interested.
Your problem is a common one that happens when you do custom view controller transitions. I know this because I've done it a lot :)
You're looking for a problem in the pop transition, but the actual problem is in the push. If you inspect the view of the first controller in the stack after the transition, you'll see that it has an unusual frame, because you've messed about with its transform and anchor point and layer position and so forth. Really, you need to clean all that up before you end the transition, otherwise it bites you later on, as you're seeing in the pop.
A much simpler and safer way to do your custom transitions is to add a "canvas" view, then to that canvas add snapshots of your outgoing and incoming views instead and manipulate those. This means you have no cleanup at the end of the transition - just remove the canvas view. I've written about this technique here. For your case, I added the following convenience method:
extension UIView {
func snapshot(view: UIView, afterUpdates: Bool) -> UIView? {
guard let snapshot = view.snapshotView(afterScreenUpdates: afterUpdates) else { return nil }
self.addSubview(snapshot)
snapshot.frame = convert(view.bounds, from: view)
return snapshot
}
}
Then updated your transition code to move the snapshots around on a canvas view instead:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
//Get references to the view hierarchy
let fromViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let toViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let sourceRect: CGRect = transitionContext.initialFrame(for: fromViewController)
let containerView: UIView = transitionContext.containerView
// The canvas is used for all animation and discarded at the end
let canvas = UIView(frame: containerView.bounds)
containerView.addSubview(canvas)
let fromView = transitionContext.view(forKey: .from)!
let toView = transitionContext.view(forKey: .to)!
toView.frame = transitionContext.finalFrame(for: toViewController)
toView.layoutIfNeeded()
let toSnap = canvas.snapshot(view: toView, afterUpdates: true)!
if self.isPresenting { // Push
//1. Settings for the fromVC ............................
// fromViewController.view.frame = sourceRect
let fromSnap = canvas.snapshot(view: fromView, afterUpdates: false)!
fromView.removeFromSuperview()
fromSnap.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
fromSnap.layer.position = CGPoint(x: fromViewController.view.frame.size.width/2, y: fromViewController.view.frame.size.height * 3);
//2. Setup toVC view...........................
toSnap.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
toSnap.layer.position = CGPoint(x: toViewController.view.frame.size.width/2, y: toViewController.view.frame.size.height * 3);
toSnap.transform = CGAffineTransform(rotationAngle: 15 * CGFloat(M_PI / 180));
//3. Perform the animation...............................
UIView.animate(withDuration: animationDuration, delay:delay, usingSpringWithDamping: damping, initialSpringVelocity: spring, options: [], animations: {
fromSnap.transform = CGAffineTransform(rotationAngle: -15 * CGFloat(M_PI / 180));
toSnap.transform = CGAffineTransform(rotationAngle: 0);
}, completion: {
(animated: Bool) -> () in
containerView.insertSubview(toViewController.view, belowSubview:canvas)
canvas.removeFromSuperview()
transitionContext.completeTransition(true)
})
} else { // Pop
//1. Settings for the fromVC ............................
fromViewController.view.frame = sourceRect
fromViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
fromViewController.view.layer.position = CGPoint(x: fromViewController.view.frame.size.width/2, y: fromViewController.view.frame.size.height * 3);
//2. Setup toVC view...........................
let toSnap = canvas.snapshot(view: toView, afterUpdates: true)!
toSnap.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
toSnap.layer.position = CGPoint(x: toViewController.view.frame.size.width/2, y: toViewController.view.frame.size.height * 3);
toSnap.transform = CGAffineTransform(rotationAngle: -15 * CGFloat(M_PI / 180));
//3. Perform the animation...............................
UIView.animate(withDuration: animationDuration, delay:delay, usingSpringWithDamping: damping, initialSpringVelocity: spring, options: [], animations: {
fromViewController.view.transform = CGAffineTransform(rotationAngle: 15 * CGFloat(M_PI / 180));
toSnap.transform = CGAffineTransform(rotationAngle: 0);
}, completion: {
//When the animation is completed call completeTransition
(animated: Bool) -> () in
containerView.insertSubview(toViewController.view, belowSubview: canvas)
canvas.removeFromSuperview()
transitionContext.completeTransition(true)
})
}
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration;
}
}
This particular transition is pretty simple so it's not too difficult to reset the properties of the view frames, but if you do anything more complex then the canvas and snapshot approach works really well, so I tend to just use it everywhere.

How to transform only the button edge of a view with animation

I am trying to achieve an effect that when I swipe up on a view, the buttom edge will move towards the top until it disappears (the views height will become smaller and smaller).
I want the top edge to stay in place and only the buttom edge to move. how can i adjust this code to achieve that?
UIView.animate(withDuration: 5.0, animations: { () in
self.topView.translatesAutoresizingMaskIntoConstraints = false
self.topView.transform = CGAffineTransform(scaleX: 1,y: 5)
})
self.topView.transform = CGAffineTransform.identity
First you should need the order of the method calls your code (not absolutly necessary, but looks better):
self.topView.transform = CGAffineTransform.identity
UIView.animate(withDuration: 5.0, animations: { () in
self.topView.translatesAutoresizingMaskIntoConstraints = false
self.topView.transform = CGAffineTransform(scaleX: 1,y: 5)
})
This scale transform will shrink the view to the center; if you want to keep the top position, you would add a translateBy(x:y:) transformation and add it to the scale transform, more or less like this:
UIView.animate(withDuration: 5.0, animations: { () in
self.topView.translatesAutoresizingMaskIntoConstraints = false
let scaleTransform = CGAffineTransform(scaleX: 1,y: 5)
let translateTransform = CGAffineTransform(translationX: 0,y: -100)
let combined = scaleTransform.concatenating(translateTransform)
self.topView.transform = combined
})
Or you could just animate the frame of the view, as described here:
Core Animation for UIView.frame

Animation to scale and move UIView (swift)

I have a UIView, placed in the middle of the screen. When the user presses a button, I want it to move up near top of the screen as it shrinks to about a fifth of its size.
I have tried this:
UIView.animateWithDuration(0.7) { () -> Void in
self.main.transform = CGAffineTransformMakeScale(0.2, 0.2)
self.main.transform = CGAffineTransformMakeTranslation(0, -250)
}
But for some reason, that only scales the view. I have also tried to put this in the animateWithDuration:
self.main.frame = CGRectMake(self.view.frame.width/2 - 10, 50, 50, 50)
How can I get both animations to work?
Joe's answer above does exactly as his GIF describes but it doesn't really answer your question since it translates then scales the view (as opposed to both translating and scaling at the same time). Your issue is that you're setting the view's transform in your animation block then immediately overwritting that value with another transform. To achieve both translation and scale at the same time, you'll want something like this:
#IBAction func animateButton(_ sender: UIButton) {
let originalTransform = self.main.transform
let scaledTransform = originalTransform.scaledBy(x: 0.2, y: 0.2)
let scaledAndTranslatedTransform = scaledTransform.translatedBy(x: 0.0, y: -250.0)
UIView.animate(withDuration: 0.7, animations: {
self.main.transform = scaledAndTranslatedTransform
})
}
You can achieve basic UIView animation in several ways in Swift. Below code is just a simple approach...
class ViewController: UIViewController {
#IBOutlet weak var animationButton: UIButton!
#IBOutlet weak var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// I added seperate colour to UIView when animation move from one animation block to another.So, You can get better understanding how the sequence of animation works.
self.myView.backgroundColor = .red
}
#IBAction func animateButton(_ sender: UIButton) {
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
//Frame Option 1:
self.myView.frame = CGRect(x: self.myView.frame.origin.x, y: 20, width: self.myView.frame.width, height: self.myView.frame.height)
//Frame Option 2:
//self.myView.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 4)
self.myView.backgroundColor = .blue
},completion: { finish in
UIView.animate(withDuration: 1, delay: 0.25,options: UIViewAnimationOptions.curveEaseOut,animations: {
self.myView.backgroundColor = .orange
self.myView.transform = CGAffineTransform(scaleX: 0.25, y: 0.25)
self.animationButton.isEnabled = false // If you want to restrict the button not to repeat animation..You can enable by setting into true
},completion: nil)})
}
}
Output:

Continuous Rotation of NSImageView (so it appears to be animated)

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
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
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
}
}
}