Setting up AVSynchonizedLayer - ios

I've been trying to produce a proof of concept that connects a video with an animation object (essentially, I am "rotoscoping" a user-selected-bitmap onto a canned video at specific locations). After watching all the available Apple material regarding Core Animation (WWDC10, etc.) and the AVSync..Layer (Apple WWDC10 sample code, doc sample code), there simply isn't a clear enough isolated code example to derive how to implemented this playback mode correctly.
Using CA, I have a video asset which is loaded into a CALayer, and an animation which is attached to a bitmap, and then connected to another CALayer. I have created an AVSynchronizedLayer and added both objects as sublayers.
When this Tree is added to the UIView's layer property I was expecting the playback of the animation to run in sync with the video. Instead, I only see the video playing, and the animation never executes - here are the relevant pieces of my code which all resides within a single View Controller (called within ViewDidLoad).
Video101.m4v = simple m4v video,
gerald.png = bitmap
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:#"Video101" withExtension:#"m4v"];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
AVPlayer *player = [[AVPlayer playerWithPlayerItem:playerItem]retain];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
playerLayer.frame = CGRectMake(0, 0, 1024, 768);
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
CALayer *gerald = [CALayer layer];
gerald.frame = CGRectMake(0, 0, 120, 120);
gerald.contents = (id) [UIImage imageNamed:#"gerald.png"].CGImage;
[self.view.layer addSublayer:playerLayer];
[self.view.layer insertSublayer:gerald above:playerLayer];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.duration = 18.0;
anim.values = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(480.0, 206.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(10.0, 760.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)], nil];
anim.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.02],
[NSNumber numberWithFloat:0.06],
[NSNumber numberWithFloat:0.09],
[NSNumber numberWithFloat:0.1],
[NSNumber numberWithFloat:0.12],
[NSNumber numberWithFloat:0.2],
[NSNumber numberWithFloat:0.25],
[NSNumber numberWithFloat:0.32],
[NSNumber numberWithFloat:0.38],
[NSNumber numberWithFloat:0.49],
[NSNumber numberWithFloat:0.53],
[NSNumber numberWithFloat:0.59],
[NSNumber numberWithFloat:0.63],
[NSNumber numberWithFloat:0.69],
[NSNumber numberWithFloat:0.78],
[NSNumber numberWithFloat:0.81],
[NSNumber numberWithFloat:0.91],
[NSNumber numberWithFloat:1.0], nil];
AVSynchronizedLayer *syncLayer = [AVSynchronizedLayer synchronizedLayerWithPlayerItem:playerItem];
[syncLayer addSublayer:gerald];
[gerald addAnimation:anim forKey:#"transform.position"];
[self.view.layer addSublayer:syncLayer];
[player play];
What is the correct format or manner to implement AVSynchronizedLayer, so that both animation and video playback together?

You have the CAKeyframeAnimation set up correctly except for one bit:
According to the WWDC "Editing Media with AV Foundation" lecture, you need to set the beginTime property of your animation to a non-zero value.
Zero beginTime is automatically translated to CACurrentMediaTime(). Use a small nonzero number: e.g., 1e-100 or -1e-100 (AVCoreAnimationBeginTimeAtZero)
I think if you add anim.beginTime = AVCoreAnimationBeginTimeAtZero; you'll find the animation begins right when the video starts playing.

Related

iOS Obj-c gradient background transition

I'd like to gradually change the background gradient from
gradient.colors = [NSArray arrayWithObjects:(id)[UIColor.blackColor CGColor],(id)[UIColor.blueColor CGColor], nil];
to
gradient.colors = [NSArray arrayWithObjects:(id)[UIColor.grayColor CGColor],(id)[UIColor.cyanColor CGColor], nil];
[EDIT] My current code is:
gradient.colors = [NSArray arrayWithObjects:(id)[UIColor.blackColor CGColor],(id)[UIColor.blueColor CGColor], nil];
NSArray *fromColors = gradient.colors;
NSArray *toColors =[NSArray arrayWithObjects:(id)[UIColor.grayColor CGColor],(id)[UIColor.cyanColor CGColor], nil];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: #"colors"];
animation.fromValue = fromColors;
animation.toValue = toColors;
animation.duration = 3.00;
animation.removedOnCompletion = false;
animation.autoreverses = NO;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
if (![gradient.animationKeys containsObject:#"colors"]) {
[gradient addAnimation:animation forKey:#"colors"];
}
[self.background.layer addSublayer:gradient];
But it doesn't do anything. Can someone help me please?
I don't think "colors" are supported in CABasicAnimation.keypath.But how to meet the requirement, I still can't think of the answer.https://stackoverflow.com/questions/5459673/how-can-i-know-the-values-in-cabasicanimation-keypath

UIFont: how to animate strikethrough and match font style?

(a) How to animate strikethrough? I tried the following but not working.
-(void)strikeThrough
{
NSNumber *strikeSize = [NSNumber numberWithInt:1];
NSDictionary *strikeThroughAttribute = [NSDictionary dictionaryWithObject:strikeSize forKey:NSStrikethroughStyleAttributeName];
NSAttributedString* strikeThroughText = [[NSAttributedString alloc] initWithString:_label.text attributes:strikeThroughAttribute];
_label.attributedText = nil;
[UIView animateWithDuration:2.0
animations:^{ _label.attributedText = strikeThroughText; }
completion:^(BOOL finished){ }];
}
(b) Also, the style of strikethrough does not match with the font. For example, I use chalk font, but the strike line does not look like chalk. How to deal with it?
Thanks a lot for the help.
One way of having a have an animation for it is to use CATransition
NSNumber *strikeSize = [NSNumber numberWithInt:1];
NSDictionary *strikeThroughAttribute = [NSDictionary dictionaryWithObject:strikeSize forKey:NSStrikethroughStyleAttributeName];
NSAttributedString* strikeThroughText = [[NSAttributedString alloc] initWithString:self.mylabel.text attributes:strikeThroughAttribute];
self.mylabel.attributedText = nil;
CATransition *transition = [CATransition new];
transition.delegate = self;
transition.type = kCATransitionFromLeft;
transition.duration = 2.0f;
self.mylabel.attributedText = strikeThroughText;
[self.mylabel.layer addAnimation:transition forKey:#"transition"];
and for the completion block
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
//Check for CATransition here
}
For your second point check here . The link briefly gives you other ways to handle the way you want the strike to look like, you cannot change the font of it.
Hope this helps.

iOS simultaneous translation and scaling issue (CAAnimation)

I have a method which translates a view and optionally scales it while translating. I am using animation groups and adding the required animations as needed. Here are the animation cases working perfectly.
Translation (x or y axis or both) (OK)
Scaling (OK)
Translation while scaling (MOSTLY)
However, doing a translation while scaling works only when the starting scalefactor is 1. When the view is already scaled, I notice most of the transition jumping instead of animating, although the end result of scaling and translation is correct.
I am posting part of the code for clarification. The problem occurs only when previousZoomScale is not 1! I appreciate any directions you can point me to.
-(void)performAnimationForView: (AnimationType)animationType
WithDX: (CGFloat)dx
AndDY: (CGFloat)dy
Scale: (CGFloat)scale
Duration: (CGFloat)duration
{
CABasicAnimation *translateXAnimation = nil, *translateYAnimation = nil, *scaleAnimation = nil;
NSString* animationGroupName;
CGPoint newDxDy;
switch (animationType)
{
case kAnimationTranslateAndScale:
// set animation key
animationGroupName = #"translatescale";
translateXAnimation = [CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
translateXAnimation.repeatCount = 0;
translateXAnimation.autoreverses = NO;
translateXAnimation.removedOnCompletion = NO;
translateXAnimation.fillMode = kCAFillModeForwards;
translateXAnimation.fromValue = [NSNumber numberWithFloat: self.previousDxDy.x]; // previous point we're tranlsating from
translateXAnimation.toValue = [NSNumber numberWithFloat:dx]; // destination point
translateYAnimation = [CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
translateYAnimation.repeatCount = 0;
translateYAnimation.autoreverses = NO;
translateYAnimation.removedOnCompletion = NO;
translateYAnimation.fillMode = kCAFillModeForwards;
translateYAnimation.fromValue = [NSNumber numberWithFloat: self.previousDxDy.y];
translateYAnimation.toValue = [NSNumber numberWithFloat:dy];
newDxDy = CGPointMake(dx, dy);
self.previousDxDy = newDxDy;
// scaling animation
scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.autoreverses = NO;
scaleAnimation.repeatCount = 0;
scaleAnimation.removedOnCompletion = NO;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.fromValue = [NSNumber numberWithDouble: self.previousZoomScale]; // initial zoom scale
scaleAnimation.toValue = [NSNumber numberWithDouble:scale]; // target scale
self.previousZoomScale = scale;
break;
}
NSMutableArray* animationArray = [[NSMutableArray alloc] initWithObjects: nil];
if (translateXAnimation != nil)
[animationArray addObject: translateXAnimation];
if (translateYAnimation != nil)
[animationArray addObject: translateYAnimation];
if (scaleAnimation != nil)
[animationArray addObject: scaleAnimation];
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.duration = duration;
theGroup.removedOnCompletion = NO;
theGroup.fillMode = kCAFillModeForwards;
theGroup.repeatCount = 0;
theGroup.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
theGroup.animations = animationArray;
theGroup.delegate = self;
[theGroup setValue: animationGroupName forKey: #"animationtype"];
[self.backgroundImageView.layer addAnimation: theGroup forKey: animationGroupName];
}

Animate views(layers) using CAKeyframeAnimation in a loop and CAAnimationGroup

I am trying to animate several views (layers) using CAKeyframeAnimation and CAAnimationGroup. Each view to be animated is contained in an array. My idea is to loop through the array where CAKeyframeAnimation instance of each view would be added to an array of animations, which then would be added to the CAAnimationGroup. My goal is to animate each view one after another. As far as I understand I need to use the group in order to have a reference for the beginTime.
I am probably missing something obvious, but my code doesn't work
CAAnimationGroup *cardAnimationGroup = [CAAnimationGroup animation];
cardAnimationGroup.delegate = self;
cardAnimationGroup.removedOnCompletion = NO;
cardAnimationGroup.beginTime = 0.0f;
[cardAnimationGroup setValue:#"animation0" forKey:#"id"];
cardAnimationGroup.duration = 1.2f * [_myViewsArray count];
NSMutableArray *animationsArray = [[NSMutableArray alloc] init];
// Loop through views
for (UIView *cardView in _myViewsArray)
{
NSUInteger index = [_myViewsArray indexOfObject:cardView];
// my target origin
CGFloat yOffset = (-(cardView.frame.origin.y - _middleCardView.frame.origin.y) - index * 2);
CAKeyframeAnimation *translateCardPositionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.y"];
translateCardPositionAnimation.removedOnCompletion = YES;
//translateCardPositionAnimation.fillMode = kCAFillModeForwards;
translateCardPositionAnimation.beginTime = 1.2f * index;
translateCardPositionAnimation.duration = 1.2f;
translateCardPositionAnimation.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:yOffset],
[NSNumber numberWithFloat:yOffset],nil];
translateCardPositionAnimation.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
[NSNumber numberWithFloat:1.2f], nil];
[_containerCardView addSubview:cardView];
[_containerCardView sendSubviewToBack:cardView];
[animationsArray addObject:translateCardPositionAnimation];
[cardView.layer addObject:translateCardPositionAnimation forKey:#"id"];
if (index == [_myViewsArray count]-1) {
[cardAnimationGroup setAnimations:animationsArray];
}
}

How to make an image from iPhones library rotate in iOS?

I have an application which displays the artwork from a chosen song selected from the iPod library and would like to make it spin constantly.
The code so far is as follows:
-(void)chosen:(MPMediaItem*)song withObject:(id)obj
{
AppDelegate* app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
//ARTWORK STUFF
UIImage *artworkImage = [UIImage imageNamed:#"noArtworkImage.png"]
MPMediaItemArtwork *artwork = [song valueForProperty: MPMediaItemPropertyArtwork];
if (artwork)
{
artworkImage = [artwork imageWithSize: CGSizeMake (200, 200) ];
}
[app.viewController.artworkImage1 setImage:artworkImage];
}
Use Quartz Core Framework. This is untested but I imagine it should work in theory.
#import <QuartzCore/QuartzCore.h>
CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotation.duration = 6;
fullRotation.repeatCount = 1e100f;
[app.viewController.artworkImage1.layer addAnimation:fullRotation forKey:#"360"];

Resources