I'm trying to slide a gridded NSView up, revealing another row from another NSView. Then, once the animation is done, the second NSView will dissapear, and the first will immediately move back to its original position with the new data.
It works 4/5s of the time. The only error I'm getting is when the first view doesn't reset to the original position; it stays were the animation finishes.
Here's the basic idea (I don't have the code here):
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
[[myView animator] setFrame:newPos];
} completionHandler:^{
[myView setFrame:originalPos];
}];
I'm having trouble Googling since runAnimationGroup:completionHandler is new in Lion.
Related
I am working on a small application on Mac that I need to create customed cursor and move it. I used NSImageView to implement it. However when I call setFrameOrigin (the same to setFrame) it will leaves images on the previous place.
Here is my code:
#property (nonatomic, strong) NSImageView *eraserView;
this is the define
_eraserView = [[NSImageView alloc] initWithFrame:CGRectMake(100, 100, 32, 32)];
_eraserView.image = [NSImage imageNamed:#"EraserCursor"];
[self.view addSubview:_eraserView];
[_eraserView setHidden:YES];
here is the initialization. Everything goes well until now but:
- (void)setImageatPoint:(NSPoint)point
{
[_eraserView setFrameOrigin:point];
}
- (void)hidePenImage
{
[_eraserView setHidden:YES];
}
- (void)unhidePenImage: (BOOL)isEraser
{
[_eraserView setHidden:NO];
}
These are methods I use to change the state of the NSImageView. They will be called by another class using delegate when corresponding events of trackpad occurs.
However every time I change the state of the NSImageView, it seems like it is drawn on the superview.
I debugged it and found there was no extra subviews. And when I use setHidden it has no effect on those tracks. I think it somehow did something to the CALayer, but I have no idea how to fix it.
Screenshots would help but in general if you move a view or change the area of the view that is drawn, you need to redraw.
To do this it kind of depends on how your drawing happens. Calling setNeedsDisplay may not be enough if your implementation of drawRect only draws a sub rect of the view bounds. Cocoa only draws what it is told to draw.
You can erase sections of the view that should be empty by drawing (filling) where it should be empty. That means drawing a color ( NSColor clearColor if nothing else) in the area that was previously drawn.
So, I have the following code to show my NSWindow:
[_window makeKeyAndOrderFront:self];
[NSAnimationContext beginGrouping];
[[_window animator] setAlphaValue:1.0];
[[_window animator] setFrame:NSMakeRect([[NSApp currentEvent] window].frame.origin.x - 102, [[NSApp currentEvent] window].frame.origin.y - 238, _window.frame.size.width, _window.frame.size.height) display:YES];
[NSAnimationContext endGrouping];
This code is called right after the user has clicked on the app's status bar icon, that's why I use the [[NSApp currentEvent] window].frame.origin.y/x to get the location of the status bar icon.
This code runs perfectly but, sometimes, it's very laggy and "jumpy" and I don't know why.
Any ideas about this and how to fix it?
The NSWindow animator uses NSAnimation, which means that it rapidly fires a timer to animate the frame of the window. At each frame of the animation, every view inside the window is redrawn. If you have large views with somewhat complex view hierarchies, the performance is quite bad and there's no real way to work around it.
I would recommend JNWAnimatableWindow as a substitute for the default NSWindow animator, as it uses a Core Animation CALayer to perform animations on and therefore is much smoother.
I'm using the iCarousel library to show a coverflow-like UI. Animating the iCarousel subviews directly tends to blow up, so I create a wrapper for my animation and stick a subview inside of it which looks like the tapped cover. Then I hide the actual iCarousel view, and animate the fake cover on top of it.
I'm using UIView's transitionWithView:duration:options:animations:completion: method, but I'm running into some trouble. When animating another view onscreen for the first time, the view appears without any animation. When animating out, the view correctly flips and hides.
My view hierarchy is as follows:
main view, loaded from a nib
wrapper view
view to transition from
view to transition to
The view I'm transitioning to is a UINavigationController's view which contains a UITableViewController subclass. Instead of the initial animation, the UINavigationController appears and then the view grows upward a little, as if it's taking over the space otherwise occupied by a status bar.
Any idea why the table view might be animating like this? (I suspect containment APIs and/or wantsFullscreen, although I'm not explicitly using them. I simply install the views into the wrapper via addSubview:.)
Here's my "flip in" code, that animates every time but the first:
- (void) flipInWithCompletion:(MBTransitionCompletion)completion {
BOOL displayingPrimary = [self isDisplayingPrimaryView];
UIView *frontView = [self frontView];
UIView *backView = [self backView];
UIView *wrapperView = [self wrapperView];
[wrapperView addSubview:frontView];
[UIView transitionWithView:wrapperView
duration:0.8
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[wrapperView addSubview:backView];
[frontView removeFromSuperview];
}
completion:^(BOOL finished) {
[self setIsDisplayingPrimaryView:!displayingPrimary];
if (completion) {
completion();
}
}
];
}
What might cause the table view to grow instead of allowing the wrapper to flip?
Edit:
I've made a video demoing the exact problem.
Sounds like the view isn't properly loaded before it starts to animate in. I think I remember having a similar problem before. Try adding it to your view first, then remove it, and try to animate it into place to see if that fixes it.
That's not really a solution though, but it should give you a clue as to what might be wrong.
I have an odd problem in one of my chain animations. I have block object for each of the blocks on my screen (26 of them), and when the user presses them I perform a flip. It works great, but I'm adding a scale animation before I do these to make them about 50% bigger on the screen. So, my sequence is:
enlarge
delay (let user see the large image)
spin-out 90% (removes block from view) - using transform.rotation.y
change image & spin-in
shrink.
I have setup the window view controller to be a delegate of these blocks, such that it can pass a counter to the blocks so I can position the right sublayer on the top (using setZposition).
It all works great, except when I have 2 blocks on positioned above/below each other on the screen, such that the enlarge will cause them to overlap, and then when the spin-out animation starts, it immediately has the right side of the block pop behind the block below it. I've tried changing the animation to transform.rotation.x and get the same behavior when the blocks are side-to-side.
I'm not sure if it's an iOS bug of if I'm just not doing something correct. Any suggestions are greatly appreciated. Here is the spin-out method:
- (void)spinOut:(id)sender
{
NSTimeInterval animationTime=0.85;
[UIView animateWithDuration:animationTime
delay:0
options: UIViewAnimationOptionCurveLinear
animations:^{
// setup the animation to spin the current view out
[CATransaction begin];
[CATransaction setDisableActions:YES];
CABasicAnimation *spinOut = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
[spinOut setDelegate:self];
[spinOut setDuration:animationTime];
CAMediaTimingFunction *tf = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[spinOut setTimingFunction:tf];
[spinOut setFromValue:[NSNumber numberWithFloat:M_PI * 0.0]];
[spinOut setToValue:[NSNumber numberWithFloat:M_PI * 0.5]];
[spinOut setRemovedOnCompletion:NO];
[spinOut setFillMode:kCAFillModeForwards];
// setup variables used to roll in the next view on animation completion.
[pageView.layer addAnimation:spinOut forKey:kMyVeryOwnABCsSpinOutKey];
[CATransaction commit];
}
completion:^(BOOL finished){
[self setFlipOutAnimationTimer:[NSTimer scheduledTimerWithTimeInterval:animationTime target:self selector:#selector(spinIn:) userInfo:nil repeats:NO]];
}];
}
OK - this is an old question, but I kindof figured out this problem. Since I was flipping these images around the Y axis, I was able to change the animated image's Zposition. In my startAnimation method, I added:
float pageLevelLayerPosition;
// logic to set the zposition variable 100 more than anything nearby
_originalImageViewZPosition = bigImageView.layer.zPosition;
[bigImageView.layer setZPosition:pageLevelLayerPosition];
and then when in the animationDidStop, I added the relevant line of code to reset it back to 0 where I was removing the animation. I had some code to manage the variable so that I was assured that the number was greater than anything nearby.
I guess if I ever get into 3-D animation, then I'll need to control this much more closely, but that is what was causing my problem.
I've noticed this functionality in the iPhone Mail.app and SMS.app apps, but I'm unsure how to implement it myself.
In a standard UITableView, when a user taps the 'Edit' button and the deletion buttons move into position, as the content views are sliding to the right, they perform a quick fade transition where they are replaced by thinner versions of themselves (to account for the space the deletion button is taking up).
I initially thought this was done by calling the following method when editing mode is enabled:
[self.tableView reloadRowsAtIndexPaths: [tableView indexPathsForVisibleRows] withRowAnimation: UITableViewRowAnimationFade];
However this cannot be right as it also fade transitions the delete button itself as well as any accessory views the cell has (suffice it to say, it looks quite odd).
I was wondering, how would it be possible to force the content view to redraw itself, and to then fade the new drawn version over the old one when the table enters edit mode?
UPDATE:
Thanks to Caleb's answer, this is the final block of code that allowed me to get what I was after:
My final solution was to subclass UITableViewCell and then apply the following code to the setEditing accessor:
-(void) setEditing: (BOOL)editing animated: (BOOL)animated
{
[super setEditing: editing animated: animated];
CATransition *animation = [CATransition animation];
animation.duration = 0.2f;
animation.type = kCATransitionFade;
//redraw the subviews (and animate)
for( UIView *subview in self.contentView.subviews )
{
[subview.layer addAnimation: animation forKey: #"editingFade"];
[subview setNeedsDisplay];
}
}
I probably should just clarify as well. Since performance is of utmost importance, everything inside my cell's contentView is being rendered through CG (ie drawRect:), so I can't control any elements being drawn in there specifically.
That animation happens when you call -setEditing:animated: on the table. The table then calls -setEditing:animated: on each of the visible cells. It looks like the standard behavior for a table cell is to adjust the size of the content views, shift them right, and remove the right accessory. If you have your own cells and want a different look in editing mode, I think you can override -setEditing:animated: to adjust the cell content as you like.