Async Image download a UITableView's cellForRowAtIndexPath not always called - objective-c

I using Apple's LazyTableImages sample to async download images for UITableViewCells. The main difference is that I'm saving a copy of the file to the Documents folder so that it doesn't need to be downloaded all the time. The method below is called by the class responsible for downloading the image
- (void)imageDidLoad:(NSIndexPath *)indexPath
{
AsyncImage *aImage = [imageDownloadsInProgress objectForKey:indexPath];
if (aImage != nil)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:aImage.indexPathInTableView];
cell.imageView.image = aImage.image;
}
}
When the image is not on the device, the download goes fine and the image is displayed. However when the image is on the device the image is not displayed and the cell variable is null.
The code for the AsyncImage is:
//
// AsyncImgView.m
// BelfastChamberOfCommerce
//
// Created by markpirvine on 23/06/2011.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "AsyncImage.h"
#import "AppManager.h"
#define kAppIconHeight 48
#interface AsyncImage ()
#property (nonatomic, retain) NSString *fileUrl;
#end
#implementation AsyncImage
#synthesize imageUrl, fileUrl;
#synthesize image;
#synthesize indexPathInTableView;
#synthesize delegate;
- (void)dealloc
{
[imageUrl release];
[fileUrl release];
[image release];
[indexPathInTableView release];
[connection cancel];
[connection release];
[data release];
[super dealloc];
}
- (void)loadImage
{
if (connection != nil)
{
[connection release];
}
if (data != nil)
{
[data release];
}
if([self.imageUrl length] > 0)
{
self.fileUrl = [[[AppManager sharedInstance] getCacheLocation] stringByAppendingPathComponent:[[self.imageUrl componentsSeparatedByString:#"/"] lastObject]];
if([[NSFileManager defaultManager] fileExistsAtPath:self.fileUrl] == YES)
{
self.image = [UIImage imageWithContentsOfFile:self.fileUrl];
[self deliverImage];
}
else
{
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.imageUrl] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
}
}
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData
{
if (data == nil)
{
data = [[NSMutableData alloc] initWithCapacity:2048];
}
[data appendData:incrementalData];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection
{
[connection release];
connection = nil;
UIImage *iTmp = [[UIImage alloc] initWithData:data];
if (iTmp.size.width != kAppIconHeight && iTmp.size.height != kAppIconHeight)
{
CGSize itemSize = CGSizeMake(kAppIconHeight, kAppIconHeight);
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[iTmp drawInRect:imageRect];
self.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
else
{
self.image = iTmp;
}
[UIImageJPEGRepresentation(self.image, 1.0) writeToFile:self.fileUrl atomically:YES];
[iTmp release];
[data release];
data = nil;
[self deliverImage];
}
- (void)deliverImage
{
[delegate imageDidLoad:self.indexPathInTableView];
}
- (void)cancelDownload
{
[connection cancel];
connection = nil;
data = nil;
}
#end
Any help would be greatly appreciated

I suggest using one of the open source classes for async download of images that has built-in support for caching. The one that I'm using is cached image for iOS.

Related

How to cache the Images that is loaded in TableViewCell - Firebase and Obj-C

I use firebase to upload messages, images, videos. I'm able to upload and download the images, messages, and videos. However, when I populate data in UITableView and scroll the UITableView, the image gets changed for every scroll. How do I cached the images that are already downloaded? I got a reference from this site, where he's creating a NSCache using the extension file(from 17:22 Mins). It is in Swift, but I want the same thing in Objective-c.
Can somebody help me in getting the same thing in Objective-c?
Below is the code to download and display image in the UITableView
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"GroupCell";
_groupCell = (GroupTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
_groupModel = _groups[indexPath.row];
_groupCell.groupName.text = _groupModel.groupName;
_groupCell.recentMessage.text = #"Development is in progress";
_groupCell.recentMessageTime.text = #"6:00 PM";
_groupCell.unreadMessages.text = #"5";
_groupCell.groupDisplayPic.image = [UIImage imageNamed:#"defaultprofilepic.png"];
NSString *groupPicUrl = [NSString stringWithFormat:#"%#",_groupModel.groupPic];
//if (groupPicUrl!=nil) {
NSURL *url = [NSURL URLWithString:groupPicUrl];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//Download Images
if (data) {
_groupProfilePic = [UIImage imageWithData:data];
if (_groupProfilePic) {
dispatch_async(dispatch_get_main_queue(), ^{
GroupTableViewCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell) {
updateCell.groupDisplayPic.image = _groupProfilePic;
UIGraphicsBeginImageContextWithOptions(_groupCell.groupDisplayPic.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:updateCell.groupDisplayPic.bounds cornerRadius:100.0] addClip];
[_groupProfilePic drawInRect:updateCell.groupDisplayPic.bounds];
updateCell.groupDisplayPic.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
});
}
}
}];
[dataTask resume];
return _groupCell;
}
//Update
Tried so far:
_groupCell.groupDisplayPic.image = [UIImage imageNamed:#"defaultprofilepic.png"];
NSString *groupPicUrl = [NSString stringWithFormat:#"%#",_groupModel.groupPic];
NSURL *url = [NSURL URLWithString:groupPicUrl];
NSURLSession *session = [NSURLSession sharedSession];
_groupProfilePic = [[GP sharedInstance]getCachedImageForKey:groupPicUrl];
if (_groupProfilePic) {
NSLog(#"Image loaded from cache");
_groupCell.groupDisplayPic.image = _groupProfilePic;
}
else
{
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
_groupProfilePic = [UIImage imageWithData:data];
if (_groupProfilePic) {
dispatch_async(dispatch_get_main_queue(), ^{
GroupTableViewCell * updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell) {
updateCell.groupDisplayPic.image = _groupProfilePic;
// _groupCell.groupDisplayPic.image = _groupProfilePic;
UIGraphicsBeginImageContextWithOptions(updateCell.groupDisplayPic.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:updateCell.groupDisplayPic.bounds cornerRadius:100.0] addClip];
[_groupProfilePic drawInRect:updateCell.groupDisplayPic.bounds];
updateCell.groupDisplayPic.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
});
}
}
}];
[dataTask resume];
//Helper Method - Just followed the same steps from this SO
GP.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface GP : NSObject
+(GP*)sharedInstance;
// set
- (void)cacheImage:(UIImage*)image forKey:(NSString*)key;
// get
-(UIImage*)getCachedImageForKey:(NSString*)key;
#end
#import "GP.h"
static GP *sharedInstance;
#interface GP ()
#property (nonatomic, strong) NSCache *imageCache;
#end
GP.m
#import "GP.h"
#implementation GP
+ (GP*)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GP alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
self.imageCache = [[NSCache alloc] init];
}
return self;
}
- (void)cacheImage:(UIImage*)image forKey:(NSString*)key {
[self.imageCache setObject:image forKey:key];
}
- (UIImage*)getCachedImageForKey:(NSString*)key {
return [self.imageCache objectForKey:key];
}
#end

How to login With instagram and share wtih instagram Using IOS ? Objective c

1) Register to Instagram
To create Application using the Instagram API, you must have an account with Instagram.
After creating your account in Instagram, login with Instagram account and open the URL: http://instagram.com/developer/
Go to “Register your application” and click on the “Register New Client” button.
You will be asked to provide Application Name, Description of your application, website and oAuth redirect_url. Here, the oAuth redirect_url specifies where to redirect users after they have chosen whether or not to authenticate your application.
After successfully registering new client, Instagram will provide CLIENT INFO like CLIENT ID, CLIENT SECRET, WEBSITE URL, REDIRECT URI. Save CLIENT ID,CLIENT SECRET, REDIRECT URI to your application constants class as you will need this for authenticating to Instagram.
Lets see how can we authenticate…
Step 1.
At First set the authentication URL
Create a header file and provide the header file name is ConstantHandle
and set the all authentication URL
#ifndef ConstantHandler_h
#define ConstantHandler_h
//set User authentication and url
#define INSTAGRAM_AUTHURL #"https://api.instagram.com/oauth/authorize/"
#define INSTAGRAM_APIURl #"https://api.instagram.com/v1/users/"
#define INSTAGRAM_CLIENT_ID #"Client Id"
#define INSTAGRAM_CLIENTSERCRET #"Clients Secret"
#define INSTAGRAM_REDIRECT_URL #"Redirect URL"
#define INSTAGRAM_ACCESS_TOKEN #"access_token"
#define INSTAGRAM_SCOPE   #"likes+comments+relationships+basic"
//Contant Url
#define ACCESS_TOKEN #"#access_token="
#define UNSIGNED #"UNSIGNED"
#define CODE #"code="
#define END_POINT_URL #"https://api.instagram.com/oauth/access_token"
#define HTTP_METHOD #"POST"
#define CONTENT_LENGTH #"Content-Length"
#define REQUEST_DATA #"application/x-www-form-urlencoded"
#define CONTENT_TYPE #"Content-Type"
//share Photo Constant
#define DOCUMENT_FILE_PATH #"Documents/originalImage.ig"
#define APP_URL #"instagram://app"
#define UTI_URL #"com.instagram.exclusivegram"
#define MESSAGE #"Instagram not installed in this device!\nTo share image please install instagram."
#endif /* ConstantHandler_h */
At first set the all micro
Step 2.
Add a file in your project
Provide the file name is
InstagramController
create a file inherit with UIViewController
Add the this code in InstagramController.h file
#import <UIKit/UIKit.h>
#import "ConstantHandler.h"
#interface InstagramController : UIViewController
- (void)loginWithInstagramWithParsentViewController:(UIViewController *)controller completionHandler:(void(^)(NSDictionary *userProfileInformation))completionHanlder failureHandler:(void(^)(NSDictionary *errorDetail))failureHandler;
- (void)sharePhotoWithInstagaramWithImage:(UIImage *)image parsentViewcontroller:(UIViewController *)controller;;
#end
Add the this code in InstagramController.m file
#import "InstagramController.h"
#interface InstagramController ()<UIWebViewDelegate,UIDocumentInteractionControllerDelegate>
{
UIView *_progressView;
UIWebView *_webView;
UIViewController *_controller;
UIActivityIndicatorView *_activitiyIndicator;
}
#property(strong,nonatomic)NSString *typeOfAuthentication;
#property (nonatomic, strong) void(^completionHandler)(NSDictionary *);
#property (nonatomic, strong) void(^failureHandler)(NSDictionary *);
#property(nonatomic,strong)UIDocumentInteractionController *docFile;
#end
#pragma mark - View Controller Life Cycle Method
#implementation InstagramController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)viewWillAppear:(BOOL)animated {
//Add the cancel Button
UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(10, 30, 70, 20)];
[button setTitle:#"Cancel" forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[button addTarget:self action:#selector(cancel:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
//Setup The UIWebView
[super viewDidAppear: animated];
_webView = [UIWebView new];
CGRect frame = self.view.frame;
frame.origin.y = 64;
_webView.frame = frame;
_webView.delegate = self;
[self.view addSubview:_webView];
//Hit instagaram API;
NSString* authURL = nil;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [storage cookies]) {
[storage deleteCookie:cookie];
}
[[NSUserDefaults standardUserDefaults] synchronize];
if ([_typeOfAuthentication isEqualToString:UNSIGNED])
{
authURL = [NSString stringWithFormat: #"%#?client_id=%#&redirect_uri=%#&response_type=token&scope=%#&DEBUG=True",
INSTAGRAM_AUTHURL,
INSTAGRAM_CLIENT_ID,
INSTAGRAM_REDIRECT_URL,
INSTAGRAM_SCOPE];
}
else
{
authURL = [NSString stringWithFormat: #"%#?client_id=%#&redirect_uri=%#&response_type=code&scope=%#&DEBUG=True",
INSTAGRAM_AUTHURL,
INSTAGRAM_CLIENT_ID,
INSTAGRAM_REDIRECT_URL,
INSTAGRAM_SCOPE];
}
[_webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: authURL]]];
}
#pragma mark - Local Method
/*!
Setup The genral Indicator View
#retur void
*/
- (void)addIndicatorView {
_progressView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 62)];
_progressView.layer.cornerRadius = 10;
_progressView.layer.masksToBounds = YES;
_progressView.backgroundColor = [UIColor blackColor];
_progressView.alpha = 0.7;
UILabel *lable = [[UILabel alloc]initWithFrame:CGRectMake(10,43, 80, 15)];
lable.textColor = [UIColor grayColor];
[lable setTextAlignment:NSTextAlignmentCenter];
lable.font = [UIFont italicSystemFontOfSize:15.0f];
lable.text = #"progress...";
[_progressView addSubview:lable];
_activitiyIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[_activitiyIndicator setCenter:CGPointMake(_progressView.frame.size.width/2.0, _progressView.frame.size.height/2.3)]; // I do this because I'm in landscape mode
[_progressView addSubview:_activitiyIndicator];
[_activitiyIndicator startAnimating];
_progressView.center = self.view.center;
[_webView addSubview:_progressView];
}
/*!
This method is used to request to call back Url and check the authenTication
#param NSURLRequest is the check the request URl;
#return BOOLk
*/
- (BOOL) checkRequestForCallbackURL: (NSURLRequest*) request
{
NSString* urlString = [[request URL] absoluteString];
if ([_typeOfAuthentication isEqualToString:UNSIGNED])
{
// check, if auth was succesfull (check for redirect URL)
if([urlString hasPrefix: INSTAGRAM_REDIRECT_URL])
{
// extract and handle access token
NSRange range = [urlString rangeOfString: ACCESS_TOKEN ];
[self handleAuth:[urlString substringFromIndex: range.location+range.length] withProfileInfo:nil];
return NO;
}
}
else
{
if([urlString hasPrefix: INSTAGRAM_REDIRECT_URL])
{
// extract and handle code
NSRange range = [urlString rangeOfString: CODE];
[self makePostRequest:[urlString substringFromIndex: range.location+range.length]];
return NO;
}
}
return YES;
}
/*!
This method is used to get the User profile information
#param NSString is a authToken
#return void;
*/
-(void)makePostRequest:(NSString *)authToken
{
NSString *post = [NSString stringWithFormat:#"client_id=%#&client_secret=%#&grant_type=authorization_code&redirect_uri=%#&code=%#",INSTAGRAM_CLIENT_ID,INSTAGRAM_CLIENTSERCRET,INSTAGRAM_REDIRECT_URL,authToken];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
// URL of the endpoint we're going to contact.
NSURL *url = [NSURL URLWithString:END_POINT_URL];
// Create a POST request with our JSON as a request body.
NSMutableURLRequest *requestData = [NSMutableURLRequest requestWithURL:url];
requestData.HTTPMethod = HTTP_METHOD;
[requestData setValue:postLength forHTTPHeaderField:CONTENT_LENGTH];
[requestData setValue:REQUEST_DATA forHTTPHeaderField:CONTENT_TYPE];
requestData.HTTPBody = postData;
// Create a task.
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:requestData
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error)
{
if (!error)
{
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
[self handleAuth:[dictionary valueForKey:#"access_token"] withProfileInfo:dictionary];
} else{
NSLog(#"Error: %#", error.localizedDescription);
[self handleAuth:nil withProfileInfo:nil];
}
}];
// Start the task.
[task resume];
}
/*!
This method is used to get The profile information is the request is completed
#param NSSTring authetication key
#return void
*/
- (void) handleAuth: (NSString*)authToken withProfileInfo:(NSDictionary *)dictionary
{
NSLog(#"successfully logged in with Tocken == %#",authToken);
if (dictionary) {
_completionHandler(dictionary);
} else _failureHandler(dictionary);
// [self makePostRequest:authToken];
[self cancelLogin];
}
- (void)cancelLogin {
dispatch_async(dispatch_get_main_queue(), ^{
UIViewController *vc = [_controller.childViewControllers lastObject];
[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];
});
}
#pragma mark -WebView Delegate Method
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
return [self checkRequestForCallbackURL: request];
}
- (void) webViewDidStartLoad:(UIWebView *)webView
{
[self addIndicatorView];
}
- (void) webViewDidFinishLoad:(UIWebView *)webView
{
[_progressView removeFromSuperview];
_progressView = nil;
[_activitiyIndicator stopAnimating];
}
- (void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
[self webViewDidFinishLoad: webView];
}
#pragma mark - user Define Method
- (void)loginWithInstagramWithParsentViewController:(UIViewController *)controller completionHandler:(void(^)(NSDictionary *userProfileInformation))completionHanlder failureHandler:(void(^)(NSDictionary *errorDetail))failureHandler {
_controller = controller;
[controller addChildViewController:self];
self.view.frame = controller.view.frame;
[controller.view addSubview:self.view];
[self didMoveToParentViewController:controller];
_completionHandler = completionHanlder;
_failureHandler = failureHandler;
}
- (void)sharePhotoWithInstagaramWithImage:(UIImage *)image parsentViewcontroller:(UIViewController *)controller{
NSString *savePath = [NSHomeDirectory() stringByAppendingPathComponent:DOCUMENT_FILE_PATH];
if ([[NSFileManager defaultManager] fileExistsAtPath:savePath]) {
NSFileManager *fielManager = [NSFileManager defaultManager];
[fielManager removeItemAtPath:savePath error:nil];
}
BOOL save = [UIImagePNGRepresentation(image) writeToFile:savePath atomically:YES];
NSURL *instagramURL = [NSURL URLWithString:APP_URL];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL] && save) {
self.docFile = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
self.docFile.UTI = UTI_URL;
self.docFile.delegate = self;
[self.docFile presentOpenInMenuFromRect:CGRectZero inView:controller.view animated:YES];
} else {
NSLog(#"%#",MESSAGE);
}
}
#pragma mark - UIDocumentInteractionController delegateMethod
- (UIDocumentInteractionController *) setupControllerWithURL: (NSURL*) fileURL usingDelegate: (id <UIDocumentInteractionControllerDelegate>) interactionDelegate {
UIDocumentInteractionController *interactionController = [UIDocumentInteractionController interactionControllerWithURL: fileURL];
interactionController.delegate = interactionDelegate;
return interactionController;
}
#pragma mark - selector method
-(void)cancel:(UIButton *)button {
[self cancelLogin];
}
#end
Step 3:
Finally import "ConstantHandler.h" file in ViewController
Add the Login and share Button in ViewController and the this code
#import "ViewController.h"
#import "InstagramController.h"
#interface ViewController ()
#property(nonatomic,strong) InstagramController *instagramHandler ;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)loginWithInstagram:(id)sender {
_instagramHandler = [InstagramController new];
[_instagramHandler loginWithInstagramWithParsentViewController:self completionHandler:^(NSDictionary *userProfileInformation) {
NSLog(#"%#",userProfileInformation);
} failureHandler:^(NSDictionary *errorDetail) {
NSLog(#"%#",errorDetail);
}];
}
- (IBAction)shareWithInstagram:(id)sender {
_instagramHandler = [InstagramController new];
UIImage *image = [UIImage imageNamed:#"flower-197343_960_720.jpg"];
[_instagramHandler sharePhotoWithInstagaramWithImage:image parsentViewcontroller:self];
}
#end
Step 4.
Build and run the project
Delete NSHTTPCookieStorage and Use App Transport Security.
And add webView delegate before webView request. After use method, dealloc and set webView delegate to nil.

Which third party Imageview class should i used?

My requirement is :
Need to show image in imageview from server URL & save it for offline use also.
But There may be chances that same URL image can be updated.
I tried EGOImageview,AsyncImageview,FXImageview,Haneke thsese all classes i tried.
my first part of requirement is achieved. but not second..i.e if "xxxxx" link image is shown for first time....if on that same link image is upadated ,app shows old image only...
You can use the below class that will full fill your requirement
DImageView.h
#import <UIKit/UIKit.h>
#interface DImageView : UIImageView
#property (nonatomic, strong) UIActivityIndicatorView *activityView;
- (void)processImageDataWithURLString:(NSString *)urlString;
+ (UIImage *)getSavedImage :(NSString *)fileName;
+ (void)saveImageWithFolderName:(NSString *)folderName AndFileName:(NSString *)fileName AndImage:(NSData *) imageData;
DImageView.m
#import "DImageView.h"
#define IMAGES_FOLDER_NAME #"DImages"
#implementation DImageView
#pragma mark - App Life Cycle
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
}
return self;
}
- (void)dealloc
{
self.activityView = nil;
[super dealloc];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self initWithFrame:[self frame]];
}
return self;
}
#pragma mark - Download images
- (void)processImageDataWithURLString:(NSString *)urlString //andBlock:(void (^)(UIImage * img))processImage
{
#autoreleasepool
{
UIImage * saveImg = [DImageView getSavedImage:urlString];
if (saveImg)
{
dispatch_queue_t callerQueue = dispatch_get_main_queue();
dispatch_async(callerQueue, ^{
#autoreleasepool
{
[self setImage:saveImg];
}
});
}
else
{
[self showActivityIndicator];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
dispatch_queue_t callerQueue = dispatch_get_main_queue();
dispatch_queue_t downloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
__block NSError* error = nil;
dispatch_async(downloadQueue, ^{
NSData * imageData = [[[NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error] retain] autorelease];
if (error)
{
NSLog(#"DImg Error %#", [error debugDescription]);
}
else
{
dispatch_async(callerQueue, ^{
#autoreleasepool
{
UIImage *image = [UIImage imageWithData:imageData];
[self setImage:image];
[self hideActivityIndicator];
/* Below code is to save image*/
[DImageView saveImageWithFolderName:IMAGES_FOLDER_NAME AndFileName:urlString AndImage:imageData];
}
});
}
});
dispatch_release(downloadQueue);
}
}
}
#pragma mark - File Save methods
+ (void)saveImageWithFolderName:(NSString *)folderName AndFileName:(NSString *)fileName AndImage:(NSData *) imageData
{
#autoreleasepool
{
NSFileManager *fileManger = [NSFileManager defaultManager] ;
NSString *directoryPath = [NSString stringWithFormat:#"%#/%#",[DImageView applicationDocumentsDirectory],folderName] ;
if (![fileManger fileExistsAtPath:directoryPath])
{
NSError *error = nil;
[fileManger createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
}
fileName = [DImageView fileNameValidate:fileName];
NSString *filePath = [NSString stringWithFormat:#"%#/%#",directoryPath,fileName] ;
BOOL isSaved = [imageData writeToFile:filePath atomically:YES];
if (!isSaved)
{
NSLog(#" ** Img Not Saved");
}
}
}
+ (NSString *)applicationDocumentsDirectory
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
+ (UIImage *)getSavedImage :(NSString *)fileName
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
fileName = [DImageView fileNameValidate:fileName];
NSFileManager * fileManger = [NSFileManager defaultManager] ;
NSString * directoryPath = [NSString stringWithFormat:#"%#/%#",[DImageView applicationDocumentsDirectory],IMAGES_FOLDER_NAME] ;
NSString * filePath = [NSString stringWithFormat:#"%#/%#",directoryPath,fileName] ;
if ([fileManger fileExistsAtPath:directoryPath])
{
UIImage *image = [UIImage imageWithContentsOfFile:filePath] ;
if (image)
{
return image;
}
else
{
NSLog(#"** Img Not Found **");
return nil;
}
}
[pool release];
return nil;
}
+ (NSString*) fileNameValidate : (NSString*) name
{
name = [name stringByReplacingOccurrencesOfString:#"://" withString:#"##"];
name = [name stringByReplacingOccurrencesOfString:#"/" withString:#"#"];
name = [name stringByReplacingOccurrencesOfString:#"%20" withString:#""];
return name;
}
#pragma mark - Activity Methods
- (void) showActivityIndicator
{
self.activityView = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.activityView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
self.activityView.hidesWhenStopped = TRUE;
self.activityView.backgroundColor = [UIColor clearColor];
self.activityView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self addSubview:self.activityView];
[self.activityView startAnimating];
}
- (void) hideActivityIndicator
{
CAAnimation *animation = [NSClassFromString(#"CATransition") animation];
[animation setValue:#"kCATransitionFade" forKey:#"type"];
animation.duration = 0.4;;
[self.layer addAnimation:animation forKey:nil];
[self.activityView stopAnimating];
[self.activityView removeFromSuperview];
for (UIView * view in self.subviews)
{
if([view isKindOfClass:[UIActivityIndicatorView class]])
[view removeFromSuperview];
}
}
What will this class do ?
It will download images from server and save it to application document directory And get it from local if its available.
How to use that ?
Set DImageView class in your nib file for UIImageView.
Then you can simply use it as below in your .m file.
[imgViewName processImageDataWithURLString:imageURl];
You should take a look at SDWebImage. It's one of the most used UIImageView category right now and offers a lot of features, including caching.
Have a look at this part of the documentation to learn how to refresh your cache with it: Handle image refresh

Unable to call up my video files using AVFoundation

I'm having difficulty playing back video that I've recently recorded in a hybrid image/video camera akin to Snapchat (e.g. tap to take a photo, press and hold to record a video, playback on button release).
I'm currently saving the video file to NSFileManager. When I log it out I do verify that something is being saved but can't inspect the file because it has to be tested on the phone.
The file path when I log it out:
file:///var/mobile/Containers/Data/Application/7D86B14D-ACFF-4494-AD61-CBBD32DCA7A5/Documents/test.mov
When I go to load the asset from the file manager I log out an error that it can't open the files. I've only just started working with AVFoundation so not sure what some of the issues/considerations are when debugging. Any insight would be greatly appreciated, thank you!
Referenced tutorial
Referenced github repository
Referenced code:
PlayerView.h (reference)
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface PlayerView : UIView
#property (nonatomic) AVPlayer *player;
- (void)setPlayer:(AVPlayer *)player;
#end
PlayerView.m (reference)
#import "PlayerView.h"
#implementation PlayerView
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer *)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
#end
HybridCameraViewController.h (reference)
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "PlayerView.h"
#interface HybridCameraViewController : UIViewController
#property UIButton *button;
#property UIButton *saveButton;
#property UIImageView *previewView;
#define VIDEO_FILE #"test.mov"
#end
HybridCameraViewController.m (reference)
#import "HybridCameraViewController.h"
static const NSString *ItemStatusContext;
#class PlayerView;
#interface HybridCameraViewController () <AVCaptureFileOutputRecordingDelegate>
#end
#implementation HybridCameraViewController
AVCaptureSession *session;
AVCaptureStillImageOutput *imageOutput;
AVCaptureMovieFileOutput *movieOutput;
AVCaptureConnection *videoConnection;
AVPlayer *player;
AVPlayerItem *playerItem;
PlayerView *playerView;
- (void)viewDidLoad {
[super viewDidLoad];
[self testDevices];
self.view.backgroundColor = [UIColor blackColor];
//Image preview
self.previewView = [[UIImageView alloc]initWithFrame:self.view.frame];
self.previewView.backgroundColor = [UIColor whiteColor];
self.previewView.contentMode = UIViewContentModeScaleAspectFill;
self.previewView.hidden = YES;
[self.view addSubview:self.previewView];
//Playerback setup
playerView = [[PlayerView alloc]initWithFrame:self.view.frame];
playerView.backgroundColor = [UIColor redColor];
[self syncUI];
//Buttons
self.button = [self createButtonWithTitle:#"REC" chooseColor:[UIColor redColor]];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPressGesture:)];
[self.button addGestureRecognizer:longPressRecognizer];
[self.button addTarget:self action:#selector(captureImage) forControlEvents:UIControlEventTouchUpInside];
self.saveButton = [self createSaveButton];
[self.saveButton addTarget:self action:#selector(saveActions) forControlEvents:UIControlEventTouchUpInside];
}
- (void)viewWillAppear:(BOOL)animated {
//Tests
[self initializeAVItems];
NSLog(#"%#", videoConnection);
NSLog(#"%#", imageOutput.connections);
NSLog(#"%#", imageOutput.description.debugDescription);
}
#pragma mark - AV initialization
- (void)initializeAVItems {
//Start session, input
session = [AVCaptureSession new];
if ([session canSetSessionPreset:AVCaptureSessionPresetHigh]) {
session.sessionPreset = AVCaptureSessionPresetHigh;
}
AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&error];
if ([session canAddInput:deviceInput]) {
[session addInput:deviceInput];
} else {
NSLog(#"%#", error);
}
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:session];
[previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
//Layer preview
CALayer *viewLayer = [[self view] layer];
[viewLayer setMasksToBounds:YES];
CGRect frame = self.view.frame;
[previewLayer setFrame:frame];
[viewLayer insertSublayer:previewLayer atIndex:0];
//Image Output
imageOutput = [AVCaptureStillImageOutput new];
NSDictionary *imageOutputSettings = [[NSDictionary alloc]initWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
imageOutput.outputSettings = imageOutputSettings;
//Video Output
movieOutput = [AVCaptureMovieFileOutput new];
[session addOutput:movieOutput];
[session addOutput:imageOutput];
[session startRunning];
}
- (void)testDevices {
NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices) {
NSLog(#"Device name: %#", [device localizedName]);
if ([device hasMediaType:AVMediaTypeVideo]) {
if ([device position] == AVCaptureDevicePositionBack) {
NSLog(#"Device position : back");
}
else {
NSLog(#"Device position : front");
}
}
}
}
#pragma mark - Image capture
- (void)captureImage {
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in imageOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
videoConnection = connection;
break;
}
}
if (videoConnection) {
break;
}
}
NSLog(#"Requesting capture from: %#", imageOutput);
[imageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
self.previewView.image = image;
self.previewView.hidden = NO;
}
}];
[self saveButtonFlyIn:self.saveButton];
}
#pragma mark - Video capture
- (void)captureVideo {
NSLog(#"%#", movieOutput.connections);
[[NSFileManager defaultManager] removeItemAtURL:[self outputURL] error:nil];
videoConnection = [self connectionWithMediaType:AVMediaTypeVideo fromConnections:movieOutput.connections];
[movieOutput startRecordingToOutputFileURL:[self outputURL] recordingDelegate:self];
}
- (AVCaptureConnection *)connectionWithMediaType:(NSString *)mediaType fromConnections:(NSArray *)connections {
for (AVCaptureConnection *connection in connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:mediaType]) {
return connection;
}
}
}
return nil;
}
#pragma mark - Show Last Recording
- (void)presentRecording {
NSLog(#"unplaying");
NSLog(#"%#",[self outputURL]);
}
- (IBAction)loadAssetFromFile {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[self outputURL] options:nil];
NSString *tracksKey = #"tracks";
[asset loadValuesAsynchronouslyForKeys:#[tracksKey] completionHandler:^{
dispatch_async(dispatch_get_main_queue(),^{
NSError *error;
AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey
error:&error];
if (status == AVKeyValueStatusLoaded) {
playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem addObserver:self forKeyPath:#"status"
options:NSKeyValueObservingOptionInitial
context:&ItemStatusContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:playerItem];
player = [AVPlayer playerWithPlayerItem:playerItem];
[playerView setPlayer:player];
}
else {
NSLog(#"The asset's tracks were not loaded:\n%#", [error localizedDescription]);
}
});
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == &ItemStatusContext) {
dispatch_async(dispatch_get_main_queue(),^{
[self syncUI];
});
return;
}
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[player seekToTime:kCMTimeZero];
}
- (void)syncUI {
if ((player.currentItem != nil) &&
([player.currentItem status] == AVPlayerItemStatusReadyToPlay)) {
self.button.enabled = YES;
}
else {
self.button.enabled = NO;
}
}
#pragma mark - AVCaptureFileOutputRecordingDelegate
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error {
if (!error) {
NSLog(#"Success!!!!");
} else {
NSLog(#"Error: %#", [error localizedDescription]);
}
}
#pragma mark - Recoding Destination URL
- (NSURL *)outputURL {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:VIDEO_FILE];
return [NSURL fileURLWithPath:filePath];
}
#pragma mark - Buttons
- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
NSLog(#"Press");
self.button.backgroundColor = [UIColor greenColor];
[self captureVideo];
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
NSLog(#"Unpress");
self.button.backgroundColor = [UIColor redColor];
[movieOutput stopRecording];
[self performSelector:#selector(loadAssetFromFile)];
[player play];
}
}
- (UIButton *)createButtonWithTitle:(NSString *)title chooseColor:(UIColor *)color {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(self.view.center.x, self.view.frame.size.height - 100, 85, 85)];
button.layer.cornerRadius = button.bounds.size.width / 2;
button.backgroundColor = color;
button.tintColor = [UIColor whiteColor];
[self.view addSubview:button];
return button;
}
- (UIButton *)createSaveButton {
UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(self.view.frame.size.width, self.view.frame.size.height - 100, 85, 85)];
button.layer.cornerRadius = button.bounds.size.width / 2;
button.backgroundColor = [UIColor greenColor];
button.tintColor = [UIColor whiteColor];
button.userInteractionEnabled = YES;
[button setTitle:#"save" forState:UIControlStateNormal];
[self.view addSubview:button];
return button;
}
- (void)saveButtonFlyIn:(UIButton *)button {
CGRect movement = button.frame;
movement.origin.x = self.view.frame.size.width - 100;
[UIView animateWithDuration:0.2 animations:^{
button.frame = movement;
}];
}
- (void)saveButtonFlyOut:(UIButton *)button {
CGRect movement = button.frame;
movement.origin.x = self.view.frame.size.width;
[UIView animateWithDuration:0.2 animations:^{
button.frame = movement;
}];
}
#pragma mark - Save actions
- (void)saveActions {
[self saveButtonFlyOut:self.saveButton];
self.previewView.image = nil;
self.previewView.hidden = YES;
}
#end
The approach was overly complex. Where you went wrong was 1) not loading the url properly and 2) not adding the 'player layer' sublayer to the main view.
Here is an example of successful playback:
//TestURL
self.videoURL = [NSURL URLWithString:(NSString *)[self.selectedVideo objectForKey:#"source"]];
//Video
self.player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc]initWithAsset:[AVAsset assetWithURL:self.videoURL]]];
//Player layer
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
self.playerLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 3);
[self.view.layer addSublayer:self.playerLayer];
[self.player play];
All you would need to do is redirect the url to the one you just saved:
self.videoURL = [self outputURL];

download multiple images with ASIHttpRequest

I have a UITableView which contain several cell. When I click a cell, I push an other controller which shows the detail of this cell. In the detail I have a scrollView which contains several UIImageViews. I would like to download the images for these views from the web using ASIHTTPRequest, when the detail view is loaded.
You can override UIView. Each view has an ASIHTTPRequest. When each download has finished, you can use drawRect to draw the downloaded image.
this is a demo:
MyImageView.h
#import <UIKit/UIKit.h>
#import "ASIHTTPRequest.h"
#interface MyImageView : UIView <ASIHTTPRequestDelegate>
{
ASIHTTPRequest *httpRequest;
UIImage *image;
}
- (void)startRequest:(NSString *)_url;
#end
MyImageView.m
#import "MyImageView.h"
#implementation MyImageView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
// Drawing code
if (image != nil) {
[image drawInRect:self.bounds];
}
}
- (void)dealloc
{
[httpRequest clearDelegatesAndCancel];
[httpRequest release];
[image release];
[super dealloc];
}
-(void)startRequest:(NSString *)_url
{
if (httpRequest != nil) {
[httpRequest clearDelegatesAndCancel];
[httpRequest release];
}
httpRequest = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:_url]];
[httpRequest setTimeOutSeconds:30];
[httpRequest setDelegate:self];
[httpRequest startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
if ([request responseStatusCode] == 200) {
image = [[UIImage alloc] initWithData:[request responseData]];
[self setNeedsDisplay];
}
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(#"request failed");
}
Usage:
MyImageView *imageView = [[MyImageView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
[imageView startRequest:#"http://imageUrl"];
[self.view addSubview:imageView];
[imageView release];
Here's a class derived from UIImageView:
Header file, UIHTTPImageView.h:
#import "ASIHTTPRequest.h"
#interface UIHTTPImageView : UIImageView {
ASIHTTPRequest *request;
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
#end
and UIHTTPImageView.m:
#import "UIHTTPImageView.h"
#implementation UIHTTPImageView
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
[request setDelegate:nil];
[request cancel];
[request release];
request = [[ASIHTTPRequest requestWithURL:url] retain];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
if (placeholder)
self.image = placeholder;
[request setDelegate:self];
[request startAsynchronous];
}
- (void)dealloc {
[request setDelegate:nil];
[request cancel];
[request release];
[super dealloc];
}
- (void)requestFinished:(ASIHTTPRequest *)req
{
if (request.responseStatusCode != 200)
return;
self.image = [UIImage imageWithData:request.responseData];
}
#end
Note that there's no reporting of errors, you may want an requestFailed: method if you want to report a problem to the user.