How to write block definition using properties? - ios

I am writing a class for getting image from Photos Library.
I want one single method that will return selected image from library.
So i started writing a class named MediaBrowser.
I used block that will give selected image. But I am confused where to write block definition. Please correct the code if i am going wrong.
In MediaBrowser.h
#interface MediaBrowser : NSObject
typedef UIImage* (^MediaBrowserCompletionHandler)(void);
+ (id)sharedInstance;
- (BOOL)startMediaBrowserFromViewController:(UIViewController*)controller
completionHandler:(MediaBrowserCompletionHandler)completion;
#end
In MediaBrowser.m
#interface MediaBrowser () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
#property (nonatomic, strong) MediaBrowserCompletionHandler completionHandler;
#end
#implementation MediaBrowser
static MediaBrowser *sharedMediaBrowser = nil;
+ (id)sharedInstance
{
if (nil != sharedMediaBrowser) {
return sharedMediaBrowser;
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMediaBrowser = [[MediaBrowser alloc] init];
});
return sharedMediaBrowser;
}
- (BOOL)startMediaBrowserFromViewController:(UIViewController *)controller completionHandler:(MediaBrowserCompletionHandler)completion
{
self.completionHandler = [completion copy];
if (([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum] == NO)
|| (controller == nil))
return NO;
UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
mediaUI.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
// Displays saved pictures from the Camera Roll album.
mediaUI.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeImage, nil];
// Hides the controls for moving & scaling pictures, or for
// trimming movies. To instead show the controls, use YES.
mediaUI.allowsEditing = NO;
mediaUI.delegate = self;
[controller presentModalViewController:mediaUI animated:YES];
return YES;
}
// UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
UIImage *imageToUse;
// Handle a still image picked from a photo album
if (CFStringCompare ((CFStringRef) mediaType, kUTTypeImage, 0) == kCFCompareEqualTo) {
imageToUse = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
// Do something with imageToUse
if (self.completionHandler) {
// Pass here UIImage
self.completionHandler();
}
}
[picker dismissModalViewControllerAnimated:YES];
}
#end

I figured out the answer. I wanted to send selected image from UIImagePickerController to the calling class. I was writing wrong block. See corrected code below.
In MediaBrowser.h
block declaration shoudld be :
typedef void (^MediaBrowserCompletionHandler)(UIImage *selectedImage);
And in MediaBroser.m
Calling block should be :
if (self.completionHandler) {
self.completionHandler(imageToUse);
}

typedef UIImage* (^MediaBrowserCompletionHandler)(void);
#interface MediaBrowser : NSObject
#property (nonatomic,copy) MediaBrowserCompletionHandler handler;
#end

This should be fine.
typedef UIImage* (^MediaBrowserCompletionHandler)(void);
#interface MediaBrowser : NSObject
....
#end

Related

Not able to share Docx in form of NSDATA using UIActivityViewController

I am using UIWebView to open a Docx file which is in the form of NSDATA passed from another controller to webview. I am using UIAcvitityController to share content on social media as well. It works fine for Image types and PDF types but it does not work for DOCX and XLSX. Anything special I need to do here?
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:self.downloadData applicationActivities:self.applicationActivities];
You need use custom data for activityItems, and implement methods in protocol UIActivityItemSource. follow it:
file: ShareData.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface ShareData : NSObject<UIActivityItemSource>
#property (nonatomic, strong) NSData *data;
#property (nonatomic, strong) NSURL *url;
#end
file:ShareData.m
#import "ShareData.h"
#implementation ShareData
-(id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
return self.data;
}
-(id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType {
if ([activityType isEqualToString:UIActivityTypeAirDrop]) {
return self.data;
}
return self.url;
}
-(NSString*)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType {
return #"share data";
}
-(NSString*)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType {
if ([activityType isEqualToString:UIActivityTypeAirDrop]) {
return #"test";
}
return nil;
}
#end
And, share content on social media:
ShareData *share = [ShareData new];
share.data = downloadData; // one of your downloadData
share.url = downloadURL; // url for the downloadData
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[share] applicationActivities:nil];
The above code has been tested , it does work for DOCX and XLSX.
The source demo is here: https://github.com/ocarol/UIActivityViewControllerDemo
Supported Data Types by UIActivityViewController

