When I write my animation code within beginAnimation-commitAnimatin blocks I get a bouncing effect, however I don't get the same effect when I do the same animation with the method written in the subject. Here are two ways to do what I want:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelay:0.5];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationRepeatCount:2];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:
#selector(resetTheChickenProperties)];
theChicken.frame = CGRectMake(15, 330, 62, 90);
[UIView commitAnimations];
the way shown above the image (it's an egg) goes down in the y direction until it hits the ground and bounces back. Bouncing effect is clearly observed. But if I do the same thing with the help of the animateWithDuration:delay:options:animations:compeletion method the egg does not bounce. It rather seems like hung on a spring.
OK, I've found the subtle detail everyone needs to take note of in order to get the animation and transitions work with the method available in iOS 4 and later.When specifying the animation/transition options for the method we must use the constants with the word "Option" in it. So instead of writing
UIViewAnimationCurveEaseIn|UIViewAnimationTransitionCurlUp
we should write
UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionTransitionCurlUp
after fixing that the animation worked just fine. I was able to get the real bouncing effect
Related
I'm doing an iPhone app in iOS 5.
In that I'm expanding and reloading the uitableview while recognizing the pinch gesture.
It works great in simulator. But in device it works very slow. For example in device after the UIGestureRecognizerStateEnded only all the rows will be expanded, but in simulator the rows are expanding and reloading while UIGestureRecognizerStateChanged itself.
any suggestions for memory issues?
my code is here
if (pinchRecognizer.state == UIGestureRecognizerStateBegan) {
self.initialPinchHeight = rowHeight;
[self updateForPinchScale:pinchRecognizer.scale];
}
else {
if (pinchRecognizer.state == UIGestureRecognizerStateChanged) {
[self updateForPinchScale:pinchRecognizer.scale];
}
else if ((pinchRecognizer.state == UIGestureRecognizerStateCancelled) || (pinchRecognizer.state == UIGestureRecognizerStateEnded)) {
}
}
-(void)updateForPinchScale:(CGFloat)scale{
CGFloat newHeight = round(MAX(self.initialPinchHeight * scale, DEFAULT_ROW_HEIGHT));
rowHeight = round(MIN(30.0, newHeight));
/*
Switch off animations during the row height resize, otherwise there is a lag before the user's action is seen.
*/
BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
[self.tableView beginUpdates];
NSArray *visibleRows = [self.tableView indexPathsForVisibleRows];
[self.tableView reloadRowsAtIndexPaths:visibleRows withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
[UIView setAnimationsEnabled:animationsEnabled];
}
Before trying to figure out what to optimize, you should measure where the problem is. You can do this using the Time Profile and Core Animation instruments. Use Xcode's Product menu and select Profile. Make sure you profile while you are attached to the device, which, as you have noticed, has different performance characteristics than the simulator.
The Time Profile instrument will help you identify work done on the CPU. The Core Animation instrument will help you identify work being done by Core Animation both on the CPU and GPU. Pay attention to the Core Animation instrument's Debug Option checkboxes. They're a little cryptic, but will help you visually identify the parts of your UI that are making the Core Animation do a lot of work.
Documentation on the different instruments available for iOS are here.
I also recommend the WWDC video covering Core Animation.
i want to create a basic push view scenario. I have the following code but it does not work. Nothing happens. Could someone tell me why?
testController *screen2 = [[testController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:screen2 animated:YES];
[screen2 release];
You should try to do an NSLog to see if screen2 is nil. Chances are it is not being appropriately loaded from a corresponding nib for some reason. As an aside, I'd highly recommend sticking to the convention of capitalizing class names. Speaking of which, did you maybe call the nib file TestController.nib? (That would cause the problem.)
I'm analyzing an image which takes some time and meanwhile I want to display a progress indicator. For this I'm using MBProgressHUD.
It almost works... I get this error: "Modifying layer that is being finalized". I guess it's due to the fact that I do pushViewController not in my main thread. Am I right? Any ideas on how to correct this issue?
My code:
- (IBAction)buttonReadSudoku:(id)sender
{
mbProgress=[[MBProgressHUD alloc] initWithView:self.view];
mbProgress.labelText=#"Läser Sudoku";
[self.view addSubview:mbProgress];
[mbProgress setDelegate:self];
[mbProgress showWhileExecuting:#selector(readSudoku) onTarget:self withObject:nil animated:YES];
}
- (void)readSudoku
{
UIImage *image = imageView.image;
image = [ImageHelpers scaleAndRotateImage:image];
NSMutableArray *numbers = [SudokuHelpers ReadSudokuFromImage:image];
sudokuDetailViewController = [[SudokuDetailViewController alloc] init];
[sudokuDetailViewController setNumbers:numbers];
[[self navigationController] pushViewController:sudokuDetailViewController animated:YES];
}
Define a new method to push your detail view controller and use -performSelectorOnMainThread:withObject:waitUntilDone: to perform it on the main thread. Don't try to make any UI changes from other threads.
All UI changes must be in the main thread, as you note. Rather than you off-main-thread method make any changes to the UI, send an NSNotification to the current viewController telling it to do the UI work.
This is an especially good route if you're crossing an MVC border, or if you already have a viewController that knows what to do so that writing a separate method results in duplicate code.
I'm using the well-known pattern to create an UIImage from an UIView:
+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, [[UIScreen mainScreen] scale]);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
Now, my problem is that i have a very complex view with a lot of subviews on it, so this process of conversion takes about 3+(!!!) seconds.
I tried forking it into another thread that run in the background and it really did improve the performance.
The only problem is that as can I remember, it is not allowed to make UI related stuff not in the main thread.
Am I wrong and this is perfectly fine?
Or - if i'm right - what can be done to improve performance? is there any other method that i can use in a different thread but does the same work?
Thanks a lot!
In the end i just did it in another thread, and everything is working fine.
Up to iOS 3.2, I used this kind of code to load UIImageView image in background, and it worked fine...
Code:
- (void)decodeImageName:(NSString *)name
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *newImage = [UIImage imageNamed:name];
[myImageView setImage:newImage];
[pool release];
}
...
[self performSelectorInBackground:#selector(decodeImageName:) withObject:#"ID"]
... even if [UIImageView setImage:] was not thread-safe !
But since iOS 4, it doesn't work any more... Images appear on screen two seconds after setImage call. And if I do a [myImageView performSelectorOnMainThread:#selector(setImage:) withObject:newImage waitUntilDone:YES] instead of [myImageView setImage:newImage], images appear immediately but seem to be re-decoded again on-the-fly (ignoring the previous [UIImage imageNamed:] which should have already decoded the image data), causing a pause on my main thread... Even if documentation says The underlying image cache is shared among all threads..
Any thought ?
Don’t do it in the background! It’s not thread-safe. Since an UIImageView is also an NSObject, I think that using -[performSelectorOnMainThread:withObject:waitUntilDone:] on it might work, like:
[myImageView performSelectorOnMainThread:#selector(setImage:) withObject:newImage waitUntilDone:NO];
And it’s UIImage which is newly made thread-safe. UIImageView is still not thread-safe.
performSelectorInBackground: runs a selector in a background thread. Yet setImage: is a UI function. UI functions should only be run on the main thread. I do not have insight into the particular problem, but this is the first gut feel about this code, and it may be that iOS4 handles the (non-supported) mechanism of running UI functions in background threads somehow differently.
If you're using iOS 4.0, you should really consider reading up on blocks and GCD. Using those technologies, you can simply replace your method with:
- (void)decodeImageName:(NSString *)name
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *newImage = [UIImage imageNamed:name];
dispatch_async(dispatch_get_main_queue(), ^{
[myImageView setImage:newImage];
}
[pool release];
}
Let's quote:
#property(nonatomic, readonly) CGImageRef CGImage
Discussion
If the image data has been purged because of memory constraints, invoking this method forces that data to be loaded back into memory. Reloading the image data may incur a performance penalty.
So you might be able to just call image.CGImage. I don't think CGImages are lazy.
If that doesn't work, you can force a render with something like
// Possibly only safe in the main thread...
UIGraphicsBeginImageContext((CGSize){1,1});
[image drawInRect:(CGRect){1,1}];
UIGraphicsEndImageContext();
Some people warn about thread-safety. The docs say UIGraphics{Push,Pop,GetCurrent}Context() are main-thread-only but don't mention anything about UIGraphicsBeginImageContext(). If you're worried, use CGBitmapContextCreate and CGContextDrawImage.