Advertise here




Advertise here

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

UITableView - Load all cells BEFORE user scrolls to view them.

DutchDutch Posts: 884Registered Users
edited August 2009 in iPhone SDK Development
I am writing an app that essentially displays a list of usernames in a UITableView that is full of custom cells. Each cells has XML parsing ability and parses XML for the user it is displaying. I have it working WONDERFULLY and am very happy.

However once the User's XML is parsed, they are (possibly) repositioned in the table (and underlying array) based on the XML data that is returned. This works fine too - except for one thing. I can only see 7 rows at time on my table. If a user has 25 cells (like I do) the last 18 cells do not parse until the user scrolls down. Once that's done, the XML is parsed and the cell is repositioned as expected. Of course, I understand that is because the cells are not loaded right off the bat to save resources. However, I would really LOVE to have them all preloaded. The data involved is not all that consuming.

Is there a way to "Pre-Load" all of the cells?
Post edited by Dutch on

Replies

  • nobre84nobre84 Posts: 981Registered Users @ @ @
    edited August 2009
    You wish them to start away in the correct order that is based on the XML parsing ?
    Well you could pre-parse everything before you even have the chance to display the few first visible items and load them in the correct order from scratch. Maybe in your "numberOfSectionsInTableView" delegate method, scan the whole datasource and arrange it accordingly.
    How big is your dataset ?
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    Just to put this out there, I don't plan on selling this app. I'll likely be giving it away. More so, I am writing it primarily for my own use. Therefore, I will tell you exactly what it is and share as much info as you need to help troubleshoot.

    I am writing an app that essentially lists out my XBox Live friends, and tells me their status. I've seen an app to two that do this, but they don't do exactly what I want.. That being said...
    nobre84 wrote: »
    You wish them to start away in the correct order that is based on the XML parsing ?

    I don't really care what order they are fired in. Each one runs on it's own and updates itself when the parsing is finished. Its setup like this...
    RootviewController
    ........UITableView
    ................UserCell (UITableViewCell)
    ........................UserInfo (Custom NSObject)
    ................................User Information Variables
    ................................XML Data Download (async)
    ................................XML Parser / methods (async)
    ................................User Image Download (async)
    

    So when RootviewController's cellForRowAtIndexPath is called, the UserCell is told to start parsing... it tells it's UserInfo object to do all the work. When UserInfo is done (parserDidEndDocument), it tells its parent UserCell to refresh the data. The UserCell then accesses [UserInfo variousProperties] to display information and format the object within it.
    nobre84 wrote: »
    Well you could pre-parse everything before you even have the chance to display the few first visible items and load them in the correct order from scratch.

    I have them sorted by online status, and then by username. The users who are online get placed above those who are not. The order might change if the information changes when refreshed. I'm trying to code it in such a way where it's very flexible. I want each cell to be responsible for itself... if that makes any sense.
    nobre84 wrote: »
    Maybe in your "numberOfSectionsInTableView" delegate method, scan the whole datasource and arrange it accordingly.
    How big is your dataset ?

    Actually, I am re-sorting the datasource every time an item is added, removed, or something within it (which would require a re-ordering) changes (ie on a refresh). As far as sections, I am keeping it to one. All users will be in the same list, just have a different icon indicating their status.

    My dataset is 25 right now. It could be up to 100.

    Here is an example of the XML being returned (my gamertag:cool:)
  • jsdjsd Posts: 900Registered Users
    edited August 2009
    Dutch wrote: »
    I am writing an app that essentially displays a list of usernames in a UITableView that is full of custom cells. Each cells has XML parsing ability and parses XML for the user it is displaying. I have it working WONDERFULLY and am very happy.

    However once the User's XML is parsed, they are (possibly) repositioned in the table (and underlying array) based on the XML data that is returned. This works fine too - except for one thing. I can only see 7 rows at time on my table. If a user has 25 cells (like I do) the last 18 cells do not parse until the user scrolls down. Once that's done, the XML is parsed and the cell is repositioned as expected. Of course, I understand that is because the cells are not loaded right off the bat to save resources. However, I would really LOVE to have them all preloaded. The data involved is not all that consuming.

    Is there a way to "Pre-Load" all of the cells?

    You could create an array of UITableViewCells ahead of time and then just return the correct cell when cellForRowAtIndexPath is fired. However this is inefficient. You should reuse table cells and just change the data displayed in the cell when reusing a cell.

    If you're doing a custom table cell subclass you can use the prepareForReuse method to know when the cell is about to be reused.

    I think your objection is you don't want to hit the network and do the XML parsing when each cell comes into view? In that case you should preload and parse the XML ahead of time, store it in some sort of model object, and just use the model object to populate the cell's content as it comes into view.
  • mesohornymesohorny Posts: 69Banned Members
    edited August 2009
    I agree with JSD
    jsd wrote: »
    You could create an array of UITableViewCells ahead of time and then just return the correct cell when cellForRowAtIndexPath is fired. However this is inefficient. You should reuse table cells and just change the data displayed in the cell when reusing a cell.

    If you're doing a custom table cell subclass you can use the prepareForReuse method to know when the cell is about to be reused.

    I think your objection is you don't want to hit the network and do the XML parsing when each cell comes into view? In that case you should preload and parse the XML ahead of time, store it in some sort of model object, and just use the model object to populate the cell's content as it comes into view.

    Busty Escorts
    [url=http://www.*****-enhancement-secrets.com]***** enhancement pills[/url]
  • drewagdrewag Posts: 268Registered Users @ @
    edited August 2009
    What is stored in your underlying array? your problem doesn't seem to be with loading all of the cells. Instead you want to start parsing the xml before loading the cell. Probably you want to setup a function that will start parsing all of the xml at once which should be independent of displaying or even setting up the cell. There is no reason to load all the cells before hand, you should just load the data.
  • nobre84nobre84 Posts: 981Registered Users @ @ @
    edited August 2009
    Seeing the big picture helps a lot.
    I agree with drewag that your parsing should be done ahead of time, and independent from your cells display.
    You could have a manager object that keeps a collection of your UserInfo objects, and have methods to refresh the xml, and reorder (by status, name, etc).
    That collection would provide the table datasource, which would just dumbly load reused tablecells that access the UserInfo data and display as needed.
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    I actually got it working pretty darn near the exact end result I was looking for with JSDs original idea. I think there is something in the sorting function that is slowing down the sort/table reload. They really seemed to be hogging resources (to the point where the screen statyed black until they were all done.
    -(void)sortUsers{
    	int reverseSort = NO;
    	[self setUserCells:[userCells sortedArrayUsingFunction:alphabeticSort context:&reverseSort]];
    	[COLOR="green"]//Also this gives me a warning: Passing Argument 1 ...  setUserCells ... objective-C Type[/COLOR]
    }
    
    NSInteger alphabeticSort(UserCell *cell1, UserCell *cell2, void *reverse){
    	
    	NSString *string1=[NSString stringWithFormat:@"%@|%@",[[cell1 user] statusText],[[cell1 user] username]];
    	NSString *string2=[NSString stringWithFormat:@"%@|%@",[[cell2 user] statusText],[[cell2 user] username]];
    	
    	if ((NSInteger *)reverse == NO) {
            return [string2 localizedCaseInsensitiveCompare:string1];
        }
        return [string1 localizedCaseInsensitiveCompare:string2];
    }
    
    

    Now, my cells are updating on the fly (without needing to scroll to them) but I am sorting only when the parsing is completely finished for all cells.. This really improved upon the performance.

    I will have to ponder over the other option proposed, because I really like where you guys are going with that - in fact thats how I STARTED developing my app, but changed over somewhere alogn the line. I guess the optimal structure of the objects in ObjC is not crystal clear to me just yet so I do a lot of second-guessing. and rewriting. I think you guys are right on, but since I have it working I would need to think of a really good reason why it's worth changing (even though it's more 'correct'?).

    Thank you guys SO MUCH. You made my weekend.
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    Ok, so I think there is a problem with my sorting routine. In fact, Im pretty sure. Given the code...
    -(void)sortUsers{
    	int reverseSort = NO;
    	[[self userCells] sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
    }
    
    NSInteger alphabeticSort(UserCell *cell1, UserCell *cell2, void *reverse){
    	
    	NSString *string1=[NSString stringWithFormat:@"%@|%@",[[cell1 user] statusText],[[cell1 user] username]];
    	NSString *string2=[NSString stringWithFormat:@"%@|%@",[[cell2 user] statusText],[[cell2 user] username]];
    	
    	if ((NSInteger *)reverse == NO) 
            return [string2 localizedCaseInsensitiveCompare:string1];
        else
    		return [string1 localizedCaseInsensitiveCompare:string2];
    }
    

    Basically, I am comparing the user's status and name to get the sort... (A=Online, B=offline, nil or Z=Unknown). I am getting the result:
    B|M Bland 86
    B|Anubix32
    B|drummermike22
    B|El Kabooom
    B|Jack Bauer CQB
    B|KAPOP
    B|MAYEM MONKEY
    

    I can't figure out why "B|M BLAND 86" is coming up before "B|Anubix".. Any thoughts?
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    NVM
    [[self userCells] sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
    

    should have been
    [[self userCells] sortUsingFunction:alphabeticSort context:&reverseSort];
    
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    I got one more weird problem going on... My cells are all perfect now. They are displaying and refreshing as desired, however I have a strange objc_error occurring. Once I click the cell, I am bringing the user to a detail window....

    RootViewController.m
    - (UserCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        ALog(@"");
    	iXBoxAppDelegate *mainDelegate = (iXBoxAppDelegate *)[[UIApplication sharedApplication] delegate];
    	UserCell *c=[[mainDelegate userCells] objectAtIndex:indexPath.row];
    	return c;
    	ALog(@"");
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    	ALog(@"");
    	iXBoxAppDelegate *mainDelegate = (iXBoxAppDelegate *)[[UIApplication sharedApplication] delegate];
    	userDetail=[[UserDetail alloc] initWithNibName:@"UserDetail" bundle:nil];
           [COLOR="Red"] //Setting UserDetail.cell from [mainDelegate userCells]. The Cells have all the data to display.[/COLOR]
    	[userDetail setCurrentCell:(UserCell*)[[mainDelegate userCells] objectAtIndex:indexPath.row]];
    	[[self navigationController] pushViewController:userDetail animated:YES];
    	[userDetail release], userDetail=nil;
    	ALog(@"");
    }
    

    The view shows up fine. When I click BACK on the navController, I am getting the error at that point... I put a PRETTY_FUNCTION log call at the beginning and end of EVERY method in my code.

    UserDetail.m
    -(void)setCurrentCell:(UserCell *)c{
    	[self addAllItems];
    	[self setCell:c];
    	[self updateInfo];
    }
    
    - (void)dealloc {
    	ALog(@"");
    	[countryLabel release];
    	[locationLabel release];
    	[reputationLabel release];
    	[countryLabel release];
    	[bioLabel release];
    	[locationLabel release];
    	[zoneLabel release];
    	[infoLabel release];
    	[gImage release];
    	[theScrollView release];
    	[userImage release];
    	[cell release]; 
            [COLOR="Red"]//is this releasing the ACTUAL userCell in my [mainDelegate userCells]?[/COLOR]
            [COLOR="Red"]//It seems like RootViewController.CellForRowAtIndexPath is having a hard time finding the cell.[/COLOR]
            [super dealloc];
    	ALog(@"");
    }
    

    Here is a chunk of the output from the time I click the cell, until the crash

    2009-08-25 10:59:50.847 iXBox[58225:20b] -[RootViewController tableView:didSelectRowAtIndexPath:] [Line 146]
    2009-08-25 10:59:50.848 iXBox[58225:20b] -[UserDetail addAllItems] [Line 34]
    2009-08-25 10:59:50.849 iXBox[58225:20b] -[iXBoxAppDelegate newLabelWithPrimaryColor:selectedColor:fontSize:bold:] [Line 184]
    2009-08-25 10:59:50.850 iXBox[58225:20b] -[iXBoxAppDelegate newLabelWithPrimaryColor:selectedColor:fontSize:bold:] [Line 184]
    2009-08-25 10:59:50.851 iXBox[58225:20b] -[iXBoxAppDelegate newLabelWithPrimaryColor:selectedColor:fontSize:bold:] [Line 184]
    2009-08-25 10:59:50.851 iXBox[58225:20b] -[iXBoxAppDelegate newLabelWithPrimaryColor:selectedColor:fontSize:bold:] [Line 184]
    2009-08-25 10:59:50.852 iXBox[58225:20b] -[iXBoxAppDelegate newLabelWithPrimaryColor:selectedColor:fontSize:bold:] [Line 184]
    2009-08-25 10:59:50.858 iXBox[58225:20b] -[UserDetail addAllItems] [Line 86]
    2009-08-25 10:59:50.858 iXBox[58225:20b] -[UserDetail updateInfo] [Line 92]
    2009-08-25 10:59:50.859 iXBox[58225:20b] -[iXBoxAppDelegate calcLabelSize:withFont:maxSize:] [Line 209]
    2009-08-25 10:59:50.859 iXBox[58225:20b] -[iXBoxAppDelegate calcLabelSize:withFont:maxSize:] [Line 209]
    2009-08-25 10:59:50.860 iXBox[58225:20b] -[UserDetail updateInfo] [Line 129]
    2009-08-25 10:59:50.862 iXBox[58225:20b] -[RootViewController tableView:didSelectRowAtIndexPath:] [Line 152]
    2009-08-25 10:59:52.499 iXBox[58225:20b] -[RootViewController viewDidAppear:] [Line 31]
    2009-08-25 10:59:52.500 iXBox[58225:20b] -[RootViewController numberOfSectionsInTableView:] [Line 42]
    2009-08-25 10:59:52.505 iXBox[58225:20b] -[RootViewController tableView:numberOfRowsInSection:] [Line 53]
    2009-08-25 10:59:52.506 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.506 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.507 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.508 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.509 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.509 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.509 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.510 iXBox[58225:20b] -[RootViewController tableView:heightForRowAtIndexPath:] [Line 60]
    2009-08-25 10:59:52.510 iXBox[58225:20b] -[RootViewController viewDidAppear:] [Line 35]
    2009-08-25 10:59:52.511 iXBox[58225:20b] -[UserDetail dealloc] [Line 136]
    2009-08-25 10:59:52.511 iXBox[58225:20b] -[UserDetail dealloc] [Line 150]
    2009-08-25 10:59:52.512 iXBox[58225:20b] -[RootViewController tableView:cellForRowAtIndexPath:] [Line 67]
    2009-08-25 10:59:52.513 iXBox[58225:20b] -[RootViewController tableView:cellForRowAtIndexPath:] [Line 67]
    2009-08-25 10:59:52.514 iXBox[58225:20b] -[RootViewController tableView:cellForRowAtIndexPath:] [Line 67]
    2009-08-25 10:59:52.514 iXBox[58225:20b] -[RootViewController tableView:cellForRowAtIndexPath:] [Line 67]
    2009-08-25 10:59:52.515 iXBox[58225:20b] -[RootViewController tableView:cellForRowAtIndexPath:] [Line 67]

    [Session started at 2009-08-25 10:59:52 -0400.]
    2009-08-25 10:59:52.516 iXBox[58225:20b] -[RootViewController tableView:cellForRowAtIndexPath:] [Line 67]
    2009-08-25 10:59:52.524 iXBox[58225:20b] -[RootViewController tableView:cellForRowAtIndexPath:] [Line 67]
    GNU gdb 6.3.50-20050815 (Apple version gdb-966) (Tue Mar 10 02:43:13 UTC 2009)
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for details.
    This GDB was configured as "i386-apple-darwin".sharedlibrary apply-load-rules all
    Attaching to process 58225.

    I'm not really sure where to look for my error. Can someone point me in the right direction? I can't seem to find where I am over-releasing my object (which Im sure it is).

    Here is the Debugger Bit
    #0 0x00b40029 in CALayerGetDelegate
    #1 0x3091ccc6 in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]
    #2 0x30919b20 in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:]
    #3 0x30920ad6 in -[UIView(Hierarchy) removeFromSuperview]
    #4 0x30926864 in -[UIScrollView removeFromSuperview]
    #5 0x30920d54 in -[UIView dealloc]
    #6 0x30506515 in NSPopAutoreleasePool
    #7 0x00b5436e in run_animation_callbacks
    #8 0x00b54109 in CA::timer_callback
    #9 0x302454a0 in CFRunLoopRunSpecific
    #10 0x30244628 in CFRunLoopRunInMode
    #11 0x32044c31 in GSEventRunModal
    #12 0x32044cf6 in GSEventRun
    #13 0x309021ee in UIApplicationMain

    #14 0x00002548 in main at main.m:14

    Please come through for me, like you always do. :)
  • drewagdrewag Posts: 268Registered Users @ @
    edited August 2009
    First I would enabled NSZombies:
    CocoaDev: NSZombieEnabled
    Then you can run it and see what object is being released too early (it will show up in the console).
    Then I would override the retain and release function on that object:
    -(void)retain { NSLog(@"Retain Count: %d",[self retainCount]+1); // Breakpoint [super retain]; } -(void)release { NSLog(@"Retain Count: %d",[self retainCount]-1); // Breakpoint [super release]; } [/Code] Then with the breakpoints you can see what objects are retaining and releasing it and you should be able to find the problem. If it is not a class that you created (like CALayer or something) then it will be more complicated and I will try to help you with that too if that is the case.[Code]
    -(void)retain
    {
    NSLog(@"Retain Count: %d",[self retainCount]+1);
    // Breakpoint
    [super retain];
    }
    -(void)release
    {
    NSLog(@"Retain Count: %d",[self retainCount]-1);
    // Breakpoint
    [super release];
    }
    [/Code]
    Then with the breakpoints you can see what objects are retaining and releasing it and you should be able to find the problem.

    If it is not a class that you created (like CALayer or something) then it will be more complicated and I will try to help you with that too if that is the case.
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    It says
    [Session started at 2009-08-25 12:02:08 -0400.]
    2009-08-25 12:02:08.752 iXBox[59421:20b] *** -[CALayer retain]: message sent to deallocated instance 0xdb4100
    GNU gdb 6.3.50-20050815 (Apple version gdb-966) (Tue Mar 10 02:43:13 UTC 2009)
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for details.
    This GDB was configured as "i386-apple-darwin".sharedlibrary apply-load-rules all
    Attaching to process 59421.


    Im not calling anything DIRECTLY to cause the crash as depcited by...
    #0 0x00b40029 in CALayerGetDelegate
    #1 0x3091ccc6 in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject: copySublayers:]
    #2 0x30919b20 in -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:]
    #3 0x30920ad6 in -[UIView(Hierarchy) removeFromSuperview]
    #4 0x30926864 in -[UIScrollView removeFromSuperview]
    #5 0x30920d54 in -[UIView dealloc]
    #6 0x30506515 in NSPopAutoreleasePool
    #7 0x00b5436e in run_animation_callbacks
    #8 0x00b54109 in CA::timer_callback
    #9 0x302454a0 in CFRunLoopRunSpecific
    #10 0x30244628 in CFRunLoopRunInMode
    #11 0x32044c31 in GSEventRunModal
    #12 0x32044cf6 in GSEventRun
    #13 0x309021ee in UIApplicationMain

    #14 0x00002548 in main at main.m:14

    PS -I'd be happy to zip up the code and send it over if you're willing to check it out - don't want to impose.
  • drewagdrewag Posts: 268Registered Users @ @
    edited August 2009
    Dutch wrote: »
    It says




    Im not calling anything DIRECTLY to cause the crash as depcited by...



    PS -I'd be happy to zip up the code and send it over if you're willing to check it out - don't want to impose.

    Ya sure, I'll take a look.
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    drewag wrote: »
    Ya sure, I'll take a look.



    SOLVED- had duplicate release statements in my dealloc. Whooooops. Thanks again drew
  • jsdjsd Posts: 900Registered Users
    edited August 2009
    Dutch wrote: »
    SOLVED- had duplicate release statements in my dealloc. Whooooops. Thanks again drew

    For future reference - CLANG static analyzer will find these errors (and many more) for you automatically....

    see Clang Static Analyzer
    and AnalysisTool - Clang wrapper for static analysis of iPhone and Mac applications for an easier-to-use GUI wrapper around same.

    (note: the GUI version complains about a lot of things that are highly debatable in my opinion - like warning you to use autorelease after every alloc/init. that advice might be OK for Mac OS development, but in iPhone programming, it's not a great idea.)
  • DutchDutch Posts: 884Registered Users
    edited August 2009
    jsd wrote: »
    For future reference - CLANG static analyzer will find these errors (and many more) for you automatically....

    see Clang Static Analyzer
    and AnalysisTool - Clang wrapper for static analysis of iPhone and Mac applications for an easier-to-use GUI wrapper around same.

    (note: the GUI version complains about a lot of things that are highly debatable in my opinion - like warning you to use autorelease after every alloc/init. that advice might be OK for Mac OS development, but in iPhone programming, it's not a great idea.)

    I have actually tried this before, but I am soooo new to the Mac environment it's a bit over my head. I will look into the GUI version.
Sign In or Register to comment.