problem with accessing NSMutableArray - ios

I have the following problem:
int index=6;
imageView.image=[imageArray objectAtIndex:index];
NSLog(#"%#",[imageArray objectAtIndex:index]);
If I run this code I get (null) as an output...even though I have nicely put the images inside the array using the following code:
NSURL *url = [NSURL URLWithString:#"somelink"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
[imageArray addObject:image];
I am pretty sure there are 20 images (I use an XML file and print the URL and image) and the image is fine, too. I print the values of image before putting inside the array and here's the value I get
is :
<UIImage: 0x5368670>
Can anyone kindly help me out ? Thanks.

Remember you'll need to make a new instance of the NSMutableArray... it's possible you're just calling methods on nil.
Before you start to you the imageArray, make sure you do something like:
imageArray = [NSMutableArray array];
// or = [[NSMutableArray alloc] init] if you want to "retain" it
// for use in other methods

Have you added at least seven such objects to the array, though? Remember that NSArray (and friends) count from zero, not one.
Also, are you certain that the object you're adding is not null (i.e, that dataWithContentsOfURL: and imageWithData: are both succeeding)?

Related

Why doesn't UIImage get successfully decoded?

Why doesn't the UIImage in this code snippet get restored back to its original state when I try to encode and decode it using NSKeyedArchiver?
I expect "decodedImage" to contain the image after decoding, but instead it is just NULL.
// Any image here seems to repro the issue
UIImage *image = [UIImage imageNamed:#"soda.jpg"];
// This prints YES (1), just a sanity check.
NSLog(#"Confirms %d", [[UIImage class] conformsToProtocol:#protocol(NSCoding)]);
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[coder encodeObject:image forKey:#"image"];
[coder finishEncoding];
// I would expect this to be large, instead it's < 1kb.
NSLog(#"Data length is: %zu", (unsigned long)data.length);
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
// This prints YES (1)
NSLog(#"containsValueForKey returns %d", [decoder containsValueForKey:#"image"]);
// decodedImage is NULL here, even though containsValueForKey returned YES
UIImage *decodedImage = [decoder decodeObjectForKey:#"image"];
[decoder finishDecoding];
In this case, I'm not looking for a workaround like converting the UIImage to NSData first and encoding that. The reason is that I'm trying to reproduce an unrelated piece of code which uses something like this and I'm trying to understand it.
The code works as expected if I roundtrip the image first through nsdata and back to uiimage, why??
UIImage *originalImage = [UIImage imageNamed:#"soda.jpg"];
NSData *imageData = UIImagePNGRepresentation(originalImage);
UIImage *image = [UIImage imageWithData:imageData];
Check this
Decode Data to image:
+(NSData *)decodeBase64ToImage:(NSString *)strEncodeData
{
NSData *data = [[NSData alloc]initWithBase64EncodedString:strEncodeData options:NSDataBase64DecodingIgnoreUnknownCharacters];
return data;
}
self.btnLicenseFront.image=[UIImage imageWithData:[Themes decodeBase64ToImage:licenseFront]];
I used your code and tried with 2 images:
1. A correct image file
The output is
> [36133:5889153] Confirms 1
> [36133:5889153] Data length is: 68267
> [36133:5889153] containsValueForKey returns 1
> [36133:5889153] decodedImage is 1879681920
2. Incorrect/corrupt image file
The output is
> [36130:5888794] Confirms 1
> [36130:5888794] Data length is: 136
> [36130:5888794] containsValueForKey returns 1
> [36130:5888794] decodedImage is 0
So looks like your source JPG file is corrupt or invalid.
I have found a solution.
It turns out that this only happens the image is loaded through the [UIImage imageNamed:]. If the UIImage is created through[UIImage imageWithContentsOfFile:], the issue does not happen.
I believe this must be a bug on the ios side. The imageNamed: way of creating a UIImage is specifically for images inside the bundle. There must be some optimization they have which causes NSCoder to not function as intended since the UIImage seems to not actually contain the image data (since decoding seems to return nil instead of recreating the UIImage with the image from the bundle as expected).

Adding list of URLs to an array?

I'm using MHVideoPhotoGallery to create gallery's of images that are stored on my website. The current way to add images (as shown in the example on Github) is
MHGalleryItem *photo1 = [MHGalleryItem.alloc initWithURL:#"*ENTER IMAGE URL HERE*"
galleryType:MHGalleryTypeImage];
MHGalleryItem *photo2 = [MHGalleryItem.alloc initWithURL:#"*ENTER IMAGE URL HERE*"
galleryType:MHGalleryTypeImage];
MHGalleryItem *photo3 = [MHGalleryItem.alloc initWithURL:#"*ENTER IMAGE URL HERE*"
galleryType:MHGalleryTypeImage];
self.galleryDataSource = #[#[photo1,photo2,photo3]];
But I want to add hundreds of images and this is not the most ideal way to do it. What would be an easier way for me to accomplish this?
Thanks!
You have to start with a list of the URLs. What I would do is put this in a text file in my bundle. In code, when the app runs, I would open the text file (as an NSString) and split it into an NSArray. Now I've got an NSArray of the URLs. I would then cycle through the NSArray. So now we're inside a loop. For each item the array, I would initialize the MHGalleryItem and then add it to a previously created NSMutableArray with addObject:. Thus we have a two or three-line loop which is repeated, running through all the URLs.
The following is pseudo-code and untested (so it might contain errors), but it should give the general idea of the structure I'm suggesting:
NSMutableArray* temp = [NSMutableArray new];
NSString* s =
[NSString stringWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:#"urls" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:nil];
NSArray* urls = [s componentsSeparatedByString:#"\n"];
for (NSString* url in urls) {
MHGalleryItem *item = [[MHGalleryItem alloc] initWithURL:url
galleryType:MHGalleryTypeImage];
[temp addObject:item];
}
self.galleryDataSource = temp;
Loop. If you're putting numbers at the end of your variable names, you need a loop and/or an array.
NSMutableArray * photos = [NSMutableArray new];
NSArray * photoPaths = [NSArray arrayWithContentsOfFile:plistContainingPhotoPaths];
for( NSString * path in photoPaths ){
NSURL * photoURL = [NSURL URLWithString:path];
MHGalleryItem * photo = [[MHGalleryItem alloc] initWithURL:photoURL
galleryType:MHGalleryTypeImage];
[photos addObject:photo];
}
And don't use dot syntax for alloc, or your code will burst into flames.
Use a naming protocol on your website such as:
www.mywebsite.com/appImageGallery/insertImageNumber
And replace the insertImageNumber with the number of you image. Then add this for loop to get all of the images and add them to the array.
NSMutableArray *mutableGalleryDataSource = [self.galleryDataSource mutableCopy]
for(int i = 0; i < numberOfImagesOnWebsite; i++){ //replace numberOfImagesOnWebsite with the number of images on your website.
MHGalleryItem *newItem = [MHGalleryItem.alloc initWithURL:[#"www.mywebsite.com/appImageGallery/" stringByAppendingString:[NSString stringWithFormat: #"%i", i]] galleryType:MHGalleryTypeImage];
[mutableGalleryDataSource addObject:newItem];
}
self.galleryDataSource = mutableGalleryDataSource;
There is also an -addObjectsFromArray method on NSMutableArray.

working with variables from dispatch_async

I have a method which downloads some pics from a server. I had the buffer for the downloaded data defined withing async block (NSMutableArray *imgtmp) but haven't figured out how to get the array back out of there. What's the best way to access imgtmp in order to return it's contents, or set an instance variable from it?
I've been looking in the Apple Block docs but I must not be in the right section. Do I need to declare imgtmp using the __block keyword somehow? I tried that but imgtmp was still empty outside the block. Thanks!
EDIT: code updated with working model
- (void) loadImages
{
// temp array for downloaded images. If all downloads complete, load into the actual image data array for tablerows
__block NSMutableArray *imgtmp = [[NSMutableArray alloc] init];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
int error = 0;
int totalitems = 0;
NSMutableArray *picbuf = [[NSMutableArray alloc] init];
for (int i=0; i < _imageURLS.count;i++)
{
NSLog(#"loading image for main image holder at index %i",i);
NSURL *mynsurl = [[NSURL alloc] initWithString:[_imageURLS objectAtIndex:i]];
NSData *imgData = [NSData dataWithContentsOfURL:mynsurl];
UIImage *img = [UIImage imageWithData:imgData];
if (img)
{
[picbuf addObject:img];
totalitems++;
}
else
{
NSLog(#"error loading img from %#", [_imageURLS objectAtIndex:i]);
error++;
}
}// for int i...
dispatch_async(dispatch_get_main_queue(),
^{
NSLog(#"_loadedImages download COMPLETE");
imgtmp = picbuf;
[_tvStatus setText: [NSString stringWithFormat:#"%d objects have been retrieved", totalitems]];
NSLog (#"imgtmp contains %u images", [imgtmp count]);
});// get_main_queue
});// get_global_queue
}
You're hitting that "final" NSLog call before any of your block code has executed. All your image loading stuff is wrapped in a dispatch_async. It is executed asynchronously whereas the NSLog is called right away.
I think the best thing for you to do is to pass imgtmp along to some persistent object. Perhaps your view controller can have a property like:
#property (nonatomic, copy) NSArray *images;
and you can assign that in the same block where you assign the text to _tvStatus.

Iterating an NSMutableDictionary with UIImage not working

I am trying to add Images fetched from an external service to an NSMutableDictionary and seeing weird results. This is what I am doing:
- (void)fetchImages{
//Fetch Item Brand Images
//self.itemBrands is an NSArray of NSDictionaries
for (NSDictionary *itemBrand in self.itemBrands){
NSString *currentItemId = [itemBrand objectForKey:#"ITEM_ID"];
//Valid Item Id. This Log message is displayed
NSLog(#"Current Item Id: %#",currentItemId);
NSString *currentItemImageUrl = [[IMAGE_URL stringByAppendingString:currentItemId] stringByAppendingString:#".png"];
//Image URL is valid. This log message is displayed
NSLog(#"Current Image URL: %#",currentItemImageUrl);
NSURL *url = [NSURL URLWithString:currentItemImageUrl];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
if (image == nil){
//This log message is displayed when image is not present
NSLog(#"Image not Present 1");
}else{
//This log message is displayed when image is present
NSLog(#"Image Present 1");
[self.itemBrandImages setObject:image forKey:currentItemId];
}
}
//This for loop is not being executed at all. No log messages displayed.
for(id key in self.itemBrandImages){
NSLog(#"Current Item Id2: %#",key);
if ([self.itemBrandImages objectForKey:key] == nil){
NSLog(#"Image Not Present 2");
}else{
NSLog(#"Image Present 2");
}
}
}
The 2nd for loop where I am iterating over self.itemBrandImages is not being executed at all. None of the log messages inside are being displayed.
I tried the following before posting my issue here:
1) Researched similar problems in stack overflow and incorporated suggestion from one of them. The suggestion was "Perform an alloc init of the NSMUtableDictionary" in the init method of the .m file. This didn't help either.
2) To isolate the issue, I even tried adding a simple string to the NSMUtableDictionary instead of the image but even that does not seem to retained.
I am really confused as as to what I am missing or doing wrong here. Inputs are really appreciated.
Thanks,
Mike G
Perhaps:
for(NSString *key in [self.itemBrandImages allKeys])
I did an alloc init of the NSMutableDictianary right in my fetchImages method and it worked! Not sure why the alloc init in the init method did not work.
So here are my takeaways from this issue:
1) If you have an Array or dictionary #property that you are just getting and setting and not really adding or deleting objects to, then you don't need to explicitly alloc init them.
2) If you have an Array or dictionary #property that you are adding or deleting objects to ,you need to explicitly alloc init them.
Are my above statements true? Would love to hear your inputs on this.
Thanks,
Mike
New code:
- (void)fetchImages{
//Fetch Item Brand Images
self.itemBrandImages = [[NSMutableDictionary alloc] init];
for (NSDictionary *itemBrand in self.itemBrands){
NSString *currentItemId = [itemBrand objectForKey:#"ITEM_ID"];
NSLog(#"Current Item Id in ItemList: %#",currentItemId);
NSString *currentItemImageUrl = [[#"http://anythingtogo.freeiz.com/images/"stringByAppendingString:currentItemId] stringByAppendingString:#".png"];
NSLog(#"Current Image URL in ItemList: %#",currentItemImageUrl);
NSURL *url = [NSURL URLWithString:currentItemImageUrl];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
if (image == nil){
NSLog(#"Image not Present 1");
}else{
NSLog(#"Image Present 1");
[self.itemBrandImages setObject:#"Test" forKey:currentItemId];
}
}

NSmutablearray adding unknown amount of images

I have an array i'm filling with images with a loop
items = [[NSMutableArray alloc] init];
for (int i=0; i<43; i++) {
NSString *filepath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%d",i] ofType:#"png"];
UIImage *image = (UIImage*)[UIImage imageWithContentsOfFile:filepath];
[items addObject:image];
}
it works as long as I set the max amount to match my test images. (in this case 43) if I set the max to a higher number, say 200 it of course crashes cause its trying to add nil objects to the array.
How would I go about being able to add the random number of images with that naming scheme to that array?
I have heard mention of using NSFileManager to add the files to array, is that the better method?

Resources