Getting error using iCarousel - ios

This is for my iCarouselViewController.m
- (void)dealloc
{
//it's a good idea to set these to nil here to avoid
//sending messages to a deallocated viewcontroller
carousel1.delegate = nil;
carousel1.dataSource = nil;
carousel2.delegate = nil;
carousel2.dataSource = nil;
[carousel1 release];
[carousel2 release];
[items1 release];
[items2 release];
[super dealloc];
}
I am getting an error saying
'release' is unavailable: not available in automatic reference
counting mode ARC forbids explicit message send of 'release'
'release' is unavailable: not available in automatic reference
counting mode ARC forbids explicit message send of 'release'
'release' is unavailable: not available in automatic reference
counting mode ARC forbids explicit message send of 'release'
'release' is unavailable: not available in automatic reference
counting mode ARC forbids explicit message send of 'release'
ARC forbids explicit message send of 'dealloc'
and error in this code aswell
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;
//create new view if no view is available for recycling
if (view == nil)
{
view = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)] autorelease];
((UIImageView *)view).image = [UIImage imageNamed:#"page.png"];
view.contentMode = UIViewContentModeCenter;
label = [[[UILabel alloc] initWithFrame:view.bounds] autorelease];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = UITextAlignmentCenter;
label.font = [label.font fontWithSize:50];
[view addSubview:label];
}
else
{
label = [[view subviews] lastObject];
}
saying
'autorelease' is unavailable: not available in automatic reference
counting mode ARC forbids explicit message send of
'autorelease' 'autorelease' is unavailable: not available in
automatic reference counting mode ARC forbids explicit message
send of 'autorelease'
How can I clear this error.
Update
Thank you for the answer I just have 4 error saying use of undeclared identifier imageArray1. and I know that this is happening. I just don't get " I assume you are just using app's bundle and we have two arrays of NSString which refer to each image: imageArray1 and imageArray2." Below is one of my save code and creating directory for one of my directories. Note: I have only one NSMutableArray called allImagesArray which I have declared in the header file.
NSArray *directoryNames = [NSArray arrayWithObjects:#"Apple",nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
for (int i = 0; i < [directoryNames count] ; i++) {
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:[directoryNames objectAtIndex:i]];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:nil]; //Create folder
NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:#"Tops"]; NSData *imageData = UIImagePNGRepresentation(captureImage.image);
time_t unixtime = (time_t)[[NSDate date]timeIntervalSince1970];
NSString *timestamp = [NSString stringWithFormat:#"%ldTopsImage.PNG",unixtime];
NSString *filePath = [folderPath stringByAppendingPathComponent:timestamp];
[imageData writeToFile:filePath atomically:YES];
}
}
Update 4
ThiS?
- (void)viewDidLoad
{
[super viewDidLoad];
//configure carousel
imageArray1 = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *location=#"apple";
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSArray * directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
imageArray1 = directoryContent;
imageArray2 = [[NSMutableArray alloc] init];
NSString *location=#"green";
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
imageArray2 = directoryContent;

This is not an iCarousel issue. You are using statements like release, autorelease in your code. Remove it. In ARC you do not need to do memory management manually. That is why ARC is there.
UPDATE:
According to your comments, you are facing problems in displaying images for Mutiple Carousels.
I assume you are using two iCarousel objects. lets name them carousel1 and carousel2.
Also, it seems you are using sandbox for saving images. If this is the case, then you have to fetch images from your sandbox using NSFileManager. You need to keep looking at how to do that, but the code for iCarousel will remain more or less same in that case as well. Here, for simplicity case I assume you are just using app's bundle and we have two arrays of NSString which refer to each image: imageArray1 and imageArray2.
In viewDidLoad, set delegate and datasource objects of each carousel as self
carousel1.delegate = self;
carousel1.dataSource = self;
carousel2.delegate = self
carousel2.dataSource = self;
Implement datasource methods accordingly:
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
//return the total number of items in the carousel
if (carousel == carousel1)
{
return [imageArray1 count];
}
else
{
return [imageArray2 count];
}
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;
//create new view if no view is available for recycling
if (view == nil)
{
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)];
UIImage *image;
if (carousel == carousel1)
{
image = [UIImage imageWithContentsOfFile:[imageArray1 objectAtIndex:index]];
((UIImageView *)view).image = image;
}
else
{
image = [UIImage imageWithContentsOfFile:[imageArray2 objectAtIndex:index]];
((UIImageView *)view).image = image;
}
}
return view;
}

