gOnZo gOnZo - 11 days ago 3
iOS Question

IOS Objective C load a PNG file with transparent background to a UIImageView

I'm trying to load a PNG file with a transparent background into an UIImageView (In case it matters, developing with Objective C, XCode 8.0, for IOS 10.0 target).
The background color of the image appears BLACK when displayed in the UIImageView (see below).

How can I load an arbitrary PNG file to an UIImageView such that its transparent background is preserved?

Strangely, I have PNG images with transparent background in my apps assets, and they display in an UIImageView without ANY additional messing around.

Note in the code below, one of the things I tried was to rebuild the UIImage passed from the imagePicker (see imageWithImage), setting up a new graphics context for the new image. I had seen this approach used in some postings regarding vaguely similar issues, but it did not work (not to mention it seems silly IOS would require it to support transparency defined in the loaded image).

The PNG image I'm testing with is a standard PNG32 (harvested from the internet for testing purposes - note that I have tried many other images):

enter image description here

Below left shows app screen before loading the image above, right shows with the image loaded:
enter image description here

The PNG image file is selected by an imagePicker opened by clicking on 'select image' at the bottom of the screen.

My ViewController code:

//
// ViewController.m
// ImageTest
//

#import "ViewController.h"
#import "MobileCoreServices/MobileCoreServices.h"


#define FONT_SIZE_BUTTON 32

@interface ViewController ()

@end

@implementation ViewController

UIImageView *myImageView;
UIImage *myImage;
UIButton *btnSelect;

- (void)viewDidLoad {

NSLog(@"viewDidLoad" );

[super viewDidLoad];

CGRect screenRect = [[UIScreen mainScreen] bounds];

CGFloat btnHeight = 40;
CGFloat btnMargin = 10;
CGFloat btnWidth = screenRect.size.width - 2*btnMargin;
CGFloat btnULY = screenRect.size.height - btnHeight - 2*btnMargin;

btnSelect = [[UIButton alloc] initWithFrame:CGRectMake(btnMargin, btnULY, btnWidth, btnHeight)];
[btnSelect.titleLabel setFont:[UIFont boldSystemFontOfSize:FONT_SIZE_BUTTON]];
[btnSelect setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[btnSelect setTitle :@"Select Image" forState:UIControlStateNormal];
[btnSelect.layer setBorderWidth:4.0f];
[btnSelect.layer setBorderColor:[UIColor blueColor].CGColor];
[btnSelect setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btnSelect setTitleColor:[UIColor grayColor] forState:UIControlStateSelected];
[btnSelect setTitleColor:[UIColor grayColor] forState:UIControlStateDisabled];
btnSelect.backgroundColor = [UIColor whiteColor];
btnSelect.tag = 4200;
[btnSelect addTarget:self action:@selector(onClick:) forControlEvents:UIControlEventTouchUpInside];
btnSelect.userInteractionEnabled = true;

CGFloat ulx = 10;
CGFloat uly = 20;
CGFloat width = screenRect.size.width - 2*ulx;
CGFloat height = width * 9.0 / 16.0;

CGRect imageFrame = CGRectMake(ulx, uly, width, height);

myImageView = [[UIImageView alloc] initWithFrame:imageFrame];
myImageView.alpha = 1.0f;
myImageView.backgroundColor = [UIColor lightGrayColor];
myImageView.contentMode = UIViewContentModeScaleAspectFit;

myImageView.opaque = false;
[[myImageView layer] setOpaque:false];

[[myImageView layer] setBorderWidth:4.0f];
[[myImageView layer] setBorderColor:[UIColor redColor].CGColor];

// Add elements to this view controller
[self.view addSubview:myImageView];
[self.view addSubview:btnSelect];
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}


// Ensure image is opened honoring transparency of PNGs
- (UIImage *)imageWithImage:(UIImage *)image {
CGSize newSize = image.size;
CGRect newRect = CGRectMake(0,0,newSize.width, newSize.height);

UIGraphicsBeginImageContextWithOptions(newSize, NO, 1.0);

CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor clearColor] set];
CGContextFillRect(ctx, newRect);

