Asynchronous NSImage download: first image has infinite loading - ios

I'm trying to make a module for react-native using NYTPhotoViewer and asynchronous image downloading. It works, but first image never stops to animate loading when the other downloads and shows. Interesting, that if I swipe some images (2 and more), the first image will render. The code is here:
#import <NYTPhotoViewer/NYTPhotosViewController.h>
#import "NYTImageViewer.h"
#import "NYTExamplePhoto.h"
#interface NYTImageViewer () <NYTPhotosViewControllerDelegate>
#property (nonatomic) NSArray *photos;
#end
#implementation NYTImageViewer
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(open:(NSArray *)urls)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *photos = addPhotos(urls);
NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos];
UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
[ctrl presentViewController:photosViewController animated:TRUE completion: nil];
});
}
NSArray* addPhotos(NSArray *urls) {
NSMutableArray *photos = [NSMutableArray array];
for (int i = 0; i < [urls count]; i++) {
NYTExamplePhoto *photo = [[NYTExamplePhoto alloc] init];
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: urls[i]]];
if ( imageData == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
photo.image = [UIImage imageWithData: imageData];
});
});
[photos addObject:photo];
}
return photos;
}
#end
How can I fix it?

You shouldn't/can't load that way from a remote URL, and with the dispatch_async it's generating the expected behavior:
NSData * _data = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: urls[i]]];
You must use NSURLSession or NSURLConnection via asynchronous method for remote calls, and you'll have to handle recursion at the callback.

Related

How to get an image from URL and put into NSArray?

