I have a couple of UIViews with UIImageViews on them. When I display a view the first time, there is a noticeable lag (0.5 second or so) before it is shown. When shown again everything works.
I have managed to work around this by adding all views and then removing them. Like this:
[self performSelectorOnMainThread:#selector(addAndRemoveAllViews)
withObject:nil
waitUntilDone:NO];
The function loops over all views and [addSubview:view] followed by [view removeFromSuperview]. This seems to trigger the images to load while not being shown.
Questions:
This all feels like a workaround to me. Is there a better approach to handle these types of things?
Is it possible to get a callback or something when it's completed? I want to run an animation when done.
Update:
Images are created from NSData like this:
[[UIImageView alloc] initWithImage:[UIImage imageWithData:rawData]];
It seems very likely the image is larger than the image view. If possible shrink it down to the size the image view needs.
The initial delay is loading the image from storage, after that you are using a cached version which is why it is faster.
Related
I am animating the ImageViews then the user taps a button. I have more than 40
images. The code I have used is
arr3 = [[NSArray alloc]initWithObjects:[UIImage imageNamed:#"Aperture_00000.png"],
[UIImage imageNamed:#"Aperture_00001.png"],
[UIImage imageNamed:#"Aperture_00002.png"],
...
[UIImage imageNamed:#"Aperture_00023.png"], nil];
imgv.animationImages = arr3;
imgv.animationDuration=2.0f;
imgv.animationRepeatCount =1;
The method to start the animation is:
-(void)animate {
[imgv startAnimating];
}
But it takes a lot of time when the user presses the button for the first time. What could be the solution for this?
The reason is following code:
arr3 = [[NSArray alloc]initWithObjects:[UIImage imageNamed:#"Aperture_00000"],[UIImage imageNamed:#"Aperture_00001"],[UIImage imageNamed:#"1.png",#"2.png",#"3png",#"4.png",#"5.png",#"6.png",#"7.png",#"8.png",#"9.png",#"10.png",#"11.png",#"12.png",#"13.png",#"14.png",#"15.png",];
What you can do is load this array somewhere else.
Note : This is a very memory consuming way of loading the images. Your app will definitely crash after you visit this class 2-3 times. Instead of this use some alternative. The easiest alternative I can suggest is load a image on UIImageView and change the image periodically. It'll give you animation effect.
I would suggest you rather doing it programmatically you should create animated gif image from the images. Here is online tool you can set speed and other parameters
http://picasion.com/
and use
https://github.com/arturogutierrez/Animated-GIF-iPhone
UIImageView category to display that gif image in that case you can save your CPU time.
Unhide the imageView when you want to play animation and hide when you want to stop.
I would suggest to give that at least a try.
I have a UIViewController that I'm nesting inside another UIViewController (iOS 4.3+),
it is displayed just fine except one thing - a lot of excessive shadow.
I have tried removing it with setShadowRadius etc, but no luck..
This is the code I use to create it:
RDPreviewViewController* preview = [[[RDPreviewViewController alloc] initWithNibName:#"RDPreviewViewController" bundle:[NSBundle mainBundle]] autorelease];
[preview.view.layer setShadowOpacity:0.0];
[preview.view.layer setShadowRadius:0.0];
[preview.view.layer setColor:nil];
[preview.view setFrame:CGRectMake(0, 100, 320, 264)];
[self.mainView addSubview:preview.view];
And here's the result:
How do I remove it?
I suspect - and this is a theory - that given what you've said that somehow your PNG image with the subtle shadow is being loaded multiple times. This is why your shadow appears much darker than you're expecting - several identical PNGs are being overlaid on top of each other.
The reason I think this is the case is that judging from the code you've posted you're not programatically applying a shadow, and views do not have a shadow by default. Of course, perhaps you are adding a shadow in your code elsewhere, but based on my own experience I think it looks as if somehow the same view (your image view) is getting added multiple times.
It might be helpful if you shared more of your code, if possible.
When I am scrolling images frequently in a UIScrollView then after some images, the next image takes time to load... it's not taking too much time but looks odd.
Suppose I have 27 images in a scrollView. When I start to scroll these images, for 1 or 2 images it scrolls smoothly but when I scroll again to see the 3rd image it takes time to load. Then when I start the images scrolling again from the 3rd image, it behaves like before.
I can't load all 27 images at a time or my app crashes.
When I slowly scroll the scrollview then I don't have this problem.
My code is below:
//Taking image view for 27 images;
int x=0;
for(int i = 1; i<=27; i++) {
imageView = [[UIImageView alloc] init];
imageView .frame = CGRectMake(x,0,768,1024);
imageView.tag=i;
imageView.image=nil;
imageView.userInteractionEnabled=YES;
[contentView addSubview:imageView];
x+=768;
}
//setContentOffset of the scrollView -->ContentView
[contentView setContentOffset: CGPointMake((imageNumber-1)*768, 0) animated: YES];
//desire image which i want to see from the start of the scrollview
pageNumber=imageNumber;
int pageBefore=pageNumber-1;
int pageAfter=pageNumber+1;
//Views for image
for( UIImageView * views in [contentView subviews]){
if(views.tag==pageNumber){
if(views.image==nil){
NSLog(#"entering");
views.image=[UIImage imageNamed:[ NSString stringWithFormat:#"%d.jpg",pageNumber]];
[views.image release];
}
}
if(views.tag==pageBefore){
if(views.image==nil){
views.image=[UIImage imageNamed:[ NSString stringWithFormat:#"%d.jpg",pageBefore]];
[views.image release];
}
}
if(views.tag==pageAfter){
if(views.image==nil){
views.image=[UIImage imageNamed:[ NSString stringWithFormat:#"%d.jpg",pageAfter]];
[views.image release];
}
}
My alarm bells rang when I saw this;
imageView .frame = CGRectMake(x,0,768,1024);
Apart from the space before .frame, are you saying that your images are 768x1024? That's HUGE and I suspect your problems are memory ones rather than code ones.
Be aware that in particular, using UIImage imageNamed: is likely to cause grief with such large images as that method caches the images in memory. You may wish to consider using alternative methods that load the image from a file each time.
You should try use the EGOImageView, it has caching build in which might help with your performance issues. You can implement a placeholder image to show the user that an image is being prepared for viewing. The image will load in another thread before being displayed, giving you smoother scrolling performance. The EGOImageView is part of the EGOImageLoading library.
https://github.com/tastefulworks/EGOImageLoading
As an alternative you could create your own lazy loading mechanism to increase scrolling performance. E.g. once a user stops scrolling for a second, start loading the image, otherwise display placeholder image if not yet the correct image is cached.
Edit: when thinking more about this issue, I realize caching won't help much (since you already load image from disk), but the asynchronous loading of images should help with the scroll performance, so make use of NSThread or NSOperation to load the image in a background thread, then notify the main thread that the image is loaded and ready for display.
I have a UITableView with a list of items, each having it's own image. I thought Apple's LazyTableImages sample project would be perfect to learn from, and use to implement the same kind of process of downloading images asynchronously, after the original list data is retrieved.
For the most part, it works quite well, except I did notice a subtle difference in behavior, between this sample app, and how the actual app store downloads images.
If you launch the LazyTableImages sample, then do a quick flick-scroll down, you'll see that the images do not get displayed until after the scrolling comes to a complete stop.
Now, if you do the same test with a list of items in the actual app store, you'll see that the images start displaying as soon as the new items come into view, even if scrolling hasn't stopped yet.
I'm trying to achieve these same results, but so far I'm not making any progress. Does anyone have any ideas on how to do this?
Thanks!
I'm baffled that nobody could answer this...
So, I eventually figured out how to acheive the exact same effect that is used in the actual app store, in regards to how the icons are downloaded/displayed.
Take the LazyTableImages sample project and make a few simpled modifications.
Go into the root view controller and remove all checks regarding is table scrolling and/or decelerating in cellForRowAtIndexPath
Remove all calls to loadImagesForOnScreenRows, and thus remove that method as well.
Go into IconDownload.m and change the startDownload method to not do an async image downlaod, but instead do a sync download on a background thread. Remove all the code in startDownload, and add the following, so it looks like this:
- (void)startDownload
{
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(loadImage) object:nil];
[queue addOperation:operation];
[operation release];
[queue release];
}
Then, add a loadImage, like this:
- (void)loadImage
{
NSData *imageData = [[NSData alloc] initWithContents OfURL:[NSURL URLWithString:appRecord.imageURLString]];
self.apprecord.appIcon = [UIImage imageWithData:imageData];
[imageData release];
[self performSelectorOnMainThread:#selector(notifyMainThread) withObject:nil waitUntilDone:NO];
}
Then, add notifyMainThread like this:
- (void)notifyMainThread
{
[delegate appImageDidLoad:self.indexPathInTableView];
}
Done! Run it, and you will see the exact app store behavior, no more waiting to request image downloads until scrolling stops, and no more waiting for images to display until scrolling stops, or until user has removed their finger from the screen.
Images are downloaded as soon as the cell is ready to be displayed, and the image is displayed as soon as it is downloaded, period.
Sorry for any typos, I didn't paste this from my app, I typed it in, since I'm away from my mac right now...
Anyway, I hope this helps you all...
Check out UIScrollViewDelegate. I've implemented something like this by listening for scrollViewDidScroll:, calculating the scroll speed (by checking the contentOffset against the last recorded contentOffset, divided by the difference in time), and starting to load images once the speed drops below a certain threshold. (You could achieve something similar with UIScrollViewDelegate's scrollViewDidEndDragging:willDecelerate: as well).
Of course, you don't have to check the speed; you could just load images on UITableViewDelegate's tableView:willDisplayCell:forRowAtIndexPath: whenever you see a new cell, but I've found that if the user is flipping through tons of cells, you don't need to bother until you see that they're going to slow down to browse.
Im having a bit of trouble right now adding the same image multiple times and removing it all at one time after the gamelooop.
I manage to add the image by creating 1 UIImageview for every image but i know thats its not the practical way memory wise since im allocating a lot of uiimageview with the same image.
I use this code to load the image:
UIImageView *imgView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"correct.png"]];
UIImageView *imgViewn = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"correct.png"]];
CGRect imgRect = CGRectMake((touch.x-20), (touch.y-20), 40, 40);
[imgView1 drawInRect:imgRect];
[imgViewn drawInRect:imgRect];
[imgView1 release]
[imgViewn release]
Basically i use this to add image base on the location of the usertouch made on the subview (with image). If the users set of touches are correct this is where the game loops and show another image for the user to touch.
But after the new image load the "correct.png" image which is added on the previous touch is still on view.
Can someone show me a correct way to do this coz im kinda new to programming but i know that im gonna have a memory problem later if i keep on allocating image every time a user touch the screen.
Thanks in advance.
If you're trying to merely move an image (or series of images) through each run of the gameloop, rather than creating and removing them all each time, perhaps create the series of N images only once.
When the touches or gameloop begins, draw the images in the rectangles you're calculating from the touch event, and then when the loop is over, set the hidden property on the object to YES or NO (not sure exactly what your objective is).
It's hard to give you code without more details on what you're doing, but the basic idea is this:
When you first start the program/view, create your N images, and set imageView.hidden = YES;
On each loop, call the [imageView drawInRect:imgRect] method for each image, and set imageView.hidden = NO;.
When the view/program is done, call the [imageView release] method on each object.