Don't reload cell when scroll back - ios

I have an issue that i didn't find anywhere on the web. I have to display some custom cell loaded from a Nib. I download the informations from my DB on a separated thread and allocate it on a new MutableArray.
I have also images that are allocated in a separate array and called when necessary, but not downloaded from the web.
My table view is "lagging" when scrolled down, that is (i guess) because it has to place the things on the correct cell, but when i scroll back it lags again and it reloads again all informations.
I see that Facebook app loads cells when scrolling down (but not so slowly) and when scrolled back it doesn't reload anything and cells are already loaded (no matter how many). How can i do something like this? My table is very slow and i have (at the moment) only 3 cells.. But when the application is finished these would be 100 or 200.
Can anyone help me?
This is my code: (this on viewDidLoad)
NSString *strURL = [NSString stringWithFormat:#"..(myurl)..];
// to execute php code
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:[strURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
// to receive the returend value
NSString *strResult = [[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding];
if(![strResult isEqualToString:#"0"]) {
posts = [strResult componentsSeparatedByString:#"^"];
}
immProfilo = [NSMutableArray array];
for (int i = 0; i < [posts count]; i++) {
NSArray *datiPost = [[posts objectAtIndex:i] componentsSeparatedByString:#"/"];
FBProfilePictureView *fotoProfiloFB = [[FBProfilePictureView alloc] initWithFrame:CGRectMake(22, 22, 55, 55)];
fotoProfiloFB.profileID = [datiPost objectAtIndex:1];
[immProfilo addObject:fotoProfiloFB];
}
[self.postTab reloadData];
And that is my tableview code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)postTab {
return [posts count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"%ld_%ld",(long)indexPath.section,(long)indexPath.row];
PostTabCell *cell = (PostTabCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"PostTabCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
NSString *annuncio = [posts objectAtIndex:indexPath.section];
datiAnnuncio = [annuncio componentsSeparatedByString:#"/"];
[cell addSubview:[immProfilo objectAtIndex:indexPath.section]];
cell.nome.text = [datiAnnuncio objectAtIndex:0];
cell.location.text = [self getAddressFromLatLon:[[datiAnnuncio objectAtIndex:2] floatValue] withLongitude:[[datiAnnuncio objectAtIndex:3] floatValue]];
cell.testoPost.text = [datiAnnuncio objectAtIndex:4];
return cell;
}

The reason why your table view is so laggy is because each time the table view asks the delegate for a cell (your cellForRowAtIndexPath method), you perform a synchronous network request with getAddressFromLatLon, blocking the main thread.
An immediate fix would be to -at least- store these texts in some kind of array, so that next time the table view asks for the same cell you don't have to perform a network request again.
This would solve the problem of the tableview being laggy when you scroll back up, but not when you scroll down the first time. One general rule you can always consider true, is that you shouldn't ever block the main thread with network requests.
You've got two options now: load all of these texts at the very beginning on a secondary thread while showing a spinner (easy but presents several problems, such as it wouldn't scale up very well with the number of cells). Or you would have to design an asynchronous loader that will show a placeholder string, such as loading address..., until the address is actually loaded.
Also Totumus has a point in his answer, but that is not the main cause of your lag (although it will be a big problem once the number of your cells increase).

The lag you describe is caused by the imageview you keep adding to your reusable cell. Everytime the cell reloads an imageview is added to your cell again.
Prevent functions like addSubview: in your cellForRowAtIndexPath:.
Instead you should create a custom cell that already has this UIImageView you need for your profilepictures.
The reason you load information when you scroll is that you probably load information (getAddressFromLatLon: ?) each time a cell is being created/reused. This is fine but should be done in a seperate thread (and thus the response should be handled asynchronically).
The reason you see facebook not loading anymore when you scroll back up again is because they cache their data when it is loaded into the application. Probably with CoreData.

Related

Having conflicts when update UIButton image within UITableViewCell in iOS

I have an app with some posts placed at an UITableView. Each post have a favorite button and I need to change its image when the user clicks on it. Here are the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"identifier";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if(cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"myNib" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSDictionary *post = [posts objectAtIndex:indexPath.row];
[cell.likeButton addTarget:self
action:#selector(clickedOnLike:)
forControlEvents:UIControlEventTouchUpInside];
cell.likeButton.tag = indexPath.row;
And on click handler:
-(IBAction)clickedOnLike:(id)sender
{
int tag = buttonSender.tag;
NSDictionary *post = [posts objectAtIndex:tag];
if( ![self likedAlready:post] ){
//set liked on this view...
//update view
NSLog(#"button: %#",buttonSender);
[sender setImage:[UIImage imageNamed:#"newImage.png"] forState:UIControlStateNormal];
//send like to server...
}
}
At this point, everything is going alright. The problem is, after click a button, update the view and scroll to other cells, the other buttons views I never clicked are updated too. For example, when I click a button at indexPath 1, the ones at 5 and 9 change their images automatically. This is a mistery to me, since I call the action sender directly and update only it. Thanks for help.
The reusable cell do it: dequeueReusableCellWithIdentifier.
U need to deal manually with things like this.
U can invoke manually to your clickedOnLike: method from the CellForRowAtIndexPath:
Try to maintain inside your modal (from your MVC development architecture), and access there from the CellForRowAtIndexPath:
That will solve your problem :)
Good Luck!
In your cellForRowAtIndexPath, set the default image of your button, then change it if its a favorite. The rows tend to get cached so always set your data to what you expect it to be.
As others have said, the problem is because the view is recycled for everyline, and in the recycled copy the button may have had its picture changed to the new one.
you need to keep somewhere in memory the status of the post, and on the cellForRowAtIndexpath set the correct image for the button every time the row is rendered.
Alternatively, if you have a very small number of posts, just eliminate recycling by doing
static NSString *identifier = nil;

observables and the UItableview visible cells

I am writing an IOS chat app.
I have a tableview where each cell contains a textbox, upon loading each cell I subscribe to a chat channel on pubnub.com. I have an observable in the viewdidLoad watching for incoming messages. The object receieved from the observable contains the channel name and the message text and date.
I want to display messages to their appropriate cells.
I'm not sure where to capture a fully loaded cell when its in view and subscribe to the channel. Then in the observable how do I compare the channel name to the cell currently in view on the screen?
I tried the isVisible but I'm getting more than whats visible on the screen. The thing is I want to only show messages to cells that are currenty in view, kind of how vine starts to playa video when the user has stopped on that cell even if they dont click it..
See code below
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
self.messages = [NSMutableDictionary dictionary];
self.configuration = [PNConfiguration defaultConfiguration];
[self load_DEMO_DATA];
[self setClient];
[self connectToServer];
//Observable
[[PNObservationCenter defaultCenter] addMessageReceiveObserver:self
withBlock:^(PNMessage *message) {
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.dateFormat = #"HH:mm:ss MM/dd/yy";
PNChannel *channel = message.channel;
NSString *messages = [self.messages valueForKey:channel.name];
if (messages == nil) {messages = #"";}
messages = [messages stringByAppendingFormat:#"<%#> %#\n",[dateFormatter stringFromDate:message.receiveDate.date],message.message];
//Get TextBox & Set Caption
UITextView *caption = (UITextView *)[[(UITableViewCell *)[(UITableView *)self.tableView cellForRowAtIndexPath:CurrentIndexPath] contentView] viewWithTag:105];
caption.text = [NSString stringWithFormat:#"%#%#", caption.text, messages];
[caption scrollRangeToVisible:NSMakeRange([caption.text length], 0)];
}];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TimelinePostCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell==nil)
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
// Configure the cell...
NSDictionary *post = [posts objectAtIndex:indexPath.item];
NSDictionary *user = [post objectForKey:#"user"];
//Set Current Channel
self.currentChannel = [PNChannel channelWithName:[post objectForKey:#"channelName"] shouldObservePresence:YES];
//Subscribe to Chat
[self subscribeToChannel:self.currentChannel.name];
self.currentPost = post;
//Get Channel History
[self ChannelHistory];
return cell;
}
Well, first of all, -tableView:cellForRowAtIndexPath: shouldn't be used to initiate any time consuming operations. To keep high performance, you should return prepared UITableViewCell from that method less then in 160ms or you will see "lag". This method will be called few times right after table has been shown (as many as you have cells with values).
You should use –scrollViewDidEndDragging:willDecelerate: (with decelerate NO) and –scrollViewDidEndDecelerating: as appropriate place and time when you should initiate subscriptions to the channel and any other manipulation with PubNub client.
You can subscribe on all channels at once - it will be less network overhead than subscribing for every single channel one-by-one. If you want to preserve resources and keep pricing low by keeping client subscribed on few channels, than you should use same methods to unsubscribe from previous channels (same as were suggested to detect current cell and store current channel and so on).
Also just suggestion about how you feed cell with model: move model processing inside custom cell class (there is no reason for controller to know something about structure of cell's views and which data should be shown there).

UITableview in UITableviewcontroller - cellforrowatindexpath not called on reload

I am stucked in a stupid problem since two days. I have got a UITableViewController pushed in Navigation Controller. When it loads, since there is no data, so empty table is visible:
But when I receive data from server, and call [self.tableView reloadData], both numberOfRowsInSection and heightForRowAtIndexPath get invoke except cellForRowAtIndexPath and my controller is shown without table:
I can't really understand that why it is happening. All datasource methods are called except for cellForRowAtIndexPath. Please someone guide me... Thanks..
ActLogController.h
#interface ActLogController : UITableViewController<ASIHTTPRequestDelegate,UITableViewDataSource,UITableViewDelegate>
#property(strong) NSMutableArray *activityKeys;
#end
ActLogController.m
- (void)viewDidLoad
{
[super viewDidLoad];
activityKeys = [[NSMutableArray alloc] init];
self.tableView.dataSource = self;
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self retrieveActivityLogFromServer];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return activityKeys.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
ActivityLogUnit *act = [activityKeys objectAtIndex:indexPath.row];
cell.textLabel.text = act.price;
return cell;
}
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50.0;
}
-(void) requestFinished:(ASIHTTPRequest *)request
{
NSArray *list = [request.responseString JSONValue];
for (int i = 0; i < list.count; i++) {
NSArray *singleTrade = [[list objectAtIndex:i] JSONValue];
ActivityLogUnit *unit = [[ActivityLogUnit alloc] init];
unit.symbol = [singleTrade objectAtIndex:0];
unit.type = [singleTrade objectAtIndex:1];
unit.price = [singleTrade objectAtIndex:2];
unit.volTraded = [singleTrade objectAtIndex:3];
unit.remVol = [singleTrade objectAtIndex:4];
unit.actualVol = [singleTrade objectAtIndex:5];
unit.recordID = [singleTrade objectAtIndex:6];
unit.orderNo = [singleTrade objectAtIndex:7];
unit.time = [singleTrade objectAtIndex:8];
[activityKeys addObject:unit];
}
if(activityKeys.count > 0)
{
[self.tableView reloadData];//it is called and I have got 6 items confirm
}
}
EDIT
I set some dummy data in my array activityKeys, Data is being displayed in table, and cellforrowatindexpath is called successfully. But as I reload data after sometime, other methods are called except this one and table disappears as shown in 2nd pic. Any ideas?
Your problem is that you probably download the data content on a background thread. Since you cannot update the UI on a background you need to call [self.tableView reloadData] on the main thread once the download is finished!
Hope it helps!
Looks like you in secondary thread, do reloadData in main thread by using following code
[self.tableView performSelectorOnMainThread#selector(reloadData) withObject:nil waitUntilDone:NO]
You can always use [NSThread isMainThread] to check whether you are in main thread or not.
you have to write in viewdidload
self.tableView.dataSource = self;
self.tableView.delegate = self;
Edit
You have no xib then where you are declared/sets your tableview's properties. Like
self.tableView.frame = CGRectMake(0, 45, 320, 500);
self.tableView.rowHeight = 34.0f;
self.tableView.separatorStyle=UITableViewCellSeparatorStyleNone;
[self.view addSubview:self.tableView];
[self.tableView setBackgroundColor:[UIColor clearColor]];
self.tableView.showsVerticalScrollIndicator=NO;
self.tableView.dataSource = self;
self.tableView.delegate = self;
Try with
#property(nonatomic,strong) NSMutableArray *activityKeys;
Firstly I strongly believe that the instance name of the tableview should not be similar to the local variable (i.e. tableView in class should not be equal to tableView in delegate and data source methods).
Second in your question posted I could not see the delegate set for the table view.
answer Posted By Samir Rathod should work if you have #property for the table view set in you .h or .m file.
You can also do this if you have a XIB file.
Press ctrl and click + drag the tableview to the files owner and set the delegate and datasource.
For me the problem was my stubbed-out code returning 0 as the number of sections (so it never asked how many rows were in the section, and never got their data). Just change that to 1 if it's your problem also. Additionally, I was working in Swift, where the issue mentioned by #shahid-rasheed is coded (slightly) differently:
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
At last I got it worked. cellForRowAtIndexPath was not being called because of a line of code I didn't mention here... which was actually removing some color background layer from view. It was causing reloading issue. After removing it, everything works fine.
Thank you all of you for your cooperation :)
I had the same symptoms too. In my case, the first time I loaded the data (from core data) in viewDidLoad, NSSortDescriptor was used to sort the data.
On the click of a button, the core data was fetched again (this time with changes) and tableView data reloaded. It initially gave me a blank table view after the button was clicked because I forgot to sort the data the second time I fetched it.
Learning points: Remember to call all methods which modify the cell (like background color mentioned by iAnum, or NSSortDescriptor in my case) if you have used them in the beginning!

updating a UI table view cell with upload status - iOS

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.
There are two possibilities, assuming you have a background process that is uploading:
The tableview is a delegate and implements some uploadProgress
function
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
}

cellforRowAtIndexPath efficiency?

Whenever I scroll my tableview it is very laggy. I think it has to do with how I am loading up my cells. I use UINib (5.0+) whenever I can while still providing backwards compatibility. Then I load my custom cell's labels and images with items from a NSDictionary from a NSArray which is loaded from NSUserDefaults in the ViewDidLoad.
Is there any way to improve the efficiency of this cellForRowAtIndexPath?
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = (CustomCell *)[aTableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
if ([self labelCellNib]) {
[[self labelCellNib] instantiateWithOwner:self options:nil];
} else {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
}
cell = [self CustomTableCell];
[self setCustomTableCell:nil];
}
NSDictionary *dictionary = [myArray objectAtIndex:indexPath.row];
NSData *data = [dictionary objectForKey:#"OCRImage"];
cell.previewPicture.image = [self roundCorneredImage:[UIImage imageWithData:data] radius:60];
cell.titleLabel.text = [dictionary objectForKey:#"Title"];
cell.titleLabel.delegate = self;
cell.dateLabel.text = [dictionary objectForKey:#"Date"];
if (indexPath.row%2) {
cell.backgroundImage.image = firstImage;
}
else {
cell.backgroundImage.image = secondImage;
}
return cell;
}
Edit:
- (UIImage*)roundCorneredImage: (UIImage*)orig radius:(CGFloat) r {
UIGraphicsBeginImageContextWithOptions(orig.size, NO, 0);
[[UIBezierPath bezierPathWithRoundedRect:(CGRect){CGPointZero, orig.size}
cornerRadius:r] addClip];
[orig drawInRect:(CGRect){CGPointZero, orig.size}];
UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Edit2: These are the lines that are causing the lag:
NSData *data = [dictionary objectForKey:#"OCRImage"];
cell.previewPicture.image = [self roundCorneredImage:[UIImage imageWithData:data] radius:60];
As #Till said in a comment, you should launch your app in Instruments (Product -> Profile in Xcode), and select the CPU -> Time Profiler instrument.
Then, scroll around over the place for a few seconds, then hit the Record toolbar icon in instruments to close your app. You will be able to see the scrolling section because CPU usage will probably be pinned at 100% (unless it's slow because of network activity problem).
Click on the timeline after the start of the high CPU activity area, and click the "start inspection range" toolbar button, then click before the end of the high CPU activity area and click the "stop inspection range" toolbar button.
You can now drill down into the call tree view at the bottom of the window to figure out exactly where all your CPU usage is. In my experience it's usually easier to find the problem if you turn off "invert call tree" option on the left.
Performance bugs can be very hard to find, and sometimes a line of code that is obviously slow actually isn't causing any problems at all. The only way to fix performance issues without wasting time is to use Instruments.
Make sure that you've set the reuse identifier for your cell to the same thing that you've specified in your code, i.e. #"Cell". If they don't match, then you won't be reusing cells properly, and probably spending a lot more time creating cells than necessary.
If you are properly recycling cells, then you should take a look at the code after the if (cell == nil) {...} block. You'll be skipping that entire block once the table has created enough cells to fill the screen (and maybe one or two more), so most of the time attributable to this method while scrolling will be due to the following code. It'd be interesting to know what myArray is, and if it's actually an array, what the objectForKey: method does. Nothing else there looks like it should take a long time, but the best way to find out where the cycles are going is to profile your code in Instruments.
Some of my notes after looking at your code:
Is roundCorneredImage:radius: caching the result? If not, executing CG calls for every cell would surely present a bottleneck. Updated: Use instruments to be sure, but it might be faster (memory allowing) to store the processed UIImage in a collection so that you can pull it out again the next time that method is called with the same parameters.
All of your UIImages could be declared elsewhere and then presented in this method. Your current code instantiates a new UIImage for each cell which can also bottleneck your scrolling. Updated: Since Image1.png and Image2.png are basically static, you could declare them in your interface or as a static ivar and then just assign them to the background image rather than instantiating UIImage each time.
It may be faster to subclass UITableViewCell and instantiate that instead of reaching into UINib. Also, you'd then be able to separate your layout/data logic from the delegate method. Here's a gist of what I did in my UITableViewCell subclass. Basically, I store the entity with the cell and the cell knows about it's labels and such. This keeps the cell layout logic out of my data source code.
It looks like you're using an NSDictionary as your data source. If you have a lot of objects in that dictionary, it may be considerable faster to use CoreData and an NSFetchedResultsController. Here's a good post on the matter. Updated: Ok, that shouldn't be an issue.
-
Edit
So if you removed all of this:
NSDictionary *dictionary = [myArray objectForKey:#"OCRImage"];
cell.previewPicture.image = [self roundCorneredImage:[UIImage imageWithData:data] radius:60];
if (indexPath.row%2) {
cell.backgroundImage.image = firstImage;
}
else {
cell.backgroundImage.image = secondImage;
}
and it still lags, let's look at your constructors...what do these lines do?
cell = [self CustomTableCell];
[self setCustomTableCell:nil];
Also, you're not using any transparent images or anything in your table cell are you? Those have been known to cause drawing lag...
-
Edit #2
If you strip down to this, what happens?
CustomCell *cell = (CustomCell *)[aTableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
if ([self labelCellNib]) {
[[self labelCellNib] instantiateWithOwner:self options:nil];
} else {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
}
cell = [self CustomTableCell];
[self setCustomTableCell:nil];
}
cell.titleLabel.text = [dictionary objectForKey:#"Title"];

Resources