I would like to get an image from an URL and put it into a NSArray, but doesn't work in the below codes, please help:
#interface DataViewController ()
#property (readonly, strong, nonatomic) NSArray *picData;
#end
#implementation DataViewController
- (void)viewDidLoad {
NSString *imageUrlStringAA=#"http://aaa.net/AA.png";
NSString *imageUrlStringBB=#"http://aaa.net/BB.png";
NSURL *urlAA=[NSURL URLWithString:imageUrlStringAA];
NSURL *urlBB=[NSURL URLWithString:imageUrlStringBB];
NSData *dataAA=[[NSData alloc] initWithContentsOfURL:urlAA];
NSData *dataBB=[[NSData alloc] initWithContentsOfURL:urlBB];
UIImage *imageAA=[UIImage imageWithData:dataAA];
UIImage *imageBB=[UIImage imageWithData:dataBB];
_picData = [NSArray arrayWithObjects:imageAA, imageBB, nil];
Any ideas is appreciated, thank you.
First thing for download anything from http protocol then Add following diction in info.plist
And now try this code:
#interface DataViewController ()
#property (readonly, strong, nonatomic) NSArray *picData;
#end
#implementation DataViewController
- (void)viewDidLoad {
NSString *imageUrlStringAA=#"http://www.hdwallpapers.in/download/antelope_canyon-360x640.jpg";
NSString *imageUrlStringBB=#"http://www.hdwallpapers.in/download/vegeta_dragon_ball_super-360x640.jpg";
NSURL *urlAA=[NSURL URLWithString:imageUrlStringAA];
NSURL *urlBB=[NSURL URLWithString:imageUrlStringBB];
NSData *dataAA=[[NSData alloc] initWithContentsOfURL:urlAA];
NSData *dataBB=[[NSData alloc] initWithContentsOfURL:urlBB];
UIImage *imageAA=[UIImage imageWithData:dataAA];
UIImage *imageBB=[UIImage imageWithData:dataBB];
_picData = [NSArray arrayWithObjects:imageAA, imageBB, nil];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:
[NSURL URLWithString:#"Image URL"]]];
dispatch_sync(dispatch_get_main_queue(), ^{
if(image!= nil){
[[cell myimage] setImage:image];
}else{
UIImage * image = [UIImage imageNamed:#"PLACEHOLDER"];
[[cell myimage] setImage:image];
}
[cell setNeedsLayout];
});
});
Try these...

sharedApplication takes long to display

So I added some code to this to work on both ios 8 and 9(5s and ipad). Before the change the 5s would load the share dialog fairly quick while the ipad would crash the device. After adding the fix they both don't crash, but now the ipad displays the share dialog fairly quick while the 5s takes roughly ~1-2 mins to display it. Not sure why that might be, maybe I did something wrong with my change?
#import <Foundation/Foundation.h>
extern "C" {
void _shareText(const char *text, const char *imgUrl)
{
NSString * message = [NSString stringWithUTF8String:text];
NSString * urlAddress = [NSString stringWithUTF8String:imgUrl];
NSURL *url = [NSURL URLWithString:urlAddress];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
NSMutableArray *sharingItems = [NSMutableArray new];
if (text) {
[sharingItems addObject:message];
}
if (img) {
[sharingItems addObject:img];
}
UIViewController *qtController = [[UIApplication sharedApplication].keyWindow rootViewController];
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
if ([activityController respondsToSelector:#selector(popoverPresentationController)] ){
[qtController presentViewController:activityController animated:YES completion:nil];
activityController.popoverPresentationController.sourceView = qtController.view;
}else{
[qtController presentViewController:activityController animated:YES completion:nil];
}
}
}
Update:
Unity Called function:
[DllImport("__Internal")]
private static extern void _shareText(string message, string imgUrl);
public static void InviteRequest(string message, string imgUrl)
{
#if UNITY_IOS
if (Application.platform != RuntimePlatform.OSXEditor)
{
_shareText(message, imgUrl);
}
#endif
}

Load remote server image in UIScrollView with NSOperatoinQueue

I want to load some "image" (In remote server) in a UIScrollView with NSOperatoinQueue. Because If I load it with normal NSURL, NSData or with NSMutableURLRequest it takes too much time to load for all the images. After that I show those images in UIButton. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self startAnimation:nil];
self.imageDownloadingQueue = [[NSOperationQueue alloc] init];
self.imageDownloadingQueue.maxConcurrentOperationCount = 4; // many servers limit how many concurrent requests they'll accept from a device, so make sure to set this accordingly
self.imageCache = [[NSCache alloc] init];
[self performSelector:#selector(loadData) withObject:nil afterDelay:0.5];
}
-(void) loadData
{
adParser = [[AdParser alloc] loadXMLByURL:getXMLURL];
adsListArray = [adParser ads];
displayArray = [[NSMutableArray alloc] init];
for (AdInfo *adInfo1 in adsListArray)
{
AdInfo *adInfo2 = [[AdInfo alloc] init];
[adInfo2 setBannerIconURL:adInfo1.bannerIconURL];
[adInfo2 setBannerIconLink:adInfo1.bannerIconLink];
[displayArray addObject:adInfo2];
}
[self loadScrollView];
[activityIndicator stopAnimating];
}
-(void) loadScrollView
{
[self.scrollView setScrollEnabled:YES];
[self.scrollView setContentSize:CGSizeMake([displayArray count] * ScrollerWidth, ScrollerHight)];
for (int i = 0; i < [displayArray count]; i++)
{
adButtonOutLet = [[UIButton alloc] initWithFrame:CGRectMake(i*320, 0, ButtonWidth, ButtonHight)];
currentAd = [displayArray objectAtIndex:i];
NSString *imageUrlString = [currentAd bannerIconURL];
UIImage *cachedImage = [self.imageCache objectForKey:imageUrlString];
if (cachedImage)
{
[adButtonOutLet setImage:cachedImage forState:UIControlStateNormal];
}
else
{
[self.imageDownloadingQueue addOperationWithBlock:^
{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrlString]];
UIImage *image = nil;
image = [UIImage imageWithData:imageData];
// add the image to your cache
[self.imageCache setObject:image forKey:imageUrlString];
// finally, update the user interface in the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
[adButtonOutLet setImage:image forState:UIControlStateNormal];
}];
}];
}
adButtonOutLet.userInteractionEnabled= YES;
[adButtonOutLet setTag:i];
[adButtonOutLet addTarget:self action:#selector(goToURL:) forControlEvents:UIControlEventTouchUpInside];
[self.scrollView addSubview:adButtonOutLet];
}
}
Can anyone tell me what's wrong with the above code? There is no problem of parsing or retrieving data from Remote server. I check it by NSLog. I think the NSOperationQueue have some problem, which I can't manage properly. Thanks in advance. If you needed more information, I will attach here.
Have a nice day.
Not sure if this is your problem or your solution, its hard to tell without testing myself.
Taken from RayWenderlich
addOperationWithBlock: if you have a simple operation that does not
need to be subclassed, you can create an operation using the block
API. If you want to reference any object from outside in the block,
remember that you should pass in a weak reference. Also, if you want
to do something that is related to the UI in the block, you must do it
on the main thread:
// Create a weak reference
__weak MyViewController *weakSelf = self;
// Add an operation as a block to a queue
[myQueue addOperationWithBlock: ^ {
NSURL *aURL = [NSURL URLWithString:#"http://www.somewhere.com/image.png"];
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:aURL options:nil error:&error];
UIImage *image = nil;
If (data)
image = [UIImage imageWithData:data];
// Update UI on the main thread.
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
weakSelf.imageView.image = image;
}];
}];

Creating UIImage array

