Showing a HUD whilst an image is downscaled in size

556 views Asked by At

I have an app where the user takes a photo using the Camera and then chooses to Use the photo. The following method is called:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

Within this method I check the NSData length of the image and resize the actual image if the data (Kb) size is too large, then check again. This way I only scale down small amounts to keep the highest quality/sized image rather than a specific size w/h.

Question I am trying to display a HUD to the user whilst the 'image scaling' is occurring. The HUD does not show at the moment, this is what I have tried.

// Check if the image size is too large
if ((imageData.length/1024) >= 1024) {
    MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    HUD.dimBackground = YES;
    HUD.labelText = NSLocalizedString(@"HUDHomeLoadingTableData", @"Home View Controller - Loading Table Data");
    HUD.removeFromSuperViewOnHide = YES;

    while ((imageData.length/1024) >= 1024) {
        NSLog(@"While start - The imagedata size is currently: %f KB",roundf((imageData.length/1024)));

        // While the imageData is too large scale down the image

        // Get the current image size
        CGSize currentSize = CGSizeMake(image.size.width, image.size.height);

        // Resize the image
        image = [image resizedImage:CGSizeMake(roundf(((currentSize.width/100)*80)), roundf(((currentSize.height/100)*80))) interpolationQuality:kMESImageQuality];

        // Pass the NSData out again
        imageData = UIImageJPEGRepresentation(image, kMESImageQuality);

    }

    [HUD hide:YES];
}

I am adding the HUD to self.view but it does not show? Should I possibly think about threading here also, should the image scaling be completed on a background thread and the HUD updates on the main. I am unsure when to determine if certain parts should be on different threads?

2

There are 2 answers

1
Taha Selim Bebek On BEST ANSWER

Call the scaling method in the background thread like this :

if ((imageData.length/1024) >= 1024) {
    self.HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    HUD.dimBackground = YES;
    HUD.labelText = NSLocalizedString(@"HUDHomeLoadingTableData", @"Home View Controller - Loading Table Data");
    HUD.removeFromSuperViewOnHide = YES;
    self.scaledImageData = imageData;
    [self performSelectorInBackground:@selector(scaleDown:) withObject:imageData];
}

-(void)scaleDown:(NSData*)imageData
{
    while ((imageData.length/1024) >= 1024) {
        NSLog(@"While start - The imagedata size is currently: %f KB",roundf((imageData.length/1024)));
    // While the imageData is too large scale down the image

    // Get the current image size
    CGSize currentSize = CGSizeMake(image.size.width, image.size.height);

    // Resize the image
    image = [image resizedImage:CGSizeMake(roundf(((currentSize.width/100)*80)), roundf(((currentSize.height/100)*80))) interpolationQuality:kMESImageQuality];

    // Pass the NSData out again
    self.scaledImageData = UIImageJPEGRepresentation(image, kMESImageQuality);

    }

    //hide the hud on main thread
    [self performSelectorOnMainThread:@selector(hideHUD) withObject:nil waitUntilDone:NO];
}

-(void)hideHUD
{
    [self.HUD hide:YES];
}
3
Rob On

Are you resizing only one image? Or a several? If doing one, you can:

MBProgressHUD *HUD = [self showHUD];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // do your image resizing here

    // when done, hide the HUD on the main queue

    dispatch_async(dispatch_get_main_queue(), ^{
        [self hideHUD:HUD];
    });
});

Where

- (MBProgressHUD *)showHUD
{
    MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    HUD.dimBackground = YES;
    HUD.labelText = NSLocalizedString(@"HUDHomeLoadingTableData", @"Home View Controller - Loading Table Data");
    HUD.removeFromSuperViewOnHide = YES;

    return HUD;
}

- (void)hideHUD:(MBProgressHUD *)HUD
{
    [HUD hide:YES];
}

If resizing a bunch of images, you should define your own queue to do the resizing:

MBProgressHUD *HUD = [self showHUD];

NSOperationQueue *resizeQueue = [[NSOperationQueue alloc] init];
resizeQueue.maxConcurrentOperationCount = 1;

NSOperation *completeOperation = [NSBlockOperation blockOperationWithBlock:^{

    //when done, hide the HUD (using the main queue)

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [self hideHUD:HUD];
    }];
}];

for (NSInteger i = 0; i < imageCount; i++)
{
    NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        // do your image resizing here
    }];

    // make the completion operation dependent upon each image resize operation

    [completionOperation addDependency:operation];

    // queue the resize operation

    [resizeQueue addOperation:operation];
}

// when all done, queue the operation that will remove the HUD

[resizeQueue addOperation:completionOperation];

Note, I'm assuming you're going to do them one at a time (serially), but if you want to do them concurrently, just adjust the maxConcurrentOperationCount to whatever value you want. Frankly, given that entire point of this is that you want to make huge images smaller, you probably won't want to run too many concurrently (because of memory usage concerns), but it's an option.