Search code examples
iosperformanceuiscrollviewuiscrollviewdelegate

Lazy Loading UIScrollView - how to make scrolling smoother


I have accomplished lazy loading in a scrollView like this:

-(void)scrollViewDidScroll:(UIScrollView *)myScrollView {

    int currentPage = (1 + myScrollView.contentOffset.x / kXItemSpacingIphone);
    for (ItemView* itemView in [self.itemRow subviews]){
        if (itemView.tag >= currentPage-2 && itemView.tag <= currentPage+2)
        {
            //keep it visible
            if (!itemView.isLoaded) {
                [itemView layoutWithData:[self.items objectAtIndex:itemView.tag-1]];
            }
        }
        else
        {
            //hide it
            if (itemView.isLoaded) {
                [itemView unloadData];
            }

        }
    }
}

Basically loading the view if it's +/- 2 "pages" from being on screen. This greatly reduces the amount of memory I'm using (instead of loading 20+ ItemViews at once), which is good. However, all the loading/unloading does make the scrolling a bit choppy especially on slower devices. Here is what is actually happening on the ItemView loading:

- (void)layoutWithData:(Item*)_data {
    self.data = _data;

//grab the image from the bundle
    UIImage *img;
    NSString *filePath = [[NSBundle mainBundle] pathForResource:_data.image ofType:@"jpg"];
        if(filePath.length > 0 && filePath != (id)[NSNull null]) {
            img = [UIImage imageWithContentsOfFile:filePath];
        }

    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    [btn setImage:img forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(tapDetected:) forControlEvents:UIControlEventTouchUpInside];
    btn.frame = CGRectMake(0, 0, kItemPosterWidthIphone, kItemPosterHeightIphone);
    [self addSubview:btn];

    self.isLoaded = YES;

}

And the ItemView unloading:

- (void)unloadData{
    for(UIView *subview in [self subviews]) {
        [subview removeFromSuperview];
    }
    self.data = nil;
    self.isLoaded = NO;
}

Again, what can I do to make the loading/unloading faster and therefore the UIScrollView more smooth?


Attempting async:

- (void)layoutWithData:(Item*)_data {
    self.data = _data;
    self.isLoaded = YES;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        UIImage *img;
        NSString *filePath = [[NSBundle mainBundle] pathForResource:_data.image ofType:@"jpg"];
            if(filePath.length > 0 && filePath != (id)[NSNull null]) {
                img = [UIImage imageWithContentsOfFile:filePath];
            }

        dispatch_async(dispatch_get_main_queue(), ^{
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
            [btn setImage:img forState:UIControlStateNormal];
            [btn addTarget:self action:@selector(tapDetected:) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(0, 0, kItemPosterWidthIphone, kItemPosterHeightIphone);
            self.imageView = btn;
            [self addSubview:btn];


        });

    });
}

Solution

  • I ran into this problem a while back and I moved loading images from disk to a background thread. Try it and see if it's faster.

    See here: loading images from disk in iPhone app is slow

    Edit: image loading lags could also be caused by delayed processing of UIImages

    See here: Setting image property of UIImageView causes major lag