[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.Height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#pragma mark - Select Actions
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- (IBAction)onClick: (UIButton *)btn
{
NSLog(@"onClick" );
// DDLogInfo(@"%@:%@", THIS_FILE, THIS_METHOD );

UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
NSMutableArray *mediaTypes = [[NSMutableArray alloc] init];
[mediaTypes addObject:(__bridge NSString *)kUTTypeImage];
picker.mediaTypes = mediaTypes;

[self presentViewController:picker animated:YES completion:NULL];
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#pragma mark - UIImagePicker's Delegate
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

NSLog(@"imagePickerController" );

UIImage *chosenImage = [self imageWithImage:info[UIImagePickerControllerEditedImage]];
// UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
// UIImage *chosenImage = [info[UIImagePickerControllerEditedImage] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
if (myImageView != nil)
myImageView.image = chosenImage;

myImage = chosenImage;

[picker dismissViewControllerAnimated:YES completion:NULL];
}



- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

[picker dismissViewControllerAnimated:YES completion:NULL];
}


@end


**



EDITED WITH FINAL FIX



**

A screenshot of the updated app screen to show the issue:

enter image description here

and the final (fixed) code, showing the difference between using the dictionary key (UIImagePickerControllerOriginalImage) and the dictionary key (UIImagePickerControllerEditedImage):

//
// ViewController.m
// ImageTest
//
// Created by Robb Main on 2016-11-21.
// Copyright © 2016 Robb Main. All rights reserved.
//

#import "ViewController.h"
#import "MobileCoreServices/MobileCoreServices.h"


#define FONT_SIZE_BUTTON 32

@interface ViewController ()

@end

@implementation ViewController

UIImageView *myImageView1;
UIImageView *myImageView2;
UIImageView *myImageView3;
UIImage *myImage;
UIButton *btnSelect;

