I'm completely new to Core Animation, CALayer and all this stuff, so bear with me.
I have a custom NSTextField using as a Label. I would want the content to animate it's position, so the whole string get's visible if it's too long for the Labels width.
Now, the animation itself is working fine. I've implemented this with CABasicAnimation:
- (void)awakeFromNib {
CALayer *newLayer = [CALayer layer];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithPoint:NSMakePoint(0, 0)]];
[animation setToValue:[NSValue valueWithPoint:NSMakePoint(-self.attributedStringValue.size.width, 0)]];
[animation setDuration:5.0];
[animation setRepeatCount:HUGE_VAL];
[newLayer addAnimation:animation forKey:#"position"];
[self setLayer:newLayer];
[self setWantsLayer:YES];
}
The only problem is, that the drawRect: method only draws what's on the screen.
So I thought I would override the drawRect: method to draw the whole attributed string. But if I do this, nothing get's drawn at all...
Can anyone point me into the right direction?
Thank you!
In general, you want to avoid overriding drawRect if at all possible, especially for CALayer objects that you are animating. That tends to lead to really dreadful performance.
What do you mean "the drawRect: method only draws what's on the screen?"
It only draws the portion of the string that's currently visible?
I ended up using an NSTimer. Not the most beautiful solution, but at least it works.
Related
in my app I want to change the background of an uiview based on camera, All works properly but the problem is the background is changed too sharply (I think because of the focus).
So there's a way to change the background color of a UIView smoothly?
I actually use this code:
dispatch_async(dispatch_get_main_queue(), ^ {
[myview setBackgroundColor:[UIColor colorWithRed:red green:green blue:blue alpha:1]];
});
The UIVIew animation solution doesn't work beacause the changes are too fast.
Just before setting the background colour do something like;
CATransition *transitionAnimation = [CATransition animation];
[transitionAnimation setType:kCATransitionFade];
[transitionAnimation setDuration:0.2f];
[transitionAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[transitionAnimation setFillMode:kCAFillModeBoth];
[myview layer addAnimation:transitionAnimation forKey:#"fadeAnimation"];
Hopefully this will give you a gentle fade in colour change.
I've got an NSButton called _indicatorButton with a PNG image (basically a green LED light as PNG set with setImage:) without border and text. I'm trying to make it "flash" with the following code:
- (void)animateButton
{
NSLog(#"Animating…");
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"opacity"];
[animation setFromValue:[NSNumber numberWithFloat:1.0]];
[animation setToValue:[NSNumber numberWithFloat:0.0]];
[animation setDuration:0.3f];
[animation setTimingFunction:[CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setAutoreverses:YES];
[animation setRepeatCount:20000];
[_indicatorButton.layer addAnimation:animation forKey:#"opacity"];
}
…but nothing happens. The method gets called and the link in the XIB is OK, since changing the image on the button programmatically works fine.
Any hints why it doesn't blink at all?
For backwards compatibility, views on OS X don't use Core Animation layers as their backing store by default. The reason nothing is happening is likely that layer of your button is nil.
You can tell your view that it should be layer-backed using myView.wantsLayer = YES;. Please read the documentation for wantsLayer.
I have a NSTextField as a label, showing a string.
I want to animate this label from right to left, if the content of it is too large to be displayed at once.
I've done this with an NSTimer so far, it works, but it's just not a very good solution.
The labels are displayed in an NSTextFieldCell, in a Table View.
They often get out of sync, and I guess it's just eating up a lot of CPU/GPU resources.
Is there another way with Core Animation to do this?
I have tried it with layers, as you can see right here:
CALayer and drawRect
but I didn't get it working either.
I would really appreciate your help.
You can simply animate the position of NSTextField with animator like
[[textField animator] setFrameOrigin:NSMakePoint(x,y)];
you can also embed it in "CATrancation" code like this:
[CATransaction begin];
[CATransaction setAnimationDuration:0.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[[textField animator] setFrameOrigin:NSMakePoint(x,y)];
[CATransaction commit];
if you need animation delegate, you can use CABasicAnimation
CABasicAnimation* animation = [CABasicAnimation animation];
animation.delegate = self;
NSDictionary *animations = [NSDictionary dictionaryWithObjectsAndKeys:animation,#"frameOrigin",nil];
[textField setAnimations:animations];
[[textField animator] setFrameOrigin:NSMakePoint(x,y)];
Delegate methods are
- (void)animationDidStart:(CAAnimation *)theAnimation;
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag;
If you need to mask your text field, just embed it in other NSView.
First, animate the label using one of the functions offered in the other answers.
Then, if you want to display another view on the sides without overlapping, you can:
Insert the label in a subview with the limits you wish
Use bringSubviewtoFront: or sendSubviewToBack: to make sure your label stays in the back
I'm trying to bring the ripple effect seen in the dashboard application to iphone. My idea is whenever i place a button in the layout view there should be ripple effect around the button.
However, I`m able to bring the ripple effect for the whole view but I needs the effect only around the button. I don know where I went wrong. I tried the following code.
LayoutButton *tempLayoutButton=[[LayoutButton alloc] initWithObject:object];
tempLayoutButton.center=copyImage.center;
[layoutView addSubview:tempLayoutButton];
CALayer *templayer=[CALayer layer];
tempLayoutButton.layer.masksToBounds=NO;
templayer.frame=CGRectMake(0,0,50,50);
[tempLayoutButton.layer addSublayer:templayer];
CATransition *animation = [CATransition animation];
animation.delegate = self;
animation.duration = 1.0f;
animation.timingFunction = UIViewAnimationCurveEaseInOut;
animation.type = #"rippleEffect";
[templayer addAnimation:animation forKey:#"animation"];
[tempLayoutButton release];
[tempLayoutButton.layer addSublayer:templayer];
If I replace templayer with LayoutView layer I can see the riple effect from the whole view. Can anyone tell me the solution
Thanks in advance
I found the answer in one of apples mailing list. The animation is apple`s own animation and no one can use that. And there is no guarantee for the animation to appear properly in the screen. If we need the same type of animation we need to manipulate the layers and get the animation
I am using a CALayer to display a path via drawLayer:inContext delegate method, which resides in the view controller of the view that the layer belongs to. Each time the user moves their finger on the screen the path is updated and the layer is redrawn. However, the drawing doesn't keep up with the touches: there is always a slight lag in displaying the last two points of the path. It also flickers, but only while displaying the last two-three points again. If I just do the drawing in the view's drawRect, it works fine and the drawing is definitely fast enough.
Does anyone know why it behaves like this? I suspect it is something to do with the layer buffering, but I couldn't find any documentation about it.
[UIView new] is simply shorthand for [[UIView alloc] init].
Give the following before setNeedsDisplay method::
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:someDelay] forKey:kCATransactionAnimationDuration];
[aLayer setNeedsDisplay];
[CATransaction commit];
You might have better luck using a layer hosting view.
Instead of using the drawLayer:inContext: method, setup the view you want and add a CALayer to it:
UIView *layerHosting = [[UIView alloc] initWithFrame:frame];
[layerHosting setLayer:[[CALayer new] autorelease]];
Hope that helps!