CoreAnimation framework

Some examples to give a user a start point for Core Animation framework.
The ASObjC script could be run in Script Editor to show you the animation of NSView.

use framework "Foundation"
use framework "AppKit"
use scripting additions

property arguments : missing value

on run
	try
		if my NSThread's isMainThread() as boolean then
			my performDialog:arguments
		else
			my performSelectorOnMainThread:"performDialog:" withObject:"arguments" waitUntilDone:true
		end if
		
	on error the errorMessage number the errorNumber
		set the ErrorText to "Error: " & the errorNumber & ". " & the errorMessage
		return the ErrorText
	end try
end run

on performDialog:arguments
	set theAnimation1 to createAnimation("position.x", 30, 420, 5, 1, true, 0)
	set theAnimation2 to createAnimation("transform.scale.x", 1, 1.5, 5, 1, true, 0)
	set theAnimation3 to createAnimation("transform.rotation.z", 30, 75, 5, 1, true, 0)
	
	set theAnimation4 to createAnimationKeyframes("position.y", {0, 10, -30, 10, 0}, {0, 0.25, 0.5, 0.75, 1}, 5, true)
	
	set theRectangle to createView(40, 60, 30, 30)
	theRectangle's setBackgroundColor:(current application's NSColor's systemRedColor())
	theRectangle's setWantsLayer:true
	theRectangle's layer()'s addAnimation:theAnimation1 forKey:"animationPosition"
	theRectangle's layer()'s addAnimation:theAnimation2 forKey:"animationTransformScale"
	theRectangle's layer()'s addAnimation:theAnimation3 forKey:"animationTransformRotation"
	theRectangle's layer()'s addAnimation:theAnimation4 forKey:"animationPositionKeyframe"
	
	set theBox to createBoxWithRect("Core Animation Example", "futura", 20, 20, 560, 110)
	
	set subviewItems to {theRectangle, theBox}
	set theWindow to createWindowWithRectAndSubview(subviewItems, 0, 0, 600, 140)
	theWindow's |center|()
	theWindow's makeKeyAndOrderFront:me
end performDialog:

(**
* [createAnimation(keyPath, fromValue, toValue, duration, speed, reverseBool, repeatCount)]
* Reference:https://developer.apple.com/documentation/quartzcore/cabasicanimation?language=objc
*)
on createAnimation(keyPath, fromValue, toValue, duration, speed, reverseBool, repeatCount)
	set theAnimation to current application's CABasicAnimation's animationWithKeyPath:keyPath
	-- Defines the value the receiver uses to start interpolation.
	theAnimation's setFromValue:fromValue
	-- Defines the value the receiver uses to end interpolation.
	theAnimation's setToValue:toValue
	-- Specifies the basic duration of the animation, in seconds.
	theAnimation's setDuration:duration
	-- Specifies how time is mapped to receiver’s time space from the parent time space. Default: 1.0
	theAnimation's setSpeed:speed
	-- Determines if the receiver plays in the reverse upon completion. Default: false
	theAnimation's setAutoreverses:reverseBool
	-- Determines the number of times the animation will repeat. Default: 0
	theAnimation's setRepeatCount:repeatCount
	return theAnimation
end createAnimation

(**
* []
*)
on createAnimationKeyframes(keyPath, theValues, keyTimes, duration, isAdditiveBool)
	set theAnimation to current application's CAKeyframeAnimation's animationWithKeyPath:keyPath
	theAnimation's setValues:theValues
	theAnimation's setKeyTimes:keyTimes
	theAnimation's setDuration:duration
	theAnimation's setAdditive:isAdditiveBool
	return theAnimation
end createAnimationKeyframes

on createView(x, y, width, height)
	set viewRectSize to current application's NSMakeRect(x, y, width, height)
	set theView to current application's NSView's alloc()'s initWithFrame:viewRectSize
	return theView
end createView

on createBoxWithRect(title, |font|, x, y, width, height)
	set boxSize to current application's NSMakeRect(x, y, width, height)
	set theBox to current application's NSBox's alloc()'s initWithFrame:boxSize
	theBox's setTitle:title
	theBox's setTitleFont:(current application's NSFont's fontWithName:|font| |size|:12)
	-- Specify the location of a box’s title with respect to its border.
	theBox's setTitlePosition:(current application's NSAtTop)
	-- theBox's setTransparent:false
	-- theBox's setBoxType:(current application's NSBoxCustom)
	-- theBox's setBorderType:(current application's NSLineBorder)
	theBox's setBorderWidth:0.5
	theBox's setCornerRadius:5
	-- theBox's setFillColor:(current application's NSColor's lightBlueColor)
	theBox's setBorderColor:(current application's NSColor's orangeColor)
	return theBox
end createBoxWithRect