I've written code that gathers images from JSON requests and attempts to add them to an NSMutableArray to be used later in a table view. The problem is after adding the image objects to the array, the size of my NSMutableArray is 0. Here is the relevant code:
#implementation example{
NSMutableArray *_placeImages;
}
-(void)viewDidLoad{
...
_placeImages = [[NSMutableArray alloc] init];
}
-(void)JsonQuery {
...
// Retrieve the results of the URL.
dispatch_async(kMyQueue, ^{
[self downloadImages];
[self performSelectorOnMainThread:#selector(reloadTableData) withObject:NULL waitUntilDone:YES];
});
}
-(void)downloadImages{
...
NSData* data = [NSData dataWithContentsOfURL: url];
UIImage* image = [UIImage imageWithData:data];
[_placeImages addObject:image];
}
you must alloc NSMutableArray first:
_placeImages = [[NSMutableArray alloc] init];
or you can using lazy init:
- (NSMutableArray *)placeImages
{
if (!_placeImages) {
_placeImages = [[NSMutableArray alloc] init];
}
return _placeImages
}

Multitasking with download images in iOS sdk

I have a UItableView that is listing number of outlets respectively, each outlet has a logo image. I save those images locally on iPhone, so if any image is found on iPhone it will fetch from there if not then it will send a service call and fetch data. It is working accordingly but when I scroll down on tableview it gets hanged at certain point when the image is being downloaded, as it is downloaded it works fine again. Is there any solution where I can perform this multitasking of downloading images along with representing them either from service call or fetching it locally.
Here is my code..
// downloading images of outlets locally
NSLog(#"Downloading...");
NSString *imageLink = [NSString stringWithFormat:#"http://cf.abc.pk/outlets/l/%#",outs.logo];
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageLink]]];
NSLog(#"%f,%f",image.size.width,image.size.height);
NSString *docDir =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSLog(#"%#",docDir);
NSLog(#"saving png");
NSString *pngFilePath = [NSString stringWithFormat:#"%#/%d.png",docDir,[[arrOutletIds objectAtIndex:webserviceCall] intValue]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pngFilePath];
if(!fileExists)
{
NSData *data1 = [NSData dataWithData:UIImagePNGRepresentation(image)];
[data1 writeToFile:pngFilePath atomically:YES];
}
Let's try:
//When you download something, execute in background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
//download some thing
dispatch_async( dispatch_get_main_queue(), ^{
// when download finish execute in main thread to update data
// this function should be called in response of downloading
});
});
Hope this helps you.
Download image in background will solve your problem here
Try this.
if(!fileExists)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data1 = [NSData dataWithData:UIImagePNGRepresentation(image)];
[data1 writeToFile:pngFilePath atomically:YES];
});
}
You can use this code which integrates ImageCache to download async and cache images. This part of code shows downloading images in UITableView. Or you can check out this project on GitHub SDImageCache
if ([[ImageCache sharedImageCache] DoesExist:photoURL] == true)
{
image = [[ImageCache sharedImageCache] GetImage:photoURL];
cell.imageView.image = image;
}
else
{
cell.imageView.image = [UIImage imageNamed:#"PlaceHolder"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:photoURL]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.section) {
cell.imageView.image = image;
[cell setNeedsLayout];
[[ImageCache sharedImageCache] AddImage:image imageURL:photoURL];
}
});
}
});
}
Import these files to your project
ImageCache.h
#import <Foundation/Foundation.h>
#interface ImageCache : NSObject
#property (nonatomic, retain) NSCache *imgCache;
#pragma mark - Methods
+ (ImageCache*)sharedImageCache;
- (void) AddImage:(UIImage *)image imageURL:(NSString *)imageURL;
- (UIImage*) GetImage:(NSString *)imageURL;
- (BOOL) DoesExist:(NSString *)imageURL;
#end
ImageCache.m
#import "ImageCache.h"
#implementation ImageCache
#synthesize imgCache;
#pragma mark - Methods
static ImageCache* sharedImageCache = nil;
+(ImageCache*)sharedImageCache
{
#synchronized([ImageCache class])
{
if (!sharedImageCache)
sharedImageCache= [[self alloc] init];
return sharedImageCache;
}
return nil;
}
+(id)alloc
{
#synchronized([ImageCache class])
{
NSAssert(sharedImageCache == nil, #"Attempted to allocate a second instance of a singleton.");
sharedImageCache = [super alloc];
return sharedImageCache;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil)
{
imgCache = [[NSCache alloc] init];
}
return self;
}
- (void) AddImage:(UIImage *)image imageURL:(NSString *)imageURL
{
if (image==nil) {
}
else
{
[imgCache setObject:image forKey:imageURL];
}
}
- (NSString*) GetImage:(NSString *)imageURL
{
return [imgCache objectForKey:imageURL];
}
- (BOOL) DoesExist:(NSString *)imageURL
{
if ([imgCache objectForKey:imageURL] == nil)
{
return false;
}
return true;
}
#end

Resources