Search code examples
iosperformanceuiimageviewcgcontextcomposite

Need a solution to combine an amount of images one by one on the screen


I have thousand of small images. I need to combine them into a big image one by one dozens at the same time and live on the screen. The user should be capable watch the small image shown one by one quickly and smoothly, no matter how we load/combine the small images.

Which means:

BigImage += SmallImage1;

BigImage += SmallImage2;

BigImage += SmallImage3;

...

BigImage += SmallImage1000+;

At the first time, I simply added the small image to the big one with addSubviews/addSublayers. As you can imagine, that caused a huge lag, because there are too many views to render which is expensive.

So I tried to use only one imageView(the big image), combine the small images to the big one every time. So I wrote some code like:

UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0f);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[bgImageView.layer renderInContext:ctx];
//[bgImageView.image drawInRect:self.frame];  // This line or the line above, same result
[smallImg drawInRect:rectToWindow];
bgImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

But there is still the huge lag problem(but in the way different), I guess because it render the big image every time which cost expensive.

So the question is:

Is there any possible I can keep the big image rendered in context, and do not render it every time?

Or may be you have the better idea can do what I need. Just talk to me, any idea will be helpful.

Thank you guys.


Solution

  • I think your view should be a subclass of UIImageView and when you want to add a group of small images to the the big image, use the following method:

    - (UIImage *)addSmallImages:(NSArray *)smallImages   // array of UIImage
                       atPoints:(NSArray *)points        // array of NSValue
                     toBigImage:(UIImage *)bigImage
    {
        if (smallImages.count != points.count)
        {
            NSLog(@"Array size mismatch");
            return nil;
        }
    
        NSUInteger count = [smallImages count];
    
        UIGraphicsBeginImageContextWithOptions(bigImage.size, NO, 0.0); 
        [bigImage drawAtPoint:CGPointZero];
    
        for (NSUInteger i = 0; i < count; i++)
        {
            UIImage *smallImage = [smallImages objectAtIndex:i];
            NSValue *pointVal = [points objectAtIndex:i];
            CGPoint point = [pointVal CGPointValue];
            [smallImage drawAtPoint:point];
        }
    
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 
        UIGraphicsEndImageContext();
        return newImage; 
    }
    

    This is called something like this:

    NSMutableArray *smallImages = [[NSMutableArray alloc] init];
    NSMutableArray *points = [[NSMutableArray alloc] init];
    
    for (NSUInteger i = 0 ...)
    {
        [smallImages addObject:smallImage];
        [points addObject:[NSValue valueWithCGPoint:CGMakePoint(100.0, 120.0)]];
    }
    
    imageView.view = [self addSmallImages:smallImages atPoints:points toBigImage:imageView.view];
    

    If you want to animate the "small image" onto the "big image" then you can do that by creating a CALayer, applying the "small image" to it, and then animating it to the right place. Then call the above method to commit the change and then remove the layer.