@@ -5,34 +5,52 @@ class RotateAnimation: BaseWindowAnimation, WindowAnimation {
55
66 // MARK: - WindowAnimation
77 func animateIn( window: NSWindow , duration: TimeInterval , completion: ( ( ) -> Void ) ? ) {
8- setupWindow ( window)
9-
108 guard
119 let layer = prepareLayer ( from: window) ,
1210 let contentView = window. contentView
1311 else {
1412 completion ? ( )
1513 return
1614 }
15+ setupWindow ( window)
16+
17+ let oldAnchor = layer. anchorPoint
18+ let oldPosition = layer. position
1719
18- let originalFrame = layer. frame
1920 layer. anchorPoint = CGPoint ( x: 0.5 , y: 0.5 )
20- layer. frame = originalFrame
21+ layer. position = CGPoint ( x : layer . bounds . midX , y : layer . bounds . midY )
2122
22- CATransaction . begin ( )
23- CATransaction . setAnimationDuration ( duration)
24- CATransaction . setAnimationTimingFunction ( AnimationTiming . easeOut)
25- CATransaction . setCompletionBlock {
26- layer. transform = CATransform3DIdentity
27- layer. anchorPoint = CGPoint ( x: 0 , y: 0 )
28- layer. frame = originalFrame
29- completion ? ( )
30- }
23+ // Calculate the mathematically perfect scale down to ensure the diagonal
24+ // never clips the shortest dimension of the window bounds during the spin.
25+ let width = layer. bounds. width
26+ let height = layer. bounds. height
27+ let diagonal = hypot ( width, height)
28+ let minDimension = min ( width, height)
29+ // Add a slight 2% margin of safety
30+ let safeScale = ( minDimension / diagonal) * 0.98
3131
3232 let rotateAnim = createAnimation ( keyPath: " transform.rotation.z " , from: CGFloat . pi * 2 , to: 0.0 , duration: duration)
33- layer. add ( rotateAnim, forKey: " rotate " )
3433
35- CATransaction . commit ( )
34+ let scaleAnim = CAKeyframeAnimation ( keyPath: " transform.scale " )
35+ scaleAnim. values = [ 1.0 , safeScale, 1.0 ]
36+ scaleAnim. keyTimes = [ 0.0 , 0.5 , 1.0 ]
37+ scaleAnim. duration = duration
38+ scaleAnim. timingFunction = CAMediaTimingFunction ( name: . easeOut)
39+
40+ let group = CAAnimationGroup ( )
41+ group. animations = [ rotateAnim, scaleAnim]
42+ group. duration = duration
43+ group. fillMode = . forwards
44+ group. isRemovedOnCompletion = false
45+
46+ group. delegate = AnimationCompletionDelegate { [ weak layer] finished in
47+ layer? . transform = CATransform3DIdentity
48+ layer? . anchorPoint = oldAnchor
49+ layer? . position = oldPosition
50+ completion ? ( )
51+ }
52+
53+ layer. add ( group, forKey: " rotateIn " )
3654
3755 animateAlpha ( contentView: contentView, from: 0.0 , to: 1.0 , duration: duration)
3856 }
@@ -46,25 +64,41 @@ class RotateAnimation: BaseWindowAnimation, WindowAnimation {
4664 return
4765 }
4866
49- let originalFrame = layer. frame
67+ let oldAnchor = layer. anchorPoint
68+ let oldPosition = layer. position
69+
5070 layer. anchorPoint = CGPoint ( x: 0.5 , y: 0.5 )
51- layer. frame = originalFrame
71+ layer. position = CGPoint ( x: layer. bounds. midX, y: layer. bounds. midY)
72+
73+ let width = layer. bounds. width
74+ let height = layer. bounds. height
75+ let diagonal = hypot ( width, height)
76+ let minDimension = min ( width, height)
77+ let safeScale = ( minDimension / diagonal) * 0.98
5278
53- CATransaction . begin ( )
54- CATransaction . setAnimationDuration ( duration)
55- CATransaction . setAnimationTimingFunction ( AnimationTiming . easeIn)
56- CATransaction . setCompletionBlock {
57- layer. transform = CATransform3DIdentity
58- layer. anchorPoint = CGPoint ( x: 0 , y: 0 )
59- layer. frame = originalFrame
79+ let rotateAnim = createAnimation ( keyPath: " transform.rotation.z " , from: 0.0 , to: CGFloat . pi * 2 , duration: duration, timing: CAMediaTimingFunction ( name: . easeIn) )
80+
81+ let scaleAnim = CAKeyframeAnimation ( keyPath: " transform.scale " )
82+ scaleAnim. values = [ 1.0 , safeScale, 1.0 ]
83+ scaleAnim. keyTimes = [ 0.0 , 0.5 , 1.0 ]
84+ scaleAnim. duration = duration
85+ scaleAnim. timingFunction = CAMediaTimingFunction ( name: . easeIn)
86+
87+ let group = CAAnimationGroup ( )
88+ group. animations = [ rotateAnim, scaleAnim]
89+ group. duration = duration
90+ group. fillMode = . forwards
91+ group. isRemovedOnCompletion = false
92+
93+ group. delegate = AnimationCompletionDelegate { [ weak layer] finished in
94+ layer? . transform = CATransform3DIdentity
95+ layer? . anchorPoint = oldAnchor
96+ layer? . position = oldPosition
6097 completion ? ( )
6198 }
6299
63- let rotateAnim = createAnimation ( keyPath: " transform.rotation.z " , from: 0.0 , to: CGFloat . pi * 2 , duration: duration)
64- layer. add ( rotateAnim, forKey: " rotate " )
65-
66- CATransaction . commit ( )
100+ layer. add ( group, forKey: " rotateOut " )
67101
68- animateAlpha ( contentView: contentView, from: 1.0 , to: 0.0 , duration: duration)
102+ animateAlpha ( contentView: contentView, from: 1.0 , to: 0.0 , duration: duration, timing : CAMediaTimingFunction ( name : . easeIn ) )
69103 }
70104}
0 commit comments