I'm developing an app that would display images and change them according to the user's action. I've created a subclass of UIView to contain an image, an index number and an array of image names. The code is like this:
#interface CustomPic : UIView {
UIImageView *pic;
NSInteger index;
NSMutableArray *picNames; //<-- an array of NSString
}
And in the implementation part, it has a method to change the image using a dissolve effect.
- (void)nextPic {
index++;
if (index >= [picNames count]) {
index = 0;
}
UIImageView *temp = pic;
pic.image = [UIImage imageNamed:[picNames objectAtIndex:index]];
temp.alpha = 0;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDuration:0.25];
pic.alpha = 0;
temp.alpha = 1;
[UIView commitAnimations];
}
In the viewController, there are several CustomPic which would change the images depends on users' choice. The images would change as expected with the fade in/out effect, but the animation performance is really bad. I've tested it on an iPhone 3G, the Instruments shows that the animation is only 2-3FPS! I tried many methods to simplify and modify the codes but with no hope. Is there something wrong in my code or in my concept? Thanks for any help.
P.S. all the images are 320*480 PNGs with a max size of 15KB.
In that code snippet you are only using one UIImageView:
UIImageView *temp = pic;
So this line does nothing:
pic.alpha = 0;
To fade one image in and another image out, you need two instances of UIImageView.
To speed things up, you might want to load the next image ahead of time. Set a delegate for the animation, and when it completes load the image that will be displayed in the next call of nextPic.
Edit:
If the images are not transparent, try setting the pic.opaque = YES; so the view can ignore underlying views. If that is not an option, try having as few views as possible between the fading view and a full screen opaque view. At each step of the fade, the view must be composited with every underlying view until an opaque view is found.
If you have many transparent views under the fade, consider making a temporary composite of them all, the equivalent of flatten layers in photoshop, and placing the composite as an image in a full screen opaque view before starting the fade. It may delay starting the fade a moment, but the fade itself should be smoother.
Are you running iPhone OS 4 beta? I find the beta OS much slower on the older generation devices like iphone 3g
Try change
[UIView setAnimationDuration:0.25];
to
[UIView setAnimationDuration:0.025];
and see if that's something you are looking for...
With an animation duration of .25 of a second, you can be guaranteed to get < 4 FPS. What FPS are you targeting?
Related
I started studying objective-c using the iPhone and iPad apps for Absolute Beginners by Rory Lewis book but i got stuck on the 5th chapter.
I want to make a button that shrinks an image.
My problem is, that after I wrote all the code, the image shrinks to the point (0, 0) of the UIImageView (the top left), while in the video the image shrinks to its center. I've done some research and found out that CGAffineTransform uses the center of the object to make translations, rotations etc.
So why does it not work in my case?
I have the latest Xcode version and haven't done anything strange.
I hope I've been clear. Sorry for my English.
EDIT
Sorry for the code, but i wrote the question from my phone.
Anyway the incriminated part goes something like this
- (IBAction)shrink:(id)sender {
if(hasShrink == NO){
[shrinkButton setTitle:#"Grow" forState:UIControlStateNormal];
}
else if(hasShrink == YES){
[shrinkButton setTitle:#"Shrink" forState:UIControlStateNormal];
}
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
myIcon.transform = CGAffineTransformMakeScale(.25, .25);
hasShrink = YES;
[UIView commitAnimations];
}
All the animations are correctly written and the app does work, it just shrinks to the top left. Why is the point not set to the center of the UIImageView by default like it should be?
EDIT:
I figured out it was a problem caused by AutoLayout.
Once disabled everything went smooth ;)
If you are transforming a view mangled managed by auto-layout, you may experience some strange side-effects. See this answer for more details.
The solution I ended up employing was to encapsulate the view I wanted to transform inside another view of exactly the same size. The outer view had the appropriate layout constraints applied to it while the inner view was simply centered in its container. Applying a CGAffineTransform scale transform to the inner view now centers properly.
Old question... but just in case others come looking:
The CGAffineTransform acts (rotates, scales) around an anchorPoint.
If you are sizing to 0, 0 then your anchor point is set to 0, 0. If you want it to size to a different point, such as the center of the image, you need to change the anchor point.
The anchor is part of a layer
So if you have a UIImageView called imageview, you would make a call like this to set the anchor to the center of imageview:
[imageview.layer setAnchorPoint:CGPointMake(0.5, 0.5)];
Try something like this:
CGAffineTransform t = CGAffineTransformMakeScale(0.5, 0.5);
CGPoint center = imageView.center; // or any point you want
[UIView animateWithDuration:0.5
animations:^{
imageView.transform = t;
imageView.center = center;
}
completion:^(BOOL finished) {
/* do something next */
}];
Next time show your code. It will be easier to help you.
Check this project: https://github.com/djromero/StackOverflow/archive/Q-13706255.zip
You must study how autolayout and autoresize affect your views. In the project above both are disabled to let you see how it works.
Does anyone know how to animation an image in Objective-C like it is done at
Famous Brands (Requires Flash Player to see)
Take your image and slice it in parts with your favorite photo app. Create an imageView for each image and add it to a new UIView which play the role of the image container. Now add this UIView to your main view.
If you want to animate it simply iterate through all imageViews inside your container view and start the following animation:
[UIView animateWithDuration:0.3
delay: i * 0.3
options:UIViewAnimationCurveEaseInOut
animations:^{
currentImageView.alpha = 0.0;
currentImageView.transform = CGAffineTransformMakeScale(0.0, 1.0);
}
completion:nil
];
Where currentImageView is the current imageView in the loop and i the iterator var.
It would be even better to slice the original Image with CoreGraphics, so you don't have to do it manually as stated above but that's a little bit more work.
in my application, I made a transition from FirstViewController to FormViewController.
[self presentModalViewController:formViewController animated:YES];
in FormViewController I want to display four images, each time the user clicks an image, I note the number or the name of the image clicked in a dictionary and I display the four next images. So, there are about 8 sets of 4 images.
So my questions are, each four images shoul be showed in a new class, or i should put all the images in FormViewController and use .hidden ?
it's possible to make an animation when changing images ? not an animation of all the view , only the images.
I'd use a single xib and a single class. The class needs some kind of data model to hold the list of available images, the previously chosen images and the current visible ones.
When the user clicks an image, update the data and show new ones with something like this to fade-out old image and fade-in the new one.
/* ARC code */
-(void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
/* destroy the transition view, set the image first */
UIImageView *transitionImageView = (UIImageView *)context;
self.pic.image = transitionImageView.image;
[transitionImageView removeFromSuperview];
transitionImageView = nil;
}
- (void)clickHandler:(id)sender {
/* temporary view for the animation */
UIImageView *transitionImageView = [[UIImageView alloc] initWithFrame:self.pic.frame];
transitionImageView.image = <# new UIImage #>;
transitionImageView.alpha = 0.0f;
[self.view addSubview:transitionImageView];
[UIView beginAnimations:#"UpdateImages" context:transitionImageView];
[UIView setAnimationDuration:2.0f];
transitionImageView.alpha = 1.0f;
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
[UIView commitAnimations];
}
Adapt it for your four images.
If you want more fancy animations you can have two sets of 4 UIImageViews and use them to show and animate the image transitions.
you can show the images in the same class and personally I believe that is what u shud do as it will be easier to manage ur code , and yes you can animate the image changes.
I'm trying to recreate the flipping album animation in iPod.app on the iPad (Music.app in iOS 5). Getting the flipping to work is easy, but I'm having trouble with positioning and enlarging the album. Right now I'm using this code:
[UIView transitionWithView:self.containerView duration:5.0 options:UIViewAnimationOptionTransitionFlipFromLeft | UIViewAnimationOptionShowHideTransitionViews animations:^(void) {
self.firstView.hidden = YES;
self.secondView.hidden = NO;
self.containerView.frame = CGRectMake(600.0, 0.0, 168.0, 1004.0);
} completion:nil];
The flipping works, but there is something strange going on in the animation. The container view does indeed move and resize, but the subviews (firstView and secondView) do not.
Because the superview clips to its bounds (even though I set that to NO; another strange thing!), it appears like the subviews are getting "cut" when the container view moves.
I hope you guys understand the problem. Any Core Animation hero who can help me with this? Thanks.
Have you set the autoresize mask on the child views? Those are used to automatically resize or reposition a view when its superview’s bounds change.
In the app I'm working on the user taps on a tableview to zoom it up to full view from a "thumbnail" or a miniature view. Everything is working great except for a somewhat annoying animation glitch or whatever. The thing is I'm using the code below:
if ([subview respondsToSelector:#selector (name)] && [subview.name isEqualToString:self.labelListName.text])
{
[self.tabBarController.view addSubview:subview];
CGRect frame = CGRectMake(35, 78, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
subview.frame = frame;
CGAffineTransform scale = CGAffineTransformMakeScale(1.39, 1.39);
CGAffineTransform move = CGAffineTransformMakeTranslation(0,44);
CGAffineTransform transform = CGAffineTransformConcat(scale, move);
[UIView animateWithDuration:0.15
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
subview.transform = transform;
}
completion:^(BOOL finished){ [self goToList],subview.hidden = YES; }];
}
- (void)goToList
{
self.gotoWishList = [[WishList alloc] initWithNibName:#"WishList" bundle:nil];
self.gotoWishList.hidesBottomBarWhenPushed=YES;
self.gotoWishList.name = self.labelListName.text;
[self.navigationController pushViewController:self.gotoWishList animated:NO];
self.gotoWishList.scrollLists = self;
[WishList release];
}
And when doing the animation the transfer between the zoom view and the actual view the user is going to interact with is not completely perfect. The text inside the cell jumps a little when switching between the views. The problem lies in the translation matrix. If I skip that I can get the animation to work perfectly but then of course I need to move the miniature view down in the GUI which is not an option. If I instead do the animations in another order (move, scale) then it works better. I still get a jump at the end but it looks better, as everything jumps...and not just the text.
So...basically my question is how can I make this animation fluent. I read that the CGAffineTransformConcat still does each animation separately, and I really need both animations (scaling and moving the list) to be ONE fluent animation.
Thanks for any tips!
I think you will have to nest views/graphic-context to get what you want. The animation system doesn't support simultaneous animations because the mathematics of doing so requires an exponential amount of computational power. You might be able to trick it by sliding one view while enlarging the other.
I'm not sure about that as I have never had need to try it.
You might also be getting a jerk or skip from the tableview itself. The bounce at the top and end of scrolls can produce effects if you radically resize the table on the fly. I would turn all that off and see if you still have the problem. You might also want to test on the view independent of the tableview to make sure the problem is with the animations and not the tableview moving itself.