- (void)viewDidLoad {

NSLog(@"viewDidLoad" );

[super viewDidLoad];

CGRect screenRect = [[UIScreen mainScreen] bounds];

CGFloat btnHeight = 40;
CGFloat btnMargin = 10;
CGFloat btnWidth = screenRect.size.width - 2*btnMargin;
CGFloat btnULY = screenRect.size.height - btnHeight - 2*btnMargin;

btnSelect = [[UIButton alloc] initWithFrame:CGRectMake(btnMargin, btnULY, btnWidth, btnHeight)];
[btnSelect.titleLabel setFont:[UIFont boldSystemFontOfSize:FONT_SIZE_BUTTON]];
[btnSelect setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[btnSelect setTitle :@"Select Image" forState:UIControlStateNormal];
[btnSelect.layer setBorderWidth:4.0f];
[btnSelect.layer setBorderColor:[UIColor blueColor].CGColor];
[btnSelect setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btnSelect setTitleColor:[UIColor grayColor] forState:UIControlStateSelected];
[btnSelect setTitleColor:[UIColor grayColor] forState:UIControlStateDisabled];
btnSelect.backgroundColor = [UIColor whiteColor];
btnSelect.tag = 4200;
[btnSelect addTarget:self action:@selector(onClick:) forControlEvents:UIControlEventTouchUpInside];
btnSelect.userInteractionEnabled = true;

CGFloat imgMargin = 60;
CGFloat ulx = imgMargin;
CGFloat uly = 20;
CGFloat imgWidth = screenRect.size.width - 2*imgMargin;
CGFloat imgHeight = imgWidth * 9.0 / 16.0;

CGRect imageFrame = CGRectMake(ulx, uly, imgWidth, imgHeight);

myImageView1 = [[UIImageView alloc] initWithFrame:imageFrame];
myImageView1.alpha = 1.0f;
myImageView1.backgroundColor = [UIColor lightGrayColor];
myImageView1.contentMode = UIViewContentModeScaleAspectFit;
myImageView1.opaque = NO;
[myImageView1.layer setOpaque:NO];
// myImageView.clearsContextBeforeDrawing = YES;
[myImageView1.layer setBorderWidth:4.0f];
[myImageView1.layer setBorderColor:[UIColor redColor].CGColor];

myImageView1.image = [UIImage imageNamed:@"imageTest.png"];

uly += imgHeight+8;
CGFloat lblHeight = 10;
CGRect lblFrame = CGRectMake( ulx, uly, imgWidth, lblHeight);
UILabel *myLabel1 = [[UILabel alloc] initWithFrame:lblFrame];
[myLabel1 setFont:[UIFont systemFontOfSize:12]];
myLabel1.text = @"PNG Image from assets";

uly += lblHeight+20;
imageFrame.origin.y = uly;

myImageView2 = [[UIImageView alloc] initWithFrame:imageFrame];
myImageView2.alpha = 1.0f;
myImageView2.backgroundColor = [UIColor lightGrayColor];
myImageView2.contentMode = UIViewContentModeScaleAspectFit;
myImageView2.opaque = NO;
[myImageView2.layer setOpaque:NO];
// myImageView2.clearsContextBeforeDrawing = YES;
[myImageView2.layer setBorderWidth:4.0f];
[myImageView2.layer setBorderColor:[UIColor redColor].CGColor];

uly += imgHeight+8;
lblFrame.origin.y = uly;

UILabel *myLabel2 = [[UILabel alloc] initWithFrame:lblFrame];
[myLabel2 setFont:[UIFont systemFontOfSize:12]];
myLabel2.text = @"UIImagePickerControllerOriginalImage";

uly += lblHeight+20;
imageFrame.origin.y = uly;

myImageView3 = [[UIImageView alloc] initWithFrame:imageFrame];
myImageView3.alpha = 1.0f;
myImageView3.backgroundColor = [UIColor lightGrayColor];
myImageView3.contentMode = UIViewContentModeScaleAspectFit;
myImageView3.opaque = NO;
[myImageView3.layer setOpaque:NO];
// myImageView3.clearsContextBeforeDrawing = YES;
[myImageView3.layer setBorderWidth:4.0f];
[myImageView3.layer setBorderColor:[UIColor redColor].CGColor];

uly += imgHeight+8;
lblFrame.origin.y = uly;

UILabel *myLabel3 = [[UILabel alloc] initWithFrame:lblFrame];
[myLabel3 setFont:[UIFont systemFontOfSize:12]];
myLabel3.text = @"UIImagePickerControllerEditedImage";

// Add elements to this view controller
[self.view addSubview:myImageView1];
[self.view addSubview:myLabel1];
[self.view addSubview:myImageView2];
[self.view addSubview:myLabel2];
[self.view addSubview:myImageView3];
[self.view addSubview:myLabel3];
[self.view addSubview:btnSelect];
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#pragma mark - Select Actions
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- (IBAction)onClick: (UIButton *)btn
{
NSLog(@"onClick" );
// DDLogInfo(@"%@:%@", THIS_FILE, THIS_METHOD );

UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
NSMutableArray *mediaTypes = [[NSMutableArray alloc] init];
[mediaTypes addObject:(__bridge NSString *)kUTTypeImage];
picker.mediaTypes = mediaTypes;

[self presentViewController:picker animated:YES completion:NULL];
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#pragma mark - UIImagePicker's Delegate
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

NSLog(@"imagePickerController" );

myImage = info[UIImagePickerControllerOriginalImage];
if (myImageView2 != nil)
myImageView2.image = myImage;

if (myImageView3 != nil)
myImageView3.image = info[UIImagePickerControllerEditedImage];

[picker dismissViewControllerAnimated:YES completion:NULL];
}



- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

[picker dismissViewControllerAnimated:YES completion:NULL];
}


@end


This WORKS, though it's not clear to my why:


  1. The imagePicker wouldn't just return nil for an invalid dictionary key (I had specified editing was not allowed).

  2. The 'edited' image would strip transparency information.


Answer

Your primary issue is the use of the UIImagePickerControllerEditedImage instead of UIImagePickerControllerOriginalImage when getting the selected image. The edited image loses the alpha channel resulting in black instead of transparency.

You also have no need for the imageWithImage: method.

Comments