Odrakir
Odrakir

Reputation: 4254

iCarousel performance issue on iPad

I'm using Nick Lockwood's iCarousel for an iPad app. Right now it's just a carousel of 15 full screen images.

I setup a single view project, add the iCarousel view in storyboard, add it's view controller as a data source and put this code for the datasource methods:

- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel {
    return 15;
}

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
    UIImage* img = [UIImage imageNamed:[NSString stringWithFormat:@"image%d.jpg", index]];
    UIImageView* imgView = [[UIImageView alloc] initWithImage:img];

    return imgView;
}

This works, but the first time I scroll through all the items you can notice a little performance hit when a new item is being added to the carousel. This does not happen the second time I go through all the items.

You can see what I mean in this profiler screenshot. The peaks in the first half are for the first time I scroll through all the images, then I do it again and there are no peaks and no performance hit.

How can I fix this?

enter image description here

EDIT

I isolated one of the peaks on instruments and this is the call tree

enter image description here

EDIT

The code with jcesar suggestion

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableArray* urls_aux = [[NSMutableArray alloc] init];
    for (int i = 0; i<15; i++) {
        NSString *urlPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"captura%d", i] ofType:@"jpg"];
        NSURL *url = [NSURL fileURLWithPath:urlPath];

        [urls_aux addObject:url];
    }
    self.urls = urls_aux.copy;

    self.carousel.dataSource = self;
    self.carousel.type = iCarouselTypeLinear;
}

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {

    if (view == nil) {
        view = [[[AsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)] autorelease];
        view.contentMode = UIViewContentModeScaleAspectFit;
    }

    [[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:view];
    ((AsyncImageView *)view).imageURL = [self.urls objectAtIndex:index];

    return view;
}

Upvotes: 2

Views: 1277

Answers (3)

Odrakir
Odrakir

Reputation: 4254

I don't think this is a very orthodox solution, but it works.

What I do is use iCarousel's method insertItemAtIndex:animated: to add the images one at a time on setup. Performance gets hit then, but after that everything runs smoothly.

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.images = [[NSMutableArray alloc] init];
    self.carousel.dataSource = self;
    self.carousel.type = iCarouselTypeLinear;

    for (int i = 0; i<15; i++) {
        UIImage* img = [UIImage imageNamed:[NSString stringWithFormat:@"captura%d.jpg", i]];
        [self.images addObject:img];
        [self.carousel insertItemAtIndex:i animated:NO];
    }
}

- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel {
    return self.images.count;
}

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
    UIImage* img = [self.images objectAtIndex:index];
    UIImageView* imgView = [[UIImageView alloc] initWithImage:img];

    return imgView;
}

Upvotes: 1

jcesarmobile
jcesarmobile

Reputation: 53301

Look at his example: Dynamic Downloads. He uses AsyncImageView instead UIImageView, that loads the images in an async way. So it won't try to load all the 15 images at the same time, and I think it shouldn't have performance issues

Upvotes: 0

Simon
Simon

Reputation: 25983

It's hard to be certain, but I'd guess that the first time through it's loading the images from the file; whereas the second time it's using the cached copies - that's something that +[UIImage imageNamed:] does automatically for you; without it, you'd see the performance hit every time. You could load the images earlier, either in your app delegate or in the initialiser for your controller.

Another thing you could do would be to make use of iCarousel's view reuse logic. I don't think it's responsible for your problem, but you may as well do it now:

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
    UIImage* img = [UIImage imageNamed:[NSString stringWithFormat:@"image%d.jpg", index]];

    UIImageView* imgView = nil;
    if ([view isKindOfClass:[UIImageView class]])
    {
        imgView = (UIImageView *)view;
        imgView.image = img;
        [imgView sizeToFit];
    }
    else
    {
        [[UIImageView alloc] initWithImage:img];
    }

    return imgView;
}

UPDATE: It would be great to get a bit more information on where your app is spending its time. If you use the Instruments Time Profiler you can get a breakdown of what percentage of the time is spent in each method.

Upvotes: 0

Related Questions