UILabel being referenced as an integer pointer in xcode [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
The Goal and Problem:
After every passing second, the value of 'regularBubbleCount' - a variable of RWGameData - increases by 1. I am attempting to display this change in value by passing the new value of 'regularBubbleCount' to the 'regularBubLabel' UILabel in the PrimaryViewController. I am attempting to do this by using the following line of code,
_regularBubLabel.text = [NSString stringWithFormat:#"%li", [RWGameData sharedGameData].regularBubbleCount];
Obviously this does not work because 'regularBubLabel' is not an object of the RWGameData class where the 'timerCalled' method resides. How can I change the value of the 'regularBubLabel' from inside the RWGameData class?
RWGameData.h
#import <Foundation/Foundation.h>
#interface RWGameData : NSObject <NSCoding>
#property (assign, nonatomic) long regularBubbleCount;
#property (assign, nonatomic) BOOL dataIsInitialized;
+(instancetype)sharedGameData;
-(void)reset;
-(void)save;
-(void)timerSetup;
-(void)timerCalled;
#end
RWGameData.m
#import "RWGameData.h"
#implementation RWGameData
static NSString* const SSGameDataRegularBubbleCountKey = #"regularBubbleCount";
static NSString* const SSGameDataIsInitializedKey = #"dataIsInitializedKey";
+ (instancetype)sharedGameData {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self loadInstance];
});
return sharedInstance;
}
-(void)reset {
self.regularBubbleCount = 0;
self.dataIsInitialized = true;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeDouble:self.regularBubbleCount forKey: SSGameDataRegularBubbleCountKey];
[encoder encodeBool:self.dataIsInitialized forKey: SSGameDataIsInitializedKey];
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
self = [self init];
if (self) {
_regularBubbleCount = [decoder decodeDoubleForKey: SSGameDataRegularBubbleCountKey];
_dataIsInitialized = [decoder decodeBoolForKey: SSGameDataIsInitializedKey];
}
return self;
}
+(NSString*)filePath
{
static NSString* filePath = nil;
if (!filePath) {
filePath =
[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
stringByAppendingPathComponent:#"gamedata"];
}
return filePath;
}
+(instancetype)loadInstance
{
NSData* decodedData = [NSData dataWithContentsOfFile: [RWGameData filePath]];
if (decodedData) {
RWGameData* gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
return gameData;
}
return [[RWGameData alloc] init];
}
-(void)save
{
NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: self];
[encodedData writeToFile:[RWGameData filePath] atomically:YES];
}
- (void)timerSetup { // to be called from delegate didFinishLaunching….
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerCalled) userInfo:nil repeats:YES];
}
-(void)timerCalled
{
[RWGameData sharedGameData].regularBubbleCount++;
/* THE ISSUE IS HERE */
_regularBubLabel.text = [NSString stringWithFormat:#"%li", [RWGameData sharedGameData].regularBubbleCount];
[[RWGameData sharedGameData] save];
} NSLog(#"Regular Bubble Count: %li", [RWGameData sharedGameData].regularBubbleCount);
}
#end
PrimaryViewController.h
#import <UIKit/UIKit.h>
#import "RWGameData.h"
#interface PrimaryViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *regularBubLabel;
#end
PrimaryViewController.m
#import "PrimaryViewController.h"
#interface PrimaryViewController ()
#end
#implementation PrimaryViewController
{
NSString *bubbleImage;
UIImage *backgroundImage;
UIImageView *backgroundImageView;
int r;
int i;
}
- (void)viewDidLoad {
[super viewDidLoad];
backgroundImage = [UIImage imageNamed:#"background_new.png"];
backgroundImageView=[[UIImageView alloc]initWithFrame:self.view.frame];
backgroundImageView.image=backgroundImage;
[self.view insertSubview:backgroundImageView atIndex:0];
}
- (void)viewDidAppear:(BOOL)animated {
_regularBubLabel.text = [NSString stringWithFormat:#"%li", [RWGameData sharedGameData].regularBubbleCount];
_premiumBubLabel.text = [NSString stringWithFormat:#"%li", [RWGameData sharedGameData].premiumBubbleCount];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)increment {
if ([RWGameData sharedGameData].megaBubblePopValue == 0) {
[RWGameData sharedGameData].megaBubblePopValue++;
[[RWGameData sharedGameData] save];
}
if ([#"mysterybubble.png" isEqual:bubbleImage]) {
[RWGameData sharedGameData].premiumBubbleCount += 2;
_premiumBubLabel.text = [NSString stringWithFormat:#"%li", [RWGameData sharedGameData].premiumBubbleCount];
} else if ([#"megaBubbleLarge30.png" isEqual:bubbleImage]) {
[RWGameData sharedGameData].regularBubbleCount += [RWGameData sharedGameData].megaBubblePopValue;
_regularBubLabel.text = [NSString stringWithFormat:#"%li", [RWGameData sharedGameData].regularBubbleCount];
} i++;
}
- (IBAction)save {
[[RWGameData sharedGameData] save];
}
- (IBAction)setBubbleStatus {
r = arc4random_uniform(400);
if (r <= 1) {
bubbleImage = #"mysterybubble.png";
[_megaBubbleButton setImage:[UIImage imageNamed:bubbleImage] forState:UIControlStateNormal];
NSLog(#"Roll SUCCESS. [%i] %i", i, r);
} else {
bubbleImage = #"megaBubbleLarge30.png";
[_megaBubbleButton setImage:[UIImage imageNamed:bubbleImage] forState:UIControlStateNormal];
NSLog(#"Roll FAIL. [%i] %i", i, r);
}
}
#end
It makes no sense to have an IBOutlet in your RWGameData class, since it's a subclass of NSObject. That class has no instance (and no view) in the storyboard, so you can't hook up an IBOutlet. One way to accomplish your goal would be to make PrimaryViewController a delegate of RWGameData, and call a delegate method inside the timer's action method that would pass whatever data you need so PrimaryViewController can update its own label.
I am going to make the following assumptions:
Assumption #1 -RWGameData is instantiated inside the ApplicationDelegate when the application is loaded and can be referenced in the application delegate as self.gameData
Assumption #2 - PrimaryViewController is, as you have named it, the primary view controller in your storyboard.
Assumption #3 - You're using an NSTimer to update this count every 1 second.
Assumption #4 - Your Storyboard file is called Mainstorybaord
RWGameData
Add this to your RWGameData.h before your class definition.
#class RWGameData;
#protocol RWGameStateProtocol <NSObject>
-(void)StateUpdateForGameData:(RWGameData*)data;
#end
Inside RWGameData.h add a delegate in your class definition.
#interface RWGameData : NSObject
#property(weak)id<RWGameStateProtocol> delegate;
#end
In your selector timerCalled add a call to your delegate informing it the data has changed.
-(void)timerCalled
{
[RWGameData sharedGameData].regularBubbleCount++;
/* THE ISSUE IS HERE */
_regularBubLabel.text = [NSString stringWithFormat:#"%li", [RWGameData sharedGameData].regularBubbleCount];
[[RWGameData sharedGameData] save];
} NSLog(#"Regular Bubble Count: %li", [RWGameData sharedGameData].regularBubbleCount);
/* Inform the delegate */
[self.delegate StateUpdateForGameData:self];
}
PrimaryViewController
Once you have your protocol defined have your PrimaryViewcontroller conform to this protocol with something similar to the following.
-(void)StateUpateforGameData:(RWGameData*)data
{
self.regularBubLabel.text = [[NSString alloc]initWithFormat:#"%i",data.regularBubbleCount];
}
Application Delegate
All that remains is to set the delegate on your RWGameData instance. You can do this by asking storyboard to provide you an instance of your primary view controller. If you're using storyboard then by default this method is empty. However, since we are going to modify the primary view controller before it is shown then we need to make a few changes and perform some of the work Storyboard already does for you.
In your ApplicationDelegate's header file create a property for a UIWindow and RWGameData.
#interface YourAppDelegate : UIResponder <UIApplicationDelegate>
#property(strong,nonatomic) UIWindow *window;
#property(strong,nonatomic) RWGameData *gameData;
#end
Now that we have the properties setup you need to perform some additional work. Note that normally you don't have to perform this step when using storyboard.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
/// Screen Information
CGFloat scale = [[UIScreen mainScreen]scale];
CGRect frame = [[UIScreen mainScreen]bounds];
UIWindow* window = [[UIWindow alloc]initWithFrame:frame];
self.window = window;
self.window.contentScaleFactor = scale;
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
PrimaryViewController *vc = (PrimaryViewController*)[sb instantiateInitialViewController];
RWGameData *gameData = [[RWGameData alloc]init];
gameData.delegate = vc;
self.gameData = gameData;
[window setRootViewController:vc];
[window makeKeyAndVisible];
return YES;
}
This approach doesn't require you to tightly couple your label and game data. Instead any update to your game data can now inform your view and allows your view to select which labels need to be updated.

UIActivityViewController to share multiple images

I am trying to share images via UIActivityViewController similar to this. However if I share several images, Twitter and Facebook will disappear in the UIActivityViewController.
Is there a way to share one image for both Twitter and Facebook, several images for mail as attachment?
EDIT:
// return different string depends on the type
CustomActivityItemProvider *textProvider = [[CustomActivityItemProvider alloc] initWithText:textContent url:url title:textTitle];
NSMutableArray *applicationActivities = [NSMutableArray array];
NSMutableArray *activityItems = [#[
textProvider,
image,
url
] mutableCopy];
// custom applicationActivities
...
// If add multiple images, facebook and twitter will not show up
for(int i = 0; i < [images count]; ++i)
{
if(images[i] != image) [activityItems addObject:images[i]];
}
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:applicationActivities];
[activityController setValue:textTitle forKey:#"subject"];
activityController.excludedActivityTypes = excludeActivities;
[self presentViewController:activityController animated:YES completion:nil];
Is there a way similar to the UIActivityItemProvider?
A similar way to textActivityItemProvider I've end up using:
ImageActivityItemProvider.h
#import <UIKit/UIKit.h>
#interface ImageActivityItemProvider : UIActivityItemProvider
#property (nonatomic, strong, readonly) UIImage *image;
#property (nonatomic, readonly) NSInteger index;
#property (nonatomic, readonly) NSInteger shouldShowIndex;
- (instancetype)initWithImage:(UIImage*)image index:(NSInteger)index shouldShowIndex:(NSInteger)shouldShowIndex;
#end
ImageActivityItemProvider.m
#import "ImageActivityItemProvider.h"
#interface ImageActivityItemProvider ()
#property (nonatomic, strong) UIImage *image;
#property (nonatomic) NSInteger index;
#property (nonatomic) NSInteger shouldShowIndex;
#end
#implementation ImageActivityItemProvider
- (instancetype)initWithImage:(UIImage*)image index:(NSInteger)index shouldShowIndex:(NSInteger)shouldShowIndex
{
// make sure the placeholder is nil instead of the image
self = [super initWithPlaceholderItem:nil];
if (self)
{
self.image = image;
self.index = index;
self.shouldShowIndex = shouldShowIndex;
}
return self;
}
- (id)item
{
if (
[self.activityType isEqualToString:UIActivityTypeMail] ||
self.index == self.shouldShowIndex
)
{
return self.image;
}
return self.placeholderItem;
}
#end

Need to capture screen without delay

So guys I'm using this function to take a picture of the UIWebView which loading an e-book and display it while the user render the pages
-(UIImage*)captureScreen:(UIView*) viewToCapture
{
UIGraphicsBeginImageContextWithOptions(viewToCapture.bounds.size, viewToCapture.opaque, 0.0);
[viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
but the problem there is a delay (0.5 second) happened to get the image .. when i test in the time profiler the instruments point to this line [viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()]; as the one which causing the delay .. so any advice , suggestion how could I come over this delay.
Thanks in advanced.
Notes :
1.Im using UIPageController for the book effect.
2.to check what I have tried please check this link
The best way of solving this is to use delegates. Meaning that if you tell a delegate method to make the image, it can do it in the background and tell your view "Hey, now I got you an image for XXX" (depending on how you implement it.
This way you can load your view with the e-books in it and just show a loader in the middle of the book with a default background. When the image is done, you update the view for the book with the correct image and remove the loader.
Much like how Apple's iBooks and any other good application does.
Example from one of my own projects (adapted to your need, yet for use in UITableViewController):
BookDelegate.h
#import <Foundation/Foundation.h>
#class Book;
#protocol BookDelegate <NSObject>
#optional
- (void)didRecieveImageForBook:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath;
#end
Book.h
#import <Foundation/Foundation.h>
#import "BookDelegate.h"
#interface Book : NSOperation <NSObject>
{
id <BookDelegate> delegate;
SEL didRecieveImageForBookSelector;
}
#property (strong, nonatomic) id delegate;
#property (assign) SEL didRecieveImageForBookSelector;
- (NSString*)getBookImageForBookId:(int)BookId externalRefference:(NSString*)url indexPath:(NSIndexPath*)indexPath;
- (id)delegate;
// Delegate methods
- (void)didRecieveImageForBook:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath;
#end
Book.m
#import "Book.h"
#import <objc/runtime.h>
#implementation Book
static char kAssociationKey;
#synthesize didRecieveImageForBookSelector;
#synthesize delegate;
- (id)init
{
if (self = [super init])
{
[self setDidRecieveImageForBookSelector:#selector(didRecieveImageForBook:indexPath:)];
}
return self;
}
#pragma mark -
#pragma mark The default delegate functions
- (void)didRecieveImageForBook:(NSString*)imagePath indexPath:(NSIndexPath*)indexPath
{
NSLog(#"********************************************************");
NSLog(#"*** PLEASE IMPLEMENT THE FOLLOWING DELEGATE FUNCTION ***");
NSLog(#"*** didRecieveImageForBook:indexPath: ***");
NSLog(#"********************************************************");
}
#pragma mark -
#pragma mark Function for fechting images
// This method is not adapted to what YOU need, but left my code here in case it might help you out.
- (NSString*)getBookImageForBookId:(int)bookId externalRefference:(NSString*)url indexPath:(NSIndexPath*)indexPath
{
NSString *ext = [[url lastPathComponent] pathExtension];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *imagePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#%d.%#", APP_BookIMAGEPEFIX, BookId, ext]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:imagePath];
if (fileExists)
{
return imagePath;
}
else {
NSURL *theUrl = [NSURL URLWithString:url];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:theUrl];
[request setDidFinishSelector:#selector(BookImageFetched:)];
[request setDidFailSelector:#selector(processFailed:)];
[request setTimeOutSeconds:60];
[request setDownloadDestinationPath:imagePath];
[request setDelegate:self];
[request startAsynchronous];
objc_setAssociatedObject(request, &kAssociationKey, indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return #"";
}
}
- (void)BookImageFetched:(ASIHTTPRequest *)request
{
NSIndexPath *indexPath = objc_getAssociatedObject(request, &kAssociationKey);
NSString *imagePath = request.downloadDestinationPath;
[[self delegate] performSelector:self.didRecieveImageForBookSelector withObject:imagePath withObject:indexPath];
}
#pragma mark -
#pragma mark delegate functions
- (id)delegate
{
return delegate;
}
- (void)setDelegate:(id)newDelegate
{
delegate = newDelegate;
}
#pragma mark -
#end
You could use GCD - I noticed a a step missing from the GCD in the link you gave. This is what I use to asynchronously get an image and notify when its ready and it works fine:
dispatch_queue_t concurrent = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(concurrent, ^{
__block UIImage *image = nil;
dispatch_sync(concurrent, ^{
//put code to grab image here
});
dispatch_sync(dispatch_get_main_queue(), ^{
//this gets called when the above is finshed
//you should also check if the image is nil or not
});
});
hope it helps
for the record - I use this for taking UIView snapshots and always try to put my target inside a parentview even if it is temporarily - it seems to speed it up.
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
not sure if it helps or if you are using the same approach. I hope you solve this soon :)

Load "Loading" image before laoding Image from server?

I am new to iOS development so please bear with me.
I am trying to create a basic photo gallery but ran into a problem.
When I started out with the project I just included all the images in my project.
Now after having a lot more images(400+) I started loading them from a server.
I made an array of images using the following line of code:
[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.testsite.com/testPic.png"]]]
Obviously making the user wait for an array of 400+ images to load from a server is unacceptable.
So my question is if I included one image in my project that said something like "Loading", how could I display that image until the actual image loaded from the server?
I'm making a basic grid-style photo gallery using a table-view and scroll-view. It loads up a few rows of small(thumbnail) images and when you click one it makes it full screen.
I'm using Xcode 4.3, ARC, and storyboards if that helps!
Sorry if this is confusing!
-Shredder2794
The simplest way of doing this is to use AFNetworking, which provides setImageWithURL:placeholderImage: in a category on UIImageView (also a method with success/failure handlers).
A method is to subclass UIImageView. When you allocate it you put a default image ( the loading one) or a UIActivityIndicator, download the image you want to display ( in a separate thread) and when the image is downloaded display it. Have a look to NSURLRequest and NSURLConnection for the image download.
* EDIT *
Here is an example of code I developed. You can use this as a start point to develop your own loading image class. This class can be improved by using NSThread for the image loading.
// ImageLoader.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
/**
* #brief Class that loads an UIImage from a server
* #author Nicolas
*/
#interface ImageLoader : UIView
{
#private
NSURLConnection *connection;
NSMutableData *data;
NSString *path;
UIActivityIndicatorView *loading;
UIImageView *imageView;
}
#property (nonatomic, retain) UIActivityIndicatorView *loading;
#property (nonatomic, retain) NSURLConnection *connection;
#property (nonatomic, retain) NSMutableData *data;
#property (nonatomic, retain) NSString *path;
#property (nonatomic, retain) UIImageView *imageView;
/**
* Load an image from a server an display it
* #param URL URL to get the image
* #param chemin path to save the image
* #author Nicolas
*/
- (void)loadImageFromUrl:(NSString *)URL forPath:(NSString *)chemin;
#end
// ImageLoader.m
#import "ImageLoader.h"
#implementation ImageLoader
#synthesize path, connection, data, loading, imageView;
- (id)init
{
self = [super init];
[self setUserInteractionEnabled:YES];
return self;
}
- (void)loadImageFromUrl:(NSString *)URL forPath:(NSString *)chemin
{
//if (connection != nil) [connection release];
//if (data != nil) [data release];
//if (path != nil) [path release];
self.path = chemin;
if ([[NSFileManager defaultManager] fileExistsAtPath:chemin])
{
if (imageView != nil)
{
[imageView removeFromSuperview];
[imageView release];
}
imageView = [[UIImageView alloc] initWithFrame:self.bounds];
imageView.image = [UIImage imageWithContentsOfFile:chemin];
[self addSubview:imageView];
}
else
{
loading = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20.0f, 20.0f)];
loading.center = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
[loading setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleGray];
[loading startAnimating];
[self addSubview:loading];
NSURL *myURL = [NSURL URLWithString:[URL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:myURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0f];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
}
#pragma mark -
#pragma mark NSURLConnection protocol
- (void)connection:(NSURLConnection *)_connection didReceiveData:(NSData *)_data
{
if (data == nil)
{
data = [[NSMutableData alloc] initWithCapacity:2048];
}
[data appendData:_data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)_connection
{
[data writeToFile:self.path atomically:YES];
if (imageView != nil)
{
[imageView removeFromSuperview];
[imageView release];
}
imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:data]];
imageView.frame = self.bounds;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[loading stopAnimating];
[loading setHidden:YES];
[self addSubview:imageView];
}
- (void)connection:(NSURLConnection *)_connection didFailWithError:(NSError *)error
{
[loading stopAnimating];
[loading release];
}
#pragma mark - Memory management
- (void)dealloc
{
[connection cancel];
[connection release];
[imageView release];
[path release];
[data release];
[super dealloc];
}
#end

Resources