CATransition in Snow Leopard

Hey guys,
So I’ve been running into a bit of trouble lately with Objective-C. There’s this great class called CATransition that switches the views with a nice animated slide (either up, down, right, or left). However, for whatever reason, you cannot use the kCATransitionPush transition with any of the transition subtypes in Snow Leopard, only in Lion and now Mountain Lion (this may or may not be true for the other transition types as well, I haven’t tested them yet). Because of this, coupled with the fact that most of what I’m making is geared toward my Snow Leopard machine, I made a class that uses CABasicAnimation to act like CATransition’s kCATransitionPush and decided to post it here for anyone else with the same problem. I also added a bit of Shane Stanley’s code to make it so that if the views are different sizes, the window itself will change size to fit the new NSView. It’s pretty self explanatory, oldView is the current view, newView is the new view, theWindow is the NSWindow housing the NSView’s, parentView is the NSWindow’s NSView that contains the subviews, resizeWin is whether or not the animation should resize the window (true to resize, false to just animate the changing of views), and theDirection is either “left,” “right,” “up,” or “down.” If you want to give it a try, here’s a project folder download (http://dl.dropbox.com/u/36103137/Example.zip) with all of the IB connections and code ready to run. If you have any comments or comments, please post them. Enjoy!

~Josh

-- MyTransitions.applescript
-- Example

-- Created by Josh Fletcher on 7/30/12.
-- Copyright 2012 ChumpApps All rights reserved.


script MyTransitions
        property parent : class "NSObject"
        property oView : ""
        
        on changeView______(oldView, newView, theDirection, theWindow, resizeWin, parentView)
                set theDirection to theDirection as string
                set oView to oldView
                set oldFrame to oldView's frame()
                set newFrame to newView's frame()
                
                parentView's addSubview_(newView)
                if theDirection = "Up" then newView's setFrameOrigin_({(x of origin of oldFrame), (height of |size| of oldFrame)})
                if theDirection = "Down" then newView's setFrameOrigin_({(x of origin of oldFrame), -1 * (height of |size| of newFrame)})
                if theDirection = "Right" then newView's setFrameOrigin_({(width of |size| of oldFrame), (y of origin of oldFrame)})
                if theDirection = "Left" then newView's setFrameOrigin_({-1 * (width of |size| of newFrame), (y of origin of oldFrame)})
                
                set newFrame to newView's frame()
                
                set firstAnimation to current application's CABasicAnimation's animationWithKeyPath_("position")
                tell firstAnimation
                        setDelegate_(me)
                        setRemovedOnCompletion_(false)
                        setFromValue_(current application's NSValue's valueWithPoint_(origin of oldFrame))
                        setToValue_(current application's NSValue's valueWithPoint_({(x of origin of oldFrame) - (x of origin of newFrame), (y of origin of oldFrame) - (y of origin of newFrame)}))
                        setDuration_(1.5)
                        setTimingFunction_(current application's CAMediaTimingFunction's functionWithControlPoints____(0.25, 0.1, 0.25, 1)) -- for smooth transition use (0.1, 2.5, 0.3, 0.3)
                end tell
                set secondAnimation to current application's CABasicAnimation's animationWithKeyPath_("position")
                tell secondAnimation
                        setRemovedOnCompletion_(false)
                        setFromValue_(current application's NSValue's valueWithPoint_(origin of newFrame))
                        setToValue_(current application's NSValue's valueWithPoint_({(x of origin of oldFrame), (y of origin of oldFrame)}))
                        setDuration_(1.5)
                        setTimingFunction_(current application's CAMediaTimingFunction's functionWithControlPoints____(0.25, 0.1, 0.25, 1)) -- for smooth transition use (0.1, 2.5, 0.3, 0.3)
                end tell
                newView's layer()'s addAnimation_forKey_(secondAnimation, "position")
                oldView's layer()'s addAnimation_forKey_(firstAnimation, "position")
                
                if resizeWin then
                        set theFrame to newView's frame()
                        set height of |size| of theFrame to (height of |size| of theFrame)
                        tell theWindow
                                set theFrame to frameRectForContentRect_(theFrame)
                                set {origin:{x:frameX, y:frameY}, |size|:{width:frameWidth, height:frameHeight}} to theFrame
                                set {origin:{x:windowX, y:windowY}, |size|:{width:windowWidth, height:windowHeight}} to frame()
                                set newOrigin to {x:windowX - (frameWidth - windowWidth) / 2, y:windowY - frameHeight + windowHeight}
                                set newFrame to {origin:newOrigin, |size|:{width:frameWidth, height:frameHeight}}
                                set newFrame to my adjustFrame_forScreenOfWindow_(newFrame, theWindow)
                                setFrame_display_animate_(newFrame, true, true)
                        end tell
                end if
                tell oldView to setFrameOrigin_({(x of origin of oldFrame) - (x of origin of newFrame), (y of origin of oldFrame) - (y of origin of newFrame)})
                tell newView to setFrameOrigin_({(x of origin of oldFrame), (y of origin of oldFrame)})
        end changeView______
        
        on adjustFrame_forScreenOfWindow_(proposedFrame, aWindow)
                set {origin:{x:windowX, y:windowY}, |size|:{width:windowWidth, height:windowHeight}} to proposedFrame
                set screenFrame to aWindow's screen()'s visibleFrame()
                set {origin:{x:frameX, y:frameY}, |size|:{width:frameWidth, height:frameHeight}} to screenFrame
                if windowX < frameX then set windowX to frameX
                if windowX + windowWidth > frameX + frameWidth then set windowX to frameX + frameWidth - windowWidth
                if windowY < frameY then set windowY to frameY
                return {origin:{x:windowX, y:windowY}, |size|:{width:windowWidth, height:windowHeight}}
        end adjustFrame_forScreenOfWindow_
        
        on animationDidStop_finished_(theAnimation, flag)
                tell oView to removeFromSuperview()
        end animationDidStop_finished_
        
end script