xcode async tableview load image move listview images gone - objective-c

We want to download image and data async to our tableview and we want to cache it not to download it again. We found a code for caching.Here is our code. It works asynchronously but when we scroll table view, images gone and come back(we cant see image for a while). Sometimes wrong image come to wrong cell. What might be the reasons and how can we solve it ?
NSURL *imageURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#",[userInfo[0] serverUrl],[estateList[indexPath.row] imgUrl]]];
NSString *key = [imageURL absoluteString];
NSData *data = [FTWCache objectForKey:key];
if (data) {
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
} else {
cell.imageView.image = [UIImage imageNamed:#"yukleniyor"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:imageURL];
[FTWCache setObject:data forKey:key];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
});
});
}
Here is that code we found for caching
Header is like this
#import <Foundation/Foundation.h>
#interface FTWCache : NSObject
+ (void) resetCache;
+ (void) setObject:(NSData*)data forKey:(NSString*)key;
+ (id) objectForKey:(NSString*)key;
#end
And .m file is like this
#import "FTWCache.h"
static NSTimeInterval cacheTime = (double)604800;
#implementation FTWCache
+ (void) resetCache {
[[NSFileManager defaultManager] removeItemAtPath:[FTWCache cacheDirectory] error:nil];
}
+ (NSString*) cacheDirectory {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:#"FTWCaches"];
return cacheDirectory;
}
+ (NSData*) objectForKey:(NSString*)key {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *filename = [self.cacheDirectory stringByAppendingPathComponent:key];
if ([fileManager fileExistsAtPath:filename])
{
NSDate *modificationDate = [[fileManager attributesOfItemAtPath:filename error:nil] objectForKey:NSFileModificationDate];
if ([modificationDate timeIntervalSinceNow] > cacheTime) {
[fileManager removeItemAtPath:filename error:nil];
} else {
NSData *data = [NSData dataWithContentsOfFile:filename];
return data;
}
}
return nil;
}
+ (void) setObject:(NSData*)data forKey:(NSString*)key {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *filename = [self.cacheDirectory stringByAppendingPathComponent:key];
BOOL isDir = YES;
if (![fileManager fileExistsAtPath:self.cacheDirectory isDirectory:&isDir]) {
[fileManager createDirectoryAtPath:self.cacheDirectory withIntermediateDirectories:NO attributes:nil error:nil];
}
NSError *error;
#try {
[data writeToFile:filename options:NSDataWritingAtomic error:&error];
}
#catch (NSException * e) {
//TODO: error handling maybe
}
}
#end