Well it is actually really easy, you have ticked the "Use Automatic Reference Counting" (ARC) box when creating your XCode project. To solve it just go to your target, (iCarousel I assume) and do this "buldsettings" ---> objective-c automaticrefcount :NO
The compiler should then work. What ARC does is do this stuff automatically for you, so you don't have to write auto release and release statements, but if you need them do what I suggested. Otherwise remove these release and autorelease statements.

That is because you're using ARC in your project and the code you showed here doesn't. To disable it in those classes add -fno-objc-arc. You can do so by going into the targets Build Phases tab. In Compile Sources group, double-click the file (class) and add the -fno-objc-arc flag.
Or you could just remove all release messages.

Related

Due to heavy images stored in document directory app is receiving memory warning. And app get crashed

I am working on a app which is highly dependent on saving images in document directory and retrieving it and displaying it on screen.
As soon as I display 5 -6 images in collection view the ap gets slowed up and suddenly receives memory warning and stopes functioning and app crashes.
I am using following code to display data
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
DocumentCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"DocumentCollectionViewCell"
forIndexPath:indexPath];
if(cell!=nil){
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"DocumentCollectionViewCell" forIndexPath:indexPath];
}
Documents *documents = [arrTableData objectAtIndex:indexPath.row];
cell.imgView.contentMode = UIViewContentModeScaleAspectFit;
cell.imgContentView.layer.cornerRadius = 4.0f;
cell.imgContentView.layer.masksToBounds = YES;
cell.imgContentView.layer.borderColor = [UIColor lightGrayColor].CGColor;
cell.imgContentView.layer.borderWidth = .4f;
[cell.btnLockOrUnlock addTarget:self action:#selector(lockAction:) forControlEvents:UIControlEventTouchUpInside];
cell.btnLockOrUnlock.tag = indexPath.row;
// set count
cell.lblCount.text =[NSString stringWithFormat:#"%# Page",documents.imageCount];
cell.imgView.layer.cornerRadius = 6.0f;
cell.imgView.layer.masksToBounds = YES;
newDocDate = documents.issueDate;
// set image
NSString * passcode = documents.passcode;
if(passcode.length>3){
cell.bluredView.hidden = NO;
[cell.btnLockOrUnlock setImage:[UIImage imageNamed:#"lockWhite"] forState:UIControlStateNormal];
}
else{
[cell.btnLockOrUnlock setImage:[UIImage imageNamed:#"unlockWhite"] forState:UIControlStateNormal];
cell.bluredView.hidden = YES;
}
[cell.btnSelect addTarget:self action:#selector(cellSelectAction:) forControlEvents:UIControlEventTouchUpInside];
cell.btnSelect.tag = indexPath.row;
NSString *title = documents.title;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Fetch path for document directory
NSString * docDirectoryPath = (NSMutableString *)[documentsDirectory stringByAppendingPathComponent:title];
//-----------------path of document ------------------
NSString *filePath = [docDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"image%d.png",0]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
int i =0;
while (!fileExists) {
filePath = [docDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"image%d.png",i]];
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
i++;
}
CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:filePath], NULL);
// Create thumbnail options
CFDictionaryRef options = (__bridge CFDictionaryRef) #{
(id) kCGImageSourceCreateThumbnailWithTransform : #YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : #YES,
(id) kCGImageSourceThumbnailMaxPixelSize : #(cell.imgView.frame.size.height)
};
// Generate the thumbnail
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
UIImage* uiImage = [[UIImage alloc] initWithCGImage:thumbnail]; //<--CRASH
cell.DocName.text = documents.docName;
//-----------------display image on cell------------------
cell.imgView.image = uiImage;
uiImage = nil;
uiImage = NULL;
documents = nil;
documents = nil;
title = nil;
thumbnail = nil;
src = nil;
options = nil;
filePath = nil;
paths = nil;
documentsDirectory = nil;
docDirectoryPath = nil;
return cell;
}
I am setting all the objects to nil.
I am using following code to save images
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Fetch path for document directory
folderName = (NSMutableString *)[documentsDirectory stringByAppendingPathComponent:folderName];
NSData *pngData = UIImagePNGRepresentation(arrImages[i]);
NSString *filePath = [folderName stringByAppendingPathComponent:[NSString stringWithFormat:#"image%d.png",i]]; //Add the file name
[pngData writeToFile:filePath atomically:YES]; //Write the file
I save the original image from camera without any compression or resizing in document directory.
I am unable to understand the problem please help.
Thanks in advance.
It seems to be due to memory leaks, check your app using instruments and lacks tool.
CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:filePath], NULL);
// Create thumbnail options
CFDictionaryRef options = (__bridge CFDictionaryRef) #{
(id) kCGImageSourceCreateThumbnailWithTransform : #YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : #YES,
(id) kCGImageSourceThumbnailMaxPixelSize : #(cell.imgView.frame.size.height)
};
// Generate the thumbnail
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
UIImage* uiImage = [[UIImage alloc] initWithCGImage:thumbnail]; //<--CRASH
CFFoundation objects follow similar memory management rules (If you create or copy you need to release it) of Obj-C before ARC (ARC doesn't manage core foundation objects).
In the code you have shown I see that you aren't releasing the CGImageRef and the CGImageSourceRef, this create two leaks and probably the crash.
Collection view cells are recycled thus the number of image opened in memory are basically the number of cells you are seeing on screen they should be the cause of your crash.

Memory continuously increase then app crash when i display image from document directory during scrolling UITableview

My Requirement is download all images in application memory and display it from local if its available.
Below is my code to access image from local and if its not available then it will download then display.
[cell.imgProfilePic processImageDataWithURLString:cData.PICTURE];
I have made custom UIImageView class
DImageView.h
#import <UIKit/UIKit.h>
#interface DImageView : UIImageView
#property (nonatomic, strong) UIActivityIndicatorView *activityView;
- (void)processImageDataWithURLString:(NSString *)urlString;
+ (UIImage *)getSavedImage :(NSString *)fileName;
#end
DImageView.m
#import "DImageView.h"
#define IMAGES_FOLDER_NAME #"DImages"
#implementation DImageView
- (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;
}
- (void)processImageDataWithURLString:(NSString *)urlString
{
#autoreleasepool
{
UIImage * saveImg =[DImageView getSavedImage:urlString];
if (saveImg)
{
#autoreleasepool
{
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, ^{
#autoreleasepool
{
NSData * imageData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];
if (!error)
{
dispatch_async(callerQueue, ^{
#autoreleasepool {
UIImage *image = [UIImage imageWithData:imageData];
[self setImage:image];
[self hideActivityIndicator];
[self saveImageWithFolderName:IMAGES_FOLDER_NAME AndFileName:urlString AndImage:imageData];
}
});
}
}
});
dispatch_release(downloadQueue);
}
}
}
- (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];
}
}
- (void)saveImageWithFolderName:(NSString *)folderName AndFileName:(NSString *)fileName AndImage:(NSData *) imageData
{
#autoreleasepool{
NSFileManager *fileManger = [[NSFileManager defaultManager] autorelease];
NSString *directoryPath = [[NSString stringWithFormat:#"%#/%#",[DImageView applicationDocumentsDirectory],folderName] autorelease];
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] autorelease];
BOOL isSaved = [imageData writeToFile:filePath atomically:YES];
if (!isSaved)DLog(#" ** 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] autorelease];
NSString * directoryPath = [[NSString stringWithFormat:#"%#/%#",[DImageView applicationDocumentsDirectory],IMAGES_FOLDER_NAME] autorelease];
NSString * filePath = [[NSString stringWithFormat:#"%#/%#",directoryPath,fileName] autorelease];
if ([fileManger fileExistsAtPath:directoryPath])
{
UIImage *image = [[[UIImage imageWithContentsOfFile:filePath] retain]autorelease];
if (image)
return image;
else
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;
}
#end
Everything is working fine with smooth scrolling as well as asyncImage download in background.
The issue is when i scroll UITableview application memory is continuously increase and after some time i got Receive memory waring 2/3 time then application crash.
When i use AsyncImageView class that time memory not increase and its working fine. But due to app requirement i saved all images to Document Directory and display from it if its available.
i have tried with #autoreleasepool and release some variable but not getting success.
I appreciated if any one have the solution to manage memory management.
**ARC is off in my application.**
It's possible that UIImagePNGRepresentation returns non-autoreleased object - you can try to release it and see if that results in a crash. Obviously you are not releasing something, but nothing other than the image representation appears obvious.
A few other comments:
run your app in Instruments, using the ObjectAlloc tool, and it should be immediately obvious what objects are not dealloced. If you don't know Instruments, well, its time now to learn it.
you can 'track' objects and get a message when they are dealloced using ObjectTracker - however it was designed for ARC so you may need to tweak it. If you use it you would see a message when each of your objects are dealloced
when the table view is done with a cell, there is a delegate method that you can receive that tells you so, and you can then nil (release) and objects the cell retains
your use of downloadQueue is bizarre - create it once in your instance as an ivar, use it as you need, and in dealloc release it
you hide the activity spinner on the main queue, but don't start it on the main queue
you command the activity view to remove itself from its superview, but then look for in in the subviews and try to remove it there:
[self.activityView removeFromSuperview];
for (UIView * view in self.subviews)
{
if([view isKindOfClass:[UIActivityIndicatorView class]])
[view removeFromSuperview];
}
In the end, Instruments is what you want. You can read up more about it here, or just google and you will surely find a slew of blogs to read.
Yes Finally i have resolved it.
The code which is in Question is working fine now. but Without release some objects and #autoreleasepool block which is in code, memory was increase continuously during scroll UITableView.
From the Instrument i found that memory increase in UILableView and UIImageView. I am using Custom UITableViewCell and in that file i havnt implement dealloc method. So When i have implement dealloc method in UITableViewCell .m file and release & nil all object.
After that memory not increase during scroll TableView and its Resolved the issue.
As per my Understanding there is an issue in your "getSavedImage" Method you have to manage memory Manually instead of 'autorelease' so as My suggestion is use
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath]
and also release it after use of it. means after '[self setImage:saveImg];'
[saveImg release]
instead of this.
[[UIImage imageWithContentsOfFile:filePath] retain];
'Don't Use Autorelease because it has staying in memory until pool not drain' and just because of this you got an memory issue.

Using iCarousel with images in folder

I have a folder (Documents/Images) which I use to store images downloaded from online, when the view with iCarsousel is launched infomation on which images to use is also sent so only certain images from the folder will be used.
However for some reason the below code does not seem to work and a blank view is shown and no error message given.
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
[carousel setType:iCarouselTypeCylinder];
[self getImages];
return [images count];
}
- (void)getImages{
images=[[NSMutableArray alloc]init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:databaseName];
db = [FMDatabase databaseWithPath:writableDBPath];
if (![db open]) {
return;
}
NSLog(#"getting images");
NSLog(_galleryid);
FMResultSet *result = [db executeQuery:#"SELECT * FROM mediaImages WHERE galleryID = ?;", _galleryid];
while ([result next]){
NSString *filename = [result stringForColumnIndex:1];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *fileManager = [NSFileManager defaultManager];
//configure carousel
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:#"Images"];
NSString *filepath = [fPath stringByAppendingString:#"/"];
filepath = [filepath stringByAppendingString:filename];
NSLog(filepath);
[images addObject:filepath];
}
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
view = [[UIView alloc] init];
view.contentMode = UIViewContentModeScaleAspectFit;
CGRect rec = view.frame;
if(UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone)
{
rec.size.width = 250;
rec.size.height = 250;
}
view.frame = rec;
UIImageView *iv;
if(UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone)
{
iv=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 200, 200)];
}
NSString *temp=[images objectAtIndex:index];
iv.image=[UIImage imageNamed:temp];
iv.contentMode = UIViewContentModeScaleAspectFit;
[view addSubview:iv];
return view;
}
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index
{
NSLog(#"Image is selected.");
}
- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
{
switch (option)
{
case iCarouselOptionWrap:
{
return YES;
}
default:
{
return value;
}
}
}
Is there anything visably wrong with the code, or perhaps a good way to debug what the issue is?
My guess is that the problem is here:
iv.image=[UIImage imageNamed:temp];
temp is a path to a file, not the name of an image, so this won't work. Perhaps you want to use + imageWithContentsOfFile: instead?
If that's not the only problem, I highly recommend trying to isolate it further yourself with logging or debugging; for example if you had logged the value of temp and the image you get back from imageNamed:, you probably would have seen this problem as well.

How can I update a UITableView from a dynamically downloaded text file?

I am filling a TableView from a text file. I want to enable the user to download an updated text file and replace the existing content of the TableView with the content of the downloaded file. I am able to download the file and replace the original file. If I close the application and open it again, it loads the updated file.
But the TableView doesn't change while the app is running. When I execute the method to load data from the file into the TableView, I can see, using NSLog, that the method is getting the original data from the file.
What am I doing incorrectly? How can I get the method to see the updated text file instead of the original text file?
Thanks.
#interface
#property (strong, nonatomic) NSArray *tableViewData;
#end
#implementation
/*
When user presses button, IBAction method
- downloads text file
- saves the downloaded file, replacing the original text file
- loads the text file into the TableView data (this is what doesn't work)
- sends a reload message to the TableView
*/
- (IBAction)buttonUpdateTextFile:(UIBarButtonItem *)sender
{
NSString *contentsOfTextFile = [self downloadTextFileFromURL:#"http://www.apple.com/index.html"];
[self saveContentsOfTextFile:contentsOfTextFile toFile:#"tableViewData.txt"];
[self loadDataFromFileWithFileName:#"tableViewData" fileExtension:#"txt"];
[self.tableView reloadData];
}
- (NSString *)downloadTextFileFromURL:(NSString *)textFileURLstring
{
NSURL *textFileURL = [NSURL URLWithString:textFileURLstring];
NSError *error = nil;
NSString *contentsOfTextFile = [NSString stringWithContentsOfURL:textFileURL encoding:NSUTF8StringEncoding error:&error];
return contentsOfTextFile;
}
- (void)saveContentsOfTextFile:(NSString *)contentsOfTextFile toFile:(NSString *)fileName
{
NSString *pathName = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *fileNameWithPath = [pathName stringByAppendingPathComponent:fileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:fileNameWithPath]) {
[[NSFileManager defaultManager] createFileAtPath:fileNameWithPath contents:nil attributes:nil];
[[contentsOfTextFile dataUsingEncoding:NSUTF8StringEncoding] writeToFile:fileNameWithPath atomically:NO];
}
- (void)loadDataFromFileWithFileName:(NSString *)fileName fileExtension:(NSString *)fileExtension
{
NSString *path = [[NSBundle mainBundle] pathForResource:fileName
ofType:fileExtension];
NSString *content = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:NULL];
NSString *remainingText = [content mutableCopy];
NSMutableArray *data = [[NSMutableArray alloc] init];
NSRange *substringRange;
while (![remainingText isEqualToString:#""]) {
substringRange = [remainingText rangeOfString:#"/n"];
if (substringRange.location == NSNotFound)
{
currentLine = remainingText;
remainingText = #"";
} else {
substringRange.length = substringRange.location;
substringRange.location = 0;
currentLine = [[remainingText substringWithRange:substringRange] mutableCopy];
// - strip line from remainingText
substringRange.location = substringRange.length + 1;
substringRange.length = remainingText.length - substringRange.length - 1;
remainingText = [[remainingText substringWithRange:substringRange] mutableCopy];
}
[data addObject:currentLine];
}
self.tableViewData = [data copy];
}
I think
self.tableViewData = [data copy];
may be the problem.
I would make data a "private" property of the class. Only init once and then manually add and remove objects to it. Don't use copy.

Save and Get image from plist

I am developing one app in that getting images from array and display vertically in ScrollView.
when user double tapped on particular image i want that exact image store into plist according to tag value of that image, and retrieve that image later on when require.
i tried this one
// Store Data into plist.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [NSString stringWithFormat:#"%#/myPlist.plist",
[paths objectAtIndex:0]];
// Place an image in a dictionary that will be stored as a plist
NSMutableDictionary * dictionary=[[NSMutableDictionary alloc]init];
[dictionary setObject:ImgView.tag forKey:#"image"];
NSLog(#"%#",dictionary);
// Write the dictionary to the filesystem as a plist
[NSKeyedArchiver archiveRootObject:dictionary toFile:path];
// For getting data from NSmutable array store it to the scrollview.
int m=0;
AppDelegate * delegate=(AppDelegate *)[[UIApplication sharedApplication]delegate];
delegate.front=TRUE;
delegate.back=FALSE;
UIScrollView *scrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];
[scrollView setPagingEnabled:YES];
[scrollView setShowsHorizontalScrollIndicator:NO];
FrontsCards=[[NSMutableArray alloc]initWithObjects:#"cloub1.png",#"cloub2.png",#"cloub3.png",#"cloub4.png",#"cloub5.png",#"cloub6.png",#"cloub7.png",#"cloub8.png",#"cloub9.png",#"cloub10.png",#"cloub11.png",#"cloub12.png",#"diamond1.png",#"diamond2.png",#"diamond3.png",#"diamond4.png",#"diamond5.png", nil];
for(m=0; m<[FrontsCards count];m++)
{
ImgView.alpha=1;
ImgView.tag=m;
int randIdx=arc4random()%[FrontsCards count];
NSString *imageName=[FrontsCards objectAtIndex:randIdx];
NSString *fullImageName=[NSString stringWithFormat:#"%#",imageName];
int padding=0;
CGRect imageViewFrame=CGRectMake(scrollView.frame.size.width*m+padding, scrollView.frame.origin.y, scrollView.frame.size.width-2*padding, scrollView.frame.size.height);
ImgView=[[UIImageView alloc]initWithFrame:imageViewFrame];
[ImgView setImage:[UIImage imageNamed:fullImageName]];
NSLog(#"%d",m);
// Place an image in a dictionary that will be stored as a plist
//[dictionary setObject:image forKey:#"image"];
// Write the dictionary to the filesystem as a plist
//[NSKeyedArchiver archiveRootObject:dictionary toFile:path];
[scrollView addSubview:ImgView];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapImgView:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
[self.ImgView addGestureRecognizer:doubleTap];
self.ImgView.userInteractionEnabled=YES;
}
CGSize scrollViewSize=CGSizeMake(scrollView.frame.size.width*[FrontsCards count], scrollView.frame.size.height);
[scrollView setContentSize:scrollViewSize];
[self.view addSubview:scrollView];
help me out this thanks in advance.
Define this MACRO Definition at the top of your .m file
#define LIB_DIR_PATH NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]
Use this function to Save Image to Plist with Image and Name
- (void)saveImage:(UIImage *)image WithName:(NSString *)imageName
{
// If File Exist then read it otherwise creat new
NSMutableDictionary *imageInfoDict;
if([[NSFileManager defaultManager] fileExistsAtPath:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]])
{
NSData *fileData = [NSData dataWithContentsOfFile:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]];
imageInfoDict = [NSMutableDictionary dictionaryWithDictionary:[NSKeyedUnarchiver unarchiveObjectWithData:fileData]];
}
else
imageInfoDict = [NSMutableDictionary dictionaryWithCapacity:0];
// Add Single Image to Dictionary
[imageInfoDict setValue:image forKey:imageName];
// Convert Main info Dictionary to `NSData` to Save on Disc
[NSKeyedArchiver archiveRootObject:imageInfoDict toFile:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]];
// To Read Stored Image Use Following Code
[self readImageFromPlistByKey:imageName];
}
This function returns image for respective name from Plist
-(UIImage *)readImageFromPlistByKey:(NSString *)keyName
{
// If File Exist then read it otherwise creat new
NSMutableDictionary *imageInfoDict;
if([[NSFileManager defaultManager] fileExistsAtPath:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]])
{
NSData *fileData = [NSData dataWithContentsOfFile:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]];
if([fileData length] > 0)
{
// Read Plist
imageInfoDict = [NSMutableDictionary dictionaryWithDictionary:[NSKeyedUnarchiver unarchiveObjectWithData:fileData]];
// Here is your Image
return imageInfoDict[keyName];
}
}
else
{
// Return Default Image if not Found
return [UIImage imageNamed:#"Default.png"];
}
}
If you are going to just store indexes, you need to have a master imageArray. I added insert/delete when user double taps the imageView twice.
- (void)doubleTapImgView:(UITapGestureRecognizer *)recognizer
{
UIImageView *imageView = (UIImageView *)recognizer.view;
[self insertorDeleteImageIndex:imageView.tag-1];
}
- (NSString *)plistFilePath{
NSString *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
return [documents stringByAppendingPathComponent:#"ImageIndexes.plist"];
}
- (void)insertorDeleteImageIndex:(NSInteger)index{
NSString *filePath = [self plistFilePath];
NSMutableArray *savedIndexes = [NSMutableArray arrayWithContentsOfFile:filePath];
if (!savedIndexes) {
savedIndexes = [NSMutableArray array];
}
if (![savedIndexes containsObject:#(index)]) {
[savedIndexes addObject:#(index)];
}else{
[savedIndexes removeObject:#(index)];
}
[savedIndexes writeToFile:filePath atomically:YES];
}
- (NSArray *)savedImageIndexes{
NSString *filePath = [self plistFilePath];
return [NSArray arrayWithContentsOfFile:filePath];
}
Source code
The code you post above can't be the real code as it wouldn't compile. That said, it shows a few errors:
You can't put basic numbers (NSInteger) into a dictionary, it needs to be boxed in an NSNumber.
You're setting the tag of the image before you create the instance of the image view (so either it will do nothing or set the wrong tag).
For saving the image, if you do want to save the image instead of the tag, you need to save it as data. You can store an image inside a dictionary no problem, but if you then want to store your dictionary as a plist you need to convert the image to NSData. You can get the image data using:
UIImageJPEGRepresentation(imageToSave, 0.8)

Resources