I am building an app that will allow users to download and read issues of a journal. I am using the Download Manager framework created by Robert Ryan and I modified the test project that came with the framework to have it work in my project (there are no issues with the framework). On each row in the table there is a Issue Cover Image (UIImageView), Download/Read label (UILabel), Issue Date label (UILabel), and a progress bar (UIProgressView) all are properties of a UITableViewCell. When a user taps the row, it initiates the download process of the issue which is reflected in the progress bar; after the download completes, the progress bar becomes hidden and the Download title of the label changes to Read, and when the user taps the row again to read the downloaded journal it opens a PDF viewer in a viewcontroller. I haven't added the Read functionality as yet. All this works fine except as a test I have 2 issues of the journal in the table each in a row with its ``. When I tap the first row, the progress bar reflects the download progress and it works fine. However, when I tap the second row, the download progress is reflected in the progress bar of the first row not the second row as expected (the progress bar remains static). It does download the second journal and everything works fine. It's just this unexpected behavior where the download progress of the second row is reflected in the progress bar in the first row. I still have to streamline the code and clean it up but the relevant code sections are below:
// optional method to indicate progress of individual download
//
// In this view controller, I'll update progress indicator for the download.
- (void)downloadManager:(DownloadManager *)downloadManager downloadDidReceiveData: (Download *)download;
{
for (NSInteger row = 0; row < [downloadManager.downloads count]; row++)
{
if (download == downloadManager.downloads[row])
{
[self updateProgressViewForIndexPath:[NSIndexPath indexPathForRow:row inSection:0] download:download];
break;
}
}
}
#pragma mark - Table View delegate and data source methods
// our table view will simply display a list of files being downloaded
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return[jitsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DownloadCell";
DownloadCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[DownloadCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
jits * jitsInstance = nil;
jitsInstance = [jitsArray objectAtIndex:indexPath.row];
cell.issue.text = jitsInstance.issue;
NSString * myCoverURL = [NSString stringWithFormat:#"%#", jitsInstance.coverimage];
UIImage* myImage = [UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString: myCoverURL]]];
cell.coverimage.image = myImage;
[cell.progressView setProgress:0];
NSString * myURL = [NSString stringWithFormat:#"%#", jitsInstance.url];
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *downloadFolder = [documentsPath stringByAppendingPathComponent:#"downloads"];
NSString * fileName = [[NSString alloc]initWithFormat:#"%#", [myURL lastPathComponent]];
NSString* foofile = [downloadFolder stringByAppendingPathComponent:fileName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:foofile];
NSLog(#"Search file path: %#", foofile);
if (!fileExists) {
[cell.downloadButton setTitle:#"Download" forState:normal];
[cell.progressView setHidden:NO];
NSLog(#"File does not exist!");
}
else if (fileExists){
NSLog(#"File exist!");
[cell.downloadButton setTitle:#"Read" forState:normal];
[cell.progressView setHidden:YES];
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *downloadFolder = [documentsPath stringByAppendingPathComponent:#"downloads"];
jits * jitsInstance = nil;
jitsInstance = [jitsArray objectAtIndex:indexPath.row];
NSString * myURL = [NSString stringWithFormat:#"%#", jitsInstance.url];
self.downloadManager = [[DownloadManager alloc] initWithDelegate:self];
self.downloadManager.maxConcurrentDownloads = 4;
NSString *downloadFilename = [downloadFolder stringByAppendingPathComponent:[myURL lastPathComponent]];
NSURL *url = [NSURL URLWithString:myURL];
[self.downloadManager addDownloadWithFilename:downloadFilename URL:url];
self.cancelButton.enabled = YES;
self.startDate = [NSDate date];
[self.downloadManager start];
}
#pragma mark - Table view utility methods
- (void)updateProgressViewForIndexPath:(NSIndexPath *)indexPath download:(Download *)download
{
DownloadCell *cell = (DownloadCell *)[self.tableView cellForRowAtIndexPath: [NSIndexPath indexPathForRow:indexPath.row inSection:0]];
// if the cell is not visible, we can return
if (!cell)
return;
if (download.expectedContentLength >= 0)
{
// if the server was able to tell us the length of the file, then update progress view appropriately
// to reflect what % of the file has been downloaded
cell.progressView.progress = (double) download.progressContentLength / (double) download.expectedContentLength;
}
else
{
// if the server was unable to tell us the length of the file, we'll change the progress view, but
// it will just spin around and around, not really telling us the progress of the complete download,
// but at least we get some progress update as bytes are downloaded.
//
// This progress view will just be what % of the current megabyte has been downloaded
cell.progressView.progress = (double) (download.progressContentLength % 1000000L) / 1000000.0;
}
}
I think your issue may lie in the following code:
for (NSInteger row = 0; row < [downloadManager.downloads count]; row++)
{
if (download == downloadManager.downloads[row])
{
[self updateProgressViewForIndexPath:[NSIndexPath indexPathForRow:row inSection:0] download:download];
break;
}
}
What this seems like it's essentially doing is finding the first cell in the downloads array, calling updateProgressViewForIndexPath on that first cell it finds, then stopping. There are a number of ways to fix this issue, but the first that comes to mind is once you tell yourself to update the cell at that index path when the if-statement evaluates to true, remove that item from the downloadManager's downloads array, so next time through it won't be there. Give that a try and let me know if that works..
Also, on a side note... I would think that you don't want to do the following two lines every time a row is selected:
self.downloadManager = [[DownloadManager alloc] initWithDelegate:self];
self.downloadManager.maxConcurrentDownloads = 4;
It would seem to me that is something you'd want to do perhaps in your init method of your tableView so it only occurs once, rather than every time the user taps a row. Perhaps you're attempting to create and set as a property a new download manager every time? That sort of seems unorthodox to me. If I had access to the project I think I might be better help debugging. Any chance you'd want to share the project if my response didn't help?
Related
One of the developer is implementing a phonegap project supporting cross platform(ios & android). Things are going weird whenever there ain't any plugin available for ios. I am exhausted with a requirement that has come up, we save pdf files generated in app to documents directory. In android, when choose file is clicked, a file explorer opens up for choosing file from desired location. I had to write a custom plugin for ios, as things are sandboxed in ios...only way a user can choose is files saved in app documents directory. I am trying to fetch the documents(pdf files) from the directory and populate them in table view. Actually I followed this link on Git repository for saving pdf file. The link follows a traditional way of saving in documents directory, but I don't see the file getting saved with extension '.pdf',surprisingly the pdf files are visible with extensions in temp directory embedded in unique folders. Moreover it wipes off the file from documents directory after the user opens the file in appropriate reader s/w.
When I tried to retrieve the files from temporary directory using the below code:
NSString *docPath = NSTemporaryDirectory();
self.filesArray = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:docPath error:NULL] mutableCopy];
for (NSString *file in _filesArray) {
NSString *actualFile = [NSString stringWithFormat:#"/%#",file];
docPath = [docPath stringByAppendingString:actualFile];
}
[_filePathsArray addObject:docPath];
As expected its returning only directories (I mean the folders).... and the pdf files are hidden in those subdirectories. I need to extract these files and show up in table view. Here is the sample code of the plugin I created:
- (NSMutableDictionary *)callbackIds {
if (_callbackIds == nil) {
_callbackIds = [[NSMutableDictionary alloc] init];
}
return _callbackIds;
}
-(void)open:(CDVInvokedUrlCommand *)command
{
[self.callbackIds setValue:command.callbackId forKey:#"open"];
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat height = [UIScreen mainScreen].bounds.size.height;
self.listOfFiles = [[UITableView alloc] initWithFrame:CGRectMake(230, 210, width/1.8, height/1.8) style:UITableViewStylePlain];
self.listOfFiles.dataSource = self;
self.listOfFiles.delegate = self;
self.listOfFiles.scrollEnabled = YES;
self.listOfFiles.rowHeight = tableHeight;
[self.viewController.view addSubview:_listOfFiles];
}
#pragma mark - TableViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
self.filePathsArray = [[NSMutableArray alloc]init];
self.filesArray = [[NSMutableArray alloc]init];
NSString *docPath = NSTemporaryDirectory();
self.filesArray = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:docPath error:NULL] mutableCopy];
for (NSString *file in _filesArray) {
NSString *actualFile = [NSString stringWithFormat:#"/%#",file];
docPath = [docPath stringByAppendingString:actualFile];
}
[_filePathsArray addObject:docPath];
return _filesArray.count;
}
#pragma mark - TableViewDelegate
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [NSString stringWithFormat:#"S%1dR%1d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [_filesArray objectAtIndex:indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Send result back to the javascript plugin
NSString *resultingPath = [self.filePathsArray objectAtIndex:indexPath.row];
[self returnPluginResult:resultingPath toCallback:#"open" withError:NO];
[_listOfFiles removeFromSuperview];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return #"Choose File";
}
#pragma mark - Plugin Callback
- (void)returnPluginResult:(NSString *)result toCallback:(NSString *)callback withError:(BOOL)error
{
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:result];
if (!error) {
[self writeJavascript:[pluginResult toSuccessCallbackString:[self.callbackIds valueForKey:callback]]];
}
else {
[self writeJavascript:[pluginResult toErrorCallbackString:[self.callbackIds valueForKey:callback]]];
}
}
Someone please guide me with a solution or subsequent helpful links. I tried every samples of code over humongous googling and nothing seemed to solve my problem.
Thanks everyone in advance :)
The link I followed for storing the pdf is actually removing the file from documents directory upon storage. Truncating the removal code solved the issue for me. Hope this helps someone, thanks :)
My Code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
NSString *homeDir = NSHomeDirectory();
if(indexPath.section == 0){
homeDir = [NSString stringWithFormat: #"%#/%#", homeDir, #"Documents"];
}else{
homeDir = [NSString stringWithFormat: #"%#/%#", homeDir, #"Documents/Archived"];
}
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:homeDir error:nil];
dirContents = [[dirContents reverseObjectEnumerator] allObjects];
cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
}
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = 1.3; //seconds
[cell addGestureRecognizer:lpgr];
int i = 0;
NSString *filename;
if([dirContents count] > 0){
filename = [[dirContents objectAtIndex:indexPath.row] lastPathComponent];
while(![[filename substringFromIndex: [filename length] - 3] isEqualToString:#"pdf"]){
i ++;
filename = [[dirContents objectAtIndex:indexPath.row + i] lastPathComponent];
}
}else{
filename = #" ";
}
cell.textLabel.text = filename;
return cell;
}
The issue I am having is that, on occasion, only the first cell returns repeatedly. If an array reads "Red, Blue, Green, Yellow" it appears as:
Red
Red
Red
Red
This happens only on occasion. Sometimes when refreshing the table, it displays correctly, only to later revert back to the incorrect display. What am I doing wrong?
I posted a comment above, but I'm going to try for an answer because I suspect this will fix your problem, if not just to show what is actually happening here.
A UITableView should have a datasource. In your original code you are creating this datasource (the array dirContents) every time the table view creates a new cell. It looks like your datasource is static, so you can create this outside of the UITableViewDataSource delegate method, and place it somewhere such as the viewDidLoad method. It's only an assumption of mine that this array is not being created in time to access the elements for your cell.
You can then confirm that your cell is grabbing the correct index from this array with a NSLog right before cell.textLabel.text = filename;.
Hopefully this helps you with your problem.
Marcel, you have to re-architect your code, you have put the code which gets file names from the directory in viewDidLoad and save these names in a global array and then in cellForRowAtIndex make use of this array.
For refreshing of tableView on each download, just reload uitableview as file is completely donwloaded. Hope it Helps!
I am using a UIViewController (1st) that displays a list of comics where a user can select a comic to download.
The download takes place in another UIViewController (2nd) that has a tableView (with a custom download cell).
I have an NSMutableArray declared in the 2nd viewController with all the comic URLs and it's tableView gets loaded from this array.
The problem is:
Whenever I add a downloadable item to the list and open the 2nd viewController, the tableView gets loaded from the beginning and all the files start to download again.
I need a way so that I can add the files from the 1st viewController to 2nd viewController's tableView and the downloads should continue from the same stage as they were previously (and not from the start again)
Below is the code of how my DownloadCell works. Each cell is downloading multiple files. The first method is called while a cell is being created.
-(void)startDownload:(NSArray*)comicFiles
{
allComicFiles=comicFiles;
downloadedFiles=[[NSMutableArray alloc] init];
[self removeViews];
[self appendViewsToCell];
[loading startAnimating];
[self downloadFile:0];
}
Now, the second method is called to download the files asynchronously.
-(void)downloadFile: (int)index
{
if(index>=[allComicFiles count])
{
[self downloadComplete];
return;
}
SingleComicFile *comicFile=allComicFiles[index];
NSURL *url=comicFile.URL;
NSString *fileName=comicFile.FileName;
lblFileName.text=fileName;
NSString *applicationDocumentsDir =
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *storePath = [applicationDocumentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",fileName]];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:storePath]){
[self updateDownloadProgress:url];
[self downloadFile:index+1];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_sync(dispatch_get_main_queue(), ^{
[data writeToFile:storePath atomically:TRUE];
[self updateDownloadProgress:url];
[self downloadFile:index+1];
});
});
}
The tablewviecontroller class has a mutable array which is basically a array of file array. It has a constructor which I call from the comic listing page to populate the array.
-(MyDownloadManager *)initFileQueue: (NSMutableArray *)withComicFileQueue
{
if(_fileArray==nil)
{
_fileArray= [[NSMutableArray alloc]init];
}
_fileArray=withComicFileQueue;
return self;
}
And I am binding the tableview like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
DownloadCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil) {
cell = [[DownloadCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSArray *comicFiles= _fileArray[indexPath.row];
[cell startDownload:comicFiles];
return cell;
}
Maybe in your case the solution is to make shareViewController that will be initiated once (loadView, viewDidLoad will be called once). Also when you close shared loading view controller it is not reallocated and continues to download selected file. You can implement some delegate method to notify download is completed and so on. In this way you will get shared long-living downloader.
static DownloadViewController *sharedInstance = nil;
#implementation DownloadViewController
+ (id) sharedInstance
{
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [DownloadViewController new];
});
return sharedInstance;
}
I just want the user to just delete a picture by just tapping the picture using a collection view. I am doing this for a month and can't get things straight and even getting confused. My code for to do this is below. I am also getting a memory warning sometimes aswell, If i use the instruments app to look for detail the memory allocation does not get freed and just builds up until crash.
- (void)viewDidLoad
{ Trash = [NSMutableArray array];
filenames = [NSMutableArray new];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray *locationStrings = [[NSArray alloc]initWithObjects:#"Bottoms", #"Dress", #"Coats", #"Others", #"hats", #"Tops",nil ];
for(NSString* location in locationStrings){
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSError *error;
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:fPath error:&error];
collectionTrash.delegate =self;
collectionTrash.dataSource=self;
for(NSString *str in directoryContent){
NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
[filenames addObject:finalFilePath];
}
}
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
NSLog(#"j");
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [filenames count];
NSLog(#"b");
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"ReuseID";
TrashCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
UIImageView *imageInCell = (UIImageView*)[cell viewWithTag:1];
NSString *cacheKey = filenames[indexPath.item];
imageInCell.image = [self.imageCache objectForKey:cacheKey];
if (imageInCell.image == nil) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:filenames[indexPath.item]];
if (image) {
[self.imageCache setObject:image forKey:cacheKey];
dispatch_async(dispatch_get_main_queue(), ^{
TrashCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
UIImageView *imageInCell = (UIImageView*)[updateCell viewWithTag:1];
imageInCell.image = image;
});
}
});
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"s:%d", [Trash count]);
NSString *trashBin = [Trash objectAtIndex:indexPath.row];
NSLog(#"k%#l",trashBin);
[filenames removeObjectAtIndex:indexPath.row];
[Trash removeObjectAtIndex:indexPath.row];
[self deleteMyFiles:trashBin];
[collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]];
}
NSString *myFileName;
-(void) deleteMyFiles:(NSString*)filePath {
NSError *error;
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
} else {
NSLog(#"%#",filePath);
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
[self.imageCache removeAllObjects];
}
When I try to tap a log comes out saying below.
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
*** First throw call stack:
(0x30afbe83 0x3ae5c6c7 0x30a31d95 0x638e9 0x3349d76f 0x3349d495 0x33406ea3 0x33406ea3 0x332781a1 0x332af9fd 0x332af3ab 0x33284d79 0x33283569 0x30ac6f1f 0x30ac63e7 0x30ac4bd7 0x30a2f471 0x30a2f253 0x357632eb 0x332e4845 0x628a5 0x3b355ab7)
libc++abi.dylib: terminating with uncaught exception of type NSException
Your Trash array not content any objects.
And you are trying to access object from Trash array
NSString *trashBin = [Trash objectAtIndex:indexPath.row];
Which is the cause of crash.
Note: object name should start with small letters.
In your code, Trash = [NSMutableArray array]; will be autoreleased at some time, so when you access it in '-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath', it has been released. If you are sure there is reason to use this variable instead of using only filenames, you can do this Trash = [[NSMutableArray array] retain]; or Trash = [[NSMutableArray alloc] init]; in your '- (void)viewDidLoad'.
It is not clear what you are trying to do. The notion of "Trash" suggests you are trying to implement a two-stage delete: put items in the trash and at some later time, empty the trash (delete them for good when the user selects an "Empty the Trash" button). But your post and your code suggest a single-stage delete (the user selects to delete with no chance to undo).
Your collectionView:didSelectItemAtIndexPath: delegate method looks particularly confused. As Bhumeshwer correctly points out, your Trash is empty and trying to access any index is causing your crash. No where in this code are you putting anything in Trash But, by simply removing that line of code, I don't think the method will do what you want.
If you are implementing a two-stage delete, I think you simply want to remove the string from filenames and add it to Trash. Don't remove the object from Trash and don't delete files in Trash. Somewhere else (like when the "Empty the Trash" button is selected), call deleteMyFiles to empty the trash.
If you are implementing a single-stage delete, get rid to Trash all together: don't allocate it in viewDidLoad and don't empty it in didSelectItemAtIndexPath. Simply, remove the object from filenames AND update the imageInCell!
Suggestions:
move
collectionTrash.delegate =self;
collectionTrash.dataSource=self;
to wherever collectionTrash is being created and initialized. Inside viewDidLoad and inside the for loop, it is out of context. Your mixing unrelated lines of code making your code unorganized and confusing. At least, move it out of the for loop.
think model-view-controller. What's the model here? Is it filenames or Trash? Is the CollectionView a view of filenames or Trash? What is collectionTrash?
Trash should be trash (as Bhumeshwer points out);
what is NSString *myFileName; and what's it doing there?
don't forget to manage your imageCache. You put images in but don't take them out. When you remove a file from filenames, remove the image from imageCache.
I could go on, but will stop here. Good luck.
I would like to seek some help in setting tag for buttons in cells. This is a question with a link to the previous I posted : iOS Using NSDictionary to load data into section and rows
However, though I could pass in data dynamically now, my renewal button on each of the rows could not seem to get the data and would only detect the same book title of each section when any of the rows in the section is selected.
Based on what I've read so far, it's because the button is being recycled hence, unable to detect which book is being selected properly. I've tried to set tag:
cell.renewButton.tag = indexPath.row;
How my code looks like now:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UserCustomCell *cell = (UserCustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.bookTitle.frame = CGRectMake(12, 0, 550, 40);
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"UserCustomCell" owner:self options:nil];
cell = userCustomCell;
self.userCustomCell = nil;
cell.renewButton.tag = indexPath.row;
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
cell.bookTitle.frame = CGRectMake(12, 0, 550, 40);
cell.renewButton.frame = CGRectMake(600, 14, 68, 24);
}
[cell.renewButton useBlackActionSheetStyle];
//########## EDIT STARTS HERE
dataSource = [[NSMutableDictionary alloc] init]; // This would need to be an ivar
for (NSDictionary *rawItem in myArray) {
NSString *date = [rawItem objectForKey:#"date"]; // Store in the dictionary using the data as the key
NSMutableArray *section = [dataSource objectForKey:date]; // Grab the section that corresponds to the date
if (!section) { // If there is no section then create one and add it to the dataSource
section = [[NSMutableArray alloc] init];
[dataSource setObject:section forKey:date];
}
[section addObject:rawItem]; // add your object
}
self.dataSource = dataSource;
//NSLog(#"Data Source Dictionary: %#", dataSource);
NSArray *sections =[[dataSource allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSString *sectionTitle = [sections objectAtIndex:indexPath.section];
NSArray *items = [dataSource objectForKey:sectionTitle];
NSDictionary *dict = [items objectAtIndex:indexPath.row];
cell.bookTitle.text = [dict objectForKey:#"name"];
cell.detail.text = [NSString stringWithFormat:#"Due Date: %# Due Time: %#",
[dict objectForKey:#"date"], [dict objectForKey:#"time"]];
cell.renewButton.tag = indexPath.row;
return cell;
}
but it doesn't work at all. Would be sooooo grateful for any suggestions :) Thank you!!
P.S: My copy of xcode is not updated, only till version4. Saw some people mentioning storing of tag status in DataModel but it's only available in newer versions. :)
You cannot use the button tags, as these will be the same as those of the cells they have been recycled from. Instead, use the indexPath to determine on which row you are and use that directly. No need to go through the button tag.
I could not see your cell.renewButton being assigned a selector method (the method that should be triggered on tapping the button).
[cell.renewButton addTarget:self action:#selector(renewButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
Also, I would specify a tag number with an offset, as tag of 0 is almost like not tagging at all. First row of tableView will give indexPath.row = 0.
Above your code,
#define OFFSET 100 /* Or any number greater than 0 */
In cellForRowAtIndexPath,
...
[cell.renewButton addTarget:self action:#selector(renewButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
cell.renewbutton.tag = indexPath.row + OFFSET;
...
In the renewButtonPressed method,
-(void)renewButtonPressed:(id)sender
{
tappedNum = [sender tag] - OFFSET;
/* do your stuff */
}
tappedNum will give you the row that the button is tapped, starting with 0.