Sorry for my previous answer.
I will try to explain the problem:
We have TableView total number of rows and rows of the reserved area of the display. As a result, images with asynchronous load can not be loaded into their cells (because it is inserted as it downloads from the memory).
The solution is very simple, you must assign each ImageView with key and this key associated with the cells of the table.
I resolve this problem by using code from this git (https://gist.github.com/khanlou/4998479)
There are two files on it
UIImageView+Network.h
UIImageView+Network.m
You must include they in your project, or create own class and paste to it.
This class uses FTW/FTWCache from (https://github.com/FTW/FTWCache/tree/master/FTWCache). So you must add to your project this one too.
After that you must to import
#import "UIImageView+Network.h"
in file which will use asynchronous image loading and caching.
And short part of code with TableView using
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"MyTableCell";
MyTableCell *cell = (MyTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// construction to call method
// [cell.imageView loadImageFromURL:(NSURL*)url placeholderImage:(UIImage*)placeholder cachingKey:(NSString*)key ];
[cell.thumbnailImageView loadImageFromURL:(NSURL*)url placeholderImage:[UIImage imageNamed:#"no_foto.gif"] cachingKey:(NSString*)[NSString stringWithFormat:#"%d",indexPath.row]];
return cell;
}

Related

Open pdf files in webview according to tableview row selected

I retrieve all data from an sqlite database. I can get them to populate the table view. When I click on a row the webview opens but doesn't show the pdf file.The view is just plain white background. no crash, no errors, simply doesn't show anything.
This is my code. Not sure what I'm doing wrong.
#import "PdfTableVC.h"
#import "PdfVC.h"
#interface PdfTableVC ()
#end
#implementation PdfTableVC {
NSMutableArray *listOfPdf;
}
#synthesize pdfTable;
- (void)viewDidLoad {
[super viewDidLoad];
[self initDatabase];
[self getPoints];
}
#pragma mark - View lifecycle
-(void)initDatabase{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"my.db"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success)
{
return;
}
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"my.db"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
-(void)getPoints{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"my.db"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
const char *sql = "SELECT * FROM file_geositi";
sqlite3_stmt *searchStatement;
if (sqlite3_prepare_v2(database, sql, -1, &searchStatement, NULL) == SQLITE_OK)
{
listOfPdf = [[NSMutableArray alloc] init];
while (sqlite3_step(searchStatement) == SQLITE_ROW)
{
NSString *pdf = [NSString stringWithUTF8String:(char *)sqlite3_column_text(searchStatement,0)];
[listOfPdf addObject:pdf];
}
}
sqlite3_finalize(searchStatement);
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [listOfPdf count];
}
//Table View cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier =#"pdfCell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if(cell == nil){
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [listOfPdf objectAtIndex:indexPath.row];
return cell;
}
//Detail View items
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showPDF"]) {
NSIndexPath *indexPath = [self.pdfTable indexPathForSelectedRow];
PdfVC *destViewController = (PdfVC *)segue.destinationViewController;
destViewController.dataPdf= [listOfPdf objectAtIndex:indexPath.row];
}
}
PdfVC.h
#import <UIKit/UIKit.h>
#interface PdfVC : UIViewController
//UIWebView
#property (nonatomic, strong) IBOutlet UIWebView *webView;
#property (nonatomic, strong) NSString *dataPdf;
#end
PdfVC.m
#import "PdfVC.h"
#interface PdfVC ()
#end
#implementation PdfVC
#synthesize webView;
#synthesize dataPdf;
- (void)viewDidLoad {
[super viewDidLoad];
//No sure about this and in fact makes the app crash
NSString *pdfName = [NSString stringWithFormat:#"%#", [self.dataPdf description]];
NSString *path = [[NSBundle mainBundle] pathForResource:pdfName ofType:#"pdf"];
NSURL *url = [NSURL fileURLWithPath:path];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
}
#end
Any help? Thanx
Ok, so after having a look at your code, I found the following things:
You are storing your PDF names in a SQLite database (I don't really
understand the purpose of doing this, but let's say you need to) with
the names 'pdf_1', 'pdf_2' and 'pdf_3', but these are not matching
with your actual file names of PDF's in your supporting files folder
which are 'PDF 1' etc.
Secondly, you are passing just the name of the PDF extracted from the database onto the PdfVC, which actually does nothing, you have to actually load the PDF into the webView here, so it actually does nothing.
Solution:
Rename your PDF's from 'PDF 1' etc to 'pdf_1' etc. so that they match with the actual names stored in your database, or rename the entries in your database to match the file names of the PDF.
Secondly, place the following code into your viewDidLoad method of the PdfVC view controller:
NSString *path = [[NSBundle mainBundle] pathForResource:dataPdf ofType:#"pdf"];
NSURL *targetURL = [NSURL fileURLWithPath:path];
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[webView loadRequest:request];
All this code is doing is that it picks up the right PDF from the resources using the name stored in the property dataPdf (passed via the segue) and then loads that PDF into the webView.
I would recommend you to re-consider the purpose of an actual database here, since all you are doing is storing the names of the PDF's into the table. It may not be required at all, and you could maintain an NSArray of the PDF names in your PDFTableVC view controller.
Hope this answers your question.

UIImage gives nil

I'm quite new to iOS development. My app gets a file over a network, writes it as image.png and later on reads and displays the image. However, the display part is not working as my UIImage object is always set to nil (on the iOS simulator). I've tried implementing other answers from stackoverflow, but no luck.
Here's my code to save the file:
//inside utility class for model
NSFileHandle * handle = nil;
//For first packet of new file request
if(CountFileParts == 1)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"image.png"];
NSLog(#"%#",appFile);
handle = [NSFileHandle fileHandleForWritingAtPath:appFile];
if(handle == nil)
{
[[NSFileManager defaultManager] createFileAtPath:appFile contents:nil attributes:nil];
handle = [NSFileHandle fileHandleForWritingAtPath:appFile];
}
}
//For other incoming packets of the same request
else
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"image.png"];
//NSLog(#"%#",appFile);
handle = [NSFileHandle fileHandleForUpdatingAtPath:appFile];
[handle seekToEndOfFile];
//NSLog(#"Writing continue in new file");
}
if(handle == nil)
NSLog(#"handle nil");
NSData * data = [str dataUsingEncoding:NSUTF8StringEncoding];
[handle writeData:data];
[handle closeFile];
if(index != -1 && index!= NSNotFound)
{
NSLog(#"Inside Bool");
self.isPlotReady = YES;//kvo in view-controller as shown below
self.isPlotReady = NO;
}
Here's my code to load the image file:
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#"isPlotReady"])
{
self.isReady = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
[self updateUI];
}
}
-(void) updateUI
{
if(self.isReady)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[self lsOwnDirectory:documentsDirectory];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"image.png"];
//NSLog(#"%#",appFile);
UIImage *img = [UIImage imageWithContentsOfFile:appFile];
if(img == nil)
NSLog(#"Couldn't find image");
else
{
UIImageView *imageView = [[UIImageView alloc] initWithImage:img] ;
[self.view addSubview:imageView];
}
}
}
//Prints Directory contents of input directory
- (void) lsOwnDirectory:(NSString *) currentpath {
NSError * error = [[NSError alloc] init];
NSFileManager *filemgr;
filemgr = [[NSFileManager alloc] init];
//currentpath = [filemgr currentDirectoryPath];
NSLog(#"Current Directory Path : %#",currentpath);
NSArray * files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:currentpath error: &error];
for(NSString * file in files){
NSLog(#"%#", file);
}
}
It alway's prints "Couldn't find image" corresponding to the if statement, but I've seen the file is still there (lsOwnDirectory prints directory contents). Maybe I'm doing something basic wrong here. Thanks in advance.

Loading Images from JSON to Xcode

I having a bit difficulty loading images from a json file into UIImage - Table Cells in Xcode. I tried to load the images from the server into a NSArray then populating the table view UIImage cells. Is there something that I am missing here?
Image are located on a SQL server.
Thanks for the help.
Here is the server output from the PHP into Xcode. (cover_image)
(
"13497074790148.jpeg",
"13494650900147.png",
"13494606630147.png",
"13494605220147.jpeg",
"13494602920147.jpeg",
"13494601850147.jpeg",
"13491916300147.jpeg"
)
Here is the code in Xcode
NSArray *itemsimages = [[NSArray alloc]initWithArray:[results valueForKeyPath:#"cover_image"]];
self.itemImages = itemsimages;
Here is the code in table cells
UIImage *imageitm = [UIImage imageNamed: [self.itemImages objectAtIndex: [indexPath row]]];
cell.itmImage.image = imageitm;
return cell;
You don't have those images stored locally so it doesn't have any images to display. I suggest using SDWebImage to provide asyncronous image loading from remote location + caching mechanism.
-(void) viewDidLoad
{
NSURL *url = [NSURL URLWithString:#"YOUR URL"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSMutableArray *img = [[NSMutableArray alloc]init];
NSArray *websiteDetails = (NSArray *) [json objectForKey:#"logos"];
for(int count=0; count<[websiteDetails count]; count++)
{
NSDictionary *websiteInfo = (NSDictionary *) [websiteDetails objectAtIndex:count];
imagefile = (NSString *) [websiteInfo objectForKey:#"image_file"];
if([imagefile length]>0)
{
NSLog(#"Imagefile URL is: %#",imagefile);
[img addObject:imagefile];
}
}
//NSarray listofURL
listofURL = img;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
NSURL *url = [NSURL URLWithString:[listofURL objectAtIndex:indexPath.row]];
NSData *image = [[NSData alloc] initWithContentsOfURL:url];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:image];
});
});
}
return cell;
}
You need to have a proper url in json response or you can store the common part of the url in the code itself and append it later with the image name returned from server.
I did as follows in the same condition
__autoreleasing NSError* error = nil;
id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSDictionary *dict = ((NSDictionary *) result)[#"result"];
NSString *url = dict[#"imageURL"];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
[_buttonImageView setImage:image forState:UIControlStateNormal];
where data is the response returned from server.

how do I use a file from my apps documents directory with "didSelectRowAtIndexPath"?

I have a bunch of mp3's displayed in a UITableView which are located in my apps documents directory.
When I select the row, I want the file to load into AVAudioPlayer and Play.
Can anyone suggest how I would go about doing this?
Any help would be greatly appreciated.
Thanks!
I finally figured this out:
NSArray *songs;
AVAudioPlayer *player;
- (void)viewDidLoad
{
[self.player prepareToPlay];
// Point to Document directory
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSError *error = nil;
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
if (array == nil) {
// Handle the error
}
self.songs = array;
//[array release];
//self.title = #"Song List";
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *applicationDocumentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [applicationDocumentsDirectory stringByAppendingPathComponent: [songs objectAtIndex:indexPath.row]];
NSURL *url = [NSURL fileURLWithPath:filePath];
player = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
[player play];
}

Xcode Saving NSMutableArray of String & BOOL values

I've got a little problem with an Application i am designing at the min but i'm a complete beginner, first of all a little information on what i am trying to accomplish.
I have a plist which populates an NSMutableArray which contains many values each one has a string and a BOOL inside, i can make the program save a copy of the file upon opening the app and load the data into the tableview along with an accessoryview of a checkmark.
now the checkmark works ok and you can select different items and the checkmark only appears on those items none of the others and if you inspect the log the details for each of the items check BOOL is changed but when i come to save a second time the checkmark state is not persisted for when i open the application a second time it just saves it as a 0 everytime.
here is some of my code, any help would be appreciated.
Thanks
Brad
- (void)viewDidLoad {
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"CustomChecklist.plist"];
success = [fileManager fileExistsAtPath:filePath];
NSLog(#"STATUS OF SUCCESS %d",success);
if (!success) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"OriginalChecklist" ofType:#"plist"];
success = [fileManager copyItemAtPath:path toPath:filePath error:NULL];
self.dataArray = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"IF STATEMENT CREATING THE FILE");
}else {
self.dataArray = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"IF STATEMENT READING THE FILE");
}
NSLog(#"location information %#", filePath);
[super viewDidLoad];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCustomCellID = #"MyCellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCustomCellID];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCustomCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
NSMutableDictionary *item = [dataArray objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:#"text"];
[item setObject:cell forKey:#"cell"];
BOOL checked = [[item objectForKey:#"checked"] boolValue];
UIImage *image = (checked) ? [UIImage imageNamed:#"checked.png"] : [UIImage imageNamed:#"unchecked.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
button.frame = frame; // match the button's size with the image size
[button setBackgroundImage:image forState:UIControlStateNormal];
// set the button's target to this table view controller so we can interpret touch events and map that to a NSIndexSet
[button addTarget:self action:#selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
cell.accessoryView = button;
return cell;
}
- (void)viewWillDisappear:(BOOL)animated
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savePath = [documentsDirectory stringByAppendingPathComponent:#"CustomChecklist.plist"];
NSLog(#"View Will Disappear SAVE location information %#", savePath);
[dataArray writeToFile:savePath atomically:YES];
}
BOOL is not an object, it is a primitive type. Therefore, it cannot be saved (properly) in an array or dictionary. You need to use the NSNumber class to wrap it:
[NSNumber numberWithBool:checked] //this should be added to the dictionary
I am writing this from my phone, so I can't really see all of your code. But I just wanted to say that what you are trying to achieve can probably be solved by using NSUserdefaults instead of saving a file. Have you looked into that?
Oh, and just like Evan said, bool isn't an object. Only objects can be stored in an array.
Is there a reason you are adding the cell to your dictionary? A UITableViewCell is not a property list compatible object, so it could keep your array from saving properly.