1

Hell everyone :)

My experience with the UITablewView Controller in iOS is unfortunately quite limited. What I need in my application is a UI table view which contains one custom cell for each active upload currently being uploaded to a webserver (videos, audio, etc).

Each of these uploads run asynchrounously in the background, and should all be able to update things such as UILabels in their respective cells saying something about the update progress in percentage, etc.

Now I have found a solution which works. The problem is I do not know if it is actually secure or not. Based on my own conclusion I don't really think that it is. What I do is simply to retrieve a reference of the UIViews from a cell which is getting created, and then store those references in the upload objects, so they can change label text and so on themselves.

My Own Solution

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 static NSString *CustomCellIdentifier = @"CustomCell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];

if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"UploadCellView" owner:self options:nil];

    if ([nib count] > 0)
    {
        cell = customCell;
    }
    else
    {
        NSLog(@"Failed to load CustomCell nib file!");
    }
}

NSUInteger row = [indexPath row];
UploadActivity *tempActivity = [[[ApplicationActivities getSharedActivities] getActiveUploads] objectAtIndex:row];

UILabel *cellTitleLabel = (UILabel*)[cell viewWithTag:titleTag];
cellTitleLabel.text = tempActivity.title;

UIProgressView *progressbar = (UIProgressView*)[cell viewWithTag:progressBarTag];
[progressbar setProgress:(tempActivity.percentageDone / 100) animated:YES];

UILabel *cellStatusLabel = (UILabel*)[cell viewWithTag:percentageTag];

[cellStatusLabel setText:[NSString stringWithFormat:@"Uploader - %.f%% (%.01fMB ud af %.01fMB)", tempActivity.percentageDone, tempActivity.totalMBUploaded, tempActivity.totalMBToUpload]];

tempActivity.referencingProgressBar = progressbar;
tempActivity.referencingStatusTextLabel = cellStatusLabel;

return cell;
}

As you can see, this is where I think I'm doing something which isn't quite good enough: tempActivity.referencingProgressBar = progressbar; tempActivity.referencingStatusTextLabel = cellStatusLabel;

The upload activities get a reference to the controls stored in this cell, and can then update them themselves. The problem is that I do not know whether this is safe or not. What if the cell they are refering to gets re-used or deleted from memory, and so on?

Is there another way in which you can simply update the underlying model (my upload activites) and then force the UI table view to redraw the changed cells? Could you eventually subclass the UITableViewCell and let them continously check up against an upload and then make them upload themselves?

EDIT

This is how the upload activity objects calls their referencing UI controls:

- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten
totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
if (referencingProgressBar != nil)
{
    [referencingProgressBar setProgress:(percentageDone / 100) animated:YES];
}

if (referencingStatusTextLabel != nil)
{
    [referencingStatusTextLabel setText:[NSString stringWithFormat:@"Uploader - %.f%% (%.01fMB ud af %.01fMB)", percentageDone, totalMBUploaded, totalMBToUpload]];
}
}

My only concern is that, since these objects run asynchrounously, what if at some given point the UI table view decides to remove or re-use the cells which these upload objects are pointing to? It doesn't seem very secure at all.

CodingBeagle
  • 1,888
  • 2
  • 24
  • 52
  • Hello~ I want to use 'ApplicationActivities'. But, I can't know what I add framework. Please tell me about this. :-0 Thanks! – hyekyung Jun 19 '12 at 05:48
  • ApplicationActivities is my own custom class as part of the project :) It's not from any external framework. – CodingBeagle Jun 25 '12 at 08:40

1 Answers1

2

There are two possibilities, assuming you have a background process that is uploading:

  1. The tableview is a delegate and implements some uploadProgress function
  2. The tableview listens for uploadProgress NSNotifications

The second is easier to implement, just put the listeners start/stop in viewdidappear/viewdiddissappear. Then in your upload you can track progress and emit a notification with attached userinfo that gives an integer value to progress. The table has a function that handles this notification being received and redraws the cells. Here is how to add data to the userinfo part of an NSNotification.

If you wanted to be fancier you could have an upload id and map this to a cell index, and only redraw that particular cell. Here's a question and answers that explain how to do this.


Disgusting Pseudocode Since I don't have access to my IOS dev env right now

upload function:

uploadedStuff{
  upload_id = ... // unique i, maps to row in table somehow
  byteswritten = ...
  bytestotal = ....
  userinfo = new dict
  userinfo["rowid] = upload_id
  userinfo["progress"] = (int)byteswritten/bytestotal
  sendNotification("uploadprogress",userinfo)
}

tableview.m:

viewdidappear{
  listenForNotification name:"uploadprogress" handledBy:HandleUploadProgress
}

viewdiddisappear{
  stoplisteningForNotification name:"uploadprogess"
}

HandleUploadProgess:NSNotification notification {
 userinfo = [notification userinfo]
 rowId = [userinfo getkey:"rowId"]
 progress = [userinfo getkey:"rowId"]
 // update row per the link above
}
Community
  • 1
  • 1
nflacco
  • 4,972
  • 8
  • 45
  • 78
  • hmmmm this seems like it could be a possible solution. How exactly do you update information in one particular cell and then re-draw it? I think that's also a point where I'm not exactly sure on the best way to handle it. I also updated my post with more information about how these upload objects call their referincing UI controls. – CodingBeagle Feb 16 '12 at 18:31
  • I've added a link to an explanation of how to redraw individual rows. What you need to do in your table is have a dictionary of that maps uploads to a table row index. I assume each of your uploads has a unique identifier or name, so I'd use that. Then in your upload function you want to send an NSNotification with a userinfo dictionary containing two pieces of info, an upload unique id and progress percentage. This notification is handled by a function which unpacks the notification userinfo data and then uploads that particular row. – nflacco Feb 16 '12 at 18:57
  • Aha, this seems like it could work very well :) Thanks a lot for the help! I'll try that. – CodingBeagle Feb 16 '12 at 19:20
  • 1
    well I found a solution which follows your idea :) Basically I first subclassed UITableViewCell so that each cell in the table have a property that points to an upload activity they get assigned when getting drawn. And then it's actually those custom cell objects that listen for the notifications that the upload activity objects pumps out :) That way I could get rid of all the complex mapping. – CodingBeagle Feb 17 '12 at 13:26
  • This way I remove all the drawing responsibilities away from the upload activity objects, so all they have to do is pump out notifications when important things happen. The custom cell objects can then catch the notifications and check if that notification came from the upload activity which it points to itself, and then update its own view accordingly. Do you think this is a good solution? Or could it be done better? – CodingBeagle Feb 17 '12 at 13:28
  • very nice! more elegant than making a big map – nflacco Feb 17 '12 at 17:29
  • @user1163640 care to share some code, please? where are you instantiating and removing observers for a cell? – kain Jan 16 '13 at 06:34