on createWindowWithRectAndSubview(subviewItems, x, y, width, height)
	set windowSize to current application's NSMakeRect(x, y, width, height)
	set winStyle to (current application's NSWindowStyleMaskTitled as integer) + (current application's NSWindowStyleMaskClosable as integer) + (current application's NSWindowStyleMaskMiniaturizable as integer) + (current application's NSWindowStyleMaskResizable as integer)
	set theWindow to current application's NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:2 defer:true
	
	repeat with anSubview in subviewItems
		(theWindow's contentView()'s addSubview:anSubview)
	end repeat
	return theWindow
end createWindowWithRectAndSubview
3 Likes

In this example I do transition (fade from right) from Core Animation Programming Guide.
The code in the comments is Objective-C. I have change the duration of 1 to 5 to make it
little bit slower from the orginal code from Apple.

Apple’s code didn’t include:
myView1’s setWantsLayer:true
myView2’s setWantsLayer:true
But its a most to be able to access the layer from the view.

This is a learning exercise and fun. :slight_smile:

use framework "Foundation"
use framework "AppKit"
use scripting additions

property arguments : missing value

on run
	try
		if my NSThread's isMainThread() as boolean then
			my performDialog:arguments
		else
			my performSelectorOnMainThread:"performDialog:" withObject:"arguments" waitUntilDone:true
		end if
		
	on error the errorMessage number the errorNumber
		set the ErrorText to "Error: " & the errorNumber & ". " & the errorMessage
		return the ErrorText
	end try
end run

on performDialog:arguments
	
	(**
* CATransition* transition = [CATransition animation];
* transition.startProgress = 0;
* transition.endProgress = 1.0;
* transition.type = kCATransitionPush;
* transition.subtype = kCATransitionFromRight;
* transition.duration = 1.0;
*)
	set theTransition to current application's CATransition's animation()
	theTransition's setStartProgress:0
	theTransition's setEndProgress:1.0
	-- https://developer.apple.com/documentation/quartzcore/catransitiontype?language=objc
	theTransition's setType:(current application's kCATransitionPush)
	-- https://developer.apple.com/documentation/quartzcore/catransitionsubtype?language=objc
	theTransition's setSubtype:(current application's kCATransitionFromRight)
	theTransition's setDuration:5
	
	(**
* // Add the transition animation to both layers
* [myView1.layer addAnimation:transition forKey:@"transition"];
* [myView2.layer addAnimation:transition forKey:@"transition"];
*)
	
	set {imageName1, imageName2} to {"imageName1.png", "imageName2.png"}
	
	-- In the subview of NSView we make NSImageView
	set theURL to current application's |NSURL|'s fileURLWithPath:((POSIX path of (path to desktop)) & imageName1)
	set imageView to current application's NSImage's alloc()'s initWithContentsOfURL:theURL
	set myImageView1 to createImageView(20, 20, 100, 100, imageView)
	
	set theURL to current application's |NSURL|'s fileURLWithPath:((POSIX path of (path to desktop)) & imageName2)
	set imageView to current application's NSImage's alloc()'s initWithContentsOfURL:theURL
	set myImageView2 to createImageView(20, 20, 100, 100, imageView)
	
	
	-- First we neeed to make 2 NSView, we use a handler
	set myView1 to createView(20, 20, 100, 100, {myImageView1})
	set myView2 to createView(20, 20, 100, 100, {myImageView2})
	
	myView1's setWantsLayer:true
	myView2's setWantsLayer:true
	
	myView1's layer()'s addAnimation:theTransition forKey:"transition"
	myView2's layer()'s addAnimation:theTransition forKey:"transition"
	
	(**
* // Finally, change the visibility of the layers.
* myView1.hidden = YES;
* myView2.hidden = NO;
*)
	myView1's setHidden:true
	myView2's setHidden:false
	
	set subviewItems to {myView1, myView2}
	set theWindow to createWindowWithRectAndSubview(subviewItems, 0, 0, 600, 140)
	theWindow's |center|()
	theWindow's makeKeyAndOrderFront:me
end performDialog:

on createView(x, y, width, height, subviews)
	set viewRectSize to current application's NSMakeRect(x, y, width, height)
	set theView to current application's NSView's alloc()'s initWithFrame:viewRectSize
	theView's setSubviews:subviews -- list
	return theView
end createView

on createImageView(x, y, width, height, imageView)
	set imageViewSize to current application's NSMakeRect(x, y, width, height)
	set theImageView to current application's NSImageView's alloc()'s initWithFrame:imageViewSize
	theImageView's setImage:imageView
	return theImageView
end createImageView

on createWindowWithRectAndSubview(subviewItems, x, y, width, height)
	set windowSize to current application's NSMakeRect(x, y, width, height)
	set winStyle to (current application's NSWindowStyleMaskTitled as integer) + (current application's NSWindowStyleMaskClosable as integer) + (current application's NSWindowStyleMaskMiniaturizable as integer) + (current application's NSWindowStyleMaskResizable as integer)
	set theWindow to current application's NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:2 defer:true
	
	repeat with anSubview in subviewItems
		(theWindow's contentView()'s addSubview:anSubview)
	end repeat
	return theWindow
end createWindowWithRectAndSubview
3 Likes