Preface: I am new to the iPhone SDK, Obj-C, Interface Builder and Cocoa. I'm likely doing something obviously wrong.
Question:
I have a UITableView
which crashes if I scroll it. It will scroll a little to reveal the full cell of the bottom most half-hidden cell, but won't load the next one. Similarly, if I scroll past the top to fully hide the bottom most cell, and it rubber bands back to show that cell, it will crash before showing it. This strikes me as odd because it is drawing the first 7 of 11 cells correctly. The cell data is in an NSArray
, in a UITableViewController
linked as both the dataSource
and delegate
for the UITableView
in Interface Builder. It works when the view initializes.
I'm making an App I thought I'd be done with 2 days ago that just calculates combinations and displays a list of them in what I thought would be a convenient scrolling table view. Right now, it doesn't even calculate everything, the NSArray
in the DataSource
is initialized once with some strings like @"Hello"
and @"World"
.
Steps to reproduce: Because I'm using IB, I can't exactly show you the full story in code. So I'm going to describe what I did so far and hope it doesn't make you sleepy.
Made a new "Tab Bar Application" in Xcode, because I want 2 tabs, and I don't want a nav bar nor a full screen table. I moved the MainWindow.xib
's first tab view out into FirstView.xib
as an analogue to the given SecondView.xib
. This worked nicely. I modified the view to contain two UITextField
s for inputs, and a UITableView
for output. This worked but the table was empty. I subclassed the UITableViewController
wherein I populated an NSArray
property named combinations
with 11 strings, and then added
// Set up the cell...
cell.userInteractionEnabled = NO;
cell.text = [combinations objectAtIndex:indexPath.row];
where there had only been the comment. In IB I added a Table View Controller
to the FirstView.xib
and set it's class name to match the name of this new subclass, and control-dragged the Table View
in my view onto this Combinations Table View Controller
twice. Once linking the dataSource
and once the delegate
. Although I get the same behavior if only the dataSource
is linked.
This runs and populates the table's visible rows (6.5) with the first 7 values in the dataSource
combinations
. I can scroll 0.5 cells down, and then back up. But if I scroll more than 0.5 cells up or down the app will crash. The explanation in the report reads:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSCFTimer tableView:cellForRowAtIndexPath:]:
unrecognized selector sent to instance 0x52ca40'
I didn't make an NSCFTimer
nor did I link it to my Table View
, I suspect that the cellForRowAtIndexPath
is exactly a message that should have gone to my dataSource
or delegate
so I'm confused at why it went astray and where it ended up going.
Update: Thanks for the answers and comments. My problem seems to be that the CombinationsTableViewController
(subclass of UITableViewController
) is actually not instantiated any particular place in my code. It does get created as some time (when the FirstView.xib
is loaded) and is apparently managed while the initial tableView is filled with 7 cells, and is then released. So I need to identify where/how to make a retained reference to this controller. My Application Delegate
should probably have some outlet that holds this controller that can be linked as the instance which is in the xib
. Yeah, I'm new to this. I know I could just eliminate these troubles by avoiding the IB and doing things explicitly in code, but I figure I want to learn to use the IB flexibly.
Finally: Yes, I needed a retained instance of the table view controller. It sounds elementary, but this wasn't clear when working with the IB as I had. Read my own post for the whole process and fix.
Aside: Either the debugger needs detailed instructions (any links appreciated), or it doesn't work very well. I seem to get more information more quickly by letting the App crash and reading the report it generates. But this requires a tedious termination, relaunch, and 3 clicks. I had really wanted to move on from this to wiring up the inputs, doing the calculation, and updating the table with each change. That's supposed to be the hard part, not this making a framework member work stuff.
Further rambling: This was all in the iPhone SDK for 2.2.1. At the time iPhone OS 3.0 non-beta was not available yet without joining the club for cold hard cash. I expected it to be at the open of WWDC 2009, but it was actually today (July 17th 2009) that the free public 3.0 SDK was made available.
looks like you are losing your tableView delegate.
What looks like it is happening is the UITableViewDelegate is getting released and the app is then using the same pointer address for an NSCFTimer.
Have you called release on your delegete anywhere, or have you not retained the delegate if it is in an autorelease pool.
Okay so, if you followed the steps to reproduce I will now add the steps to solve this:
Steps to fix this problem
Subclass a UIViewController
. I called mine CombinationsViewController
. In this controller add a property as an IBOutlet for the combinations
Table
ViewController
from step 6 below.
Don't forget to import the right stuff, and synthesize the table view controller, also release it in the dealloc
method.
In FirstView.xib
change the File's Owner
class to this latest subclass.
Link it's combinationsViewController outlet to the Combinations Table View Controller
in the FirstView.xib
made in step 7 below.
Open the TabBarController
in your MainWindow.xib
select the first tab, and in the Identity Inspector change the class to the latest subclass (CombinationsViewController
).
That makes the table populate normally and scroll stuff.
Now I'm going to move on finally and get some custom table view cell stuff happening and actually make my app do stuff.
Enumerated Steps to reproduce as a reference to the fix:
Made a new "Tab Bar Application" in Xcode.
Opened the Tab View Controller
, dragged the prefab view for the first tab out into the MainWindow.xib
Made a new view based xib called FirstView.xib
as an analogue to the given SecondView.xib
, and put that prefab view into this xib.
Linked the view to the File's Owner
's view
outlet.
I modified the view to contain two UITextField
s for inputs, and a UITableView
for output.
I subclassed the UITableViewController
as CombinationsTableView
wherein I populated an NSArray
property named combinations
with 11 strings, and then added the cell.text = [combinations objectAtIndex:indexPath.row];
code where there had only been the comment about setting up the cell.
In FirstView.xib
I added a Table View Controller
and set it's class name to match the name of this new subclass, and control-dragged the Table View
in my view onto this Combinations Table View Controller
twice. Once linking the dataSource
and once the delegate
.
At this point the table does render with data, but the scrolling breaks. This is because the CombinationsTableView isn't retained anywhere. And that's very unclear to a first time IB user. So you need to apply the fix listed above.
The first person to summarize this in their answer get the correct answer check mark. E.G. Make a viewController subclass that is the file owner of FirstView.xib and contains an retained IBOutlet you can link to your table view controller in the same xib file.
The reason the first cell loads, is because the tableview pre-loads that like the other cells that are on screen.
All cells are loaded from the datasource method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
I would make sure that you have explicitely set the delegate and datasource of the tableView. This can be done in code or IB. by:
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];
Also as Bluephlame said, you might be releasing the UITableVewController somewhere.
To find out, set a breakpoint inside the dealloc method:
- (void)dealloc{
//releasing things
[super dealloc];
}
If you do release it, you will hit this breakpoint. Then you can start to track down the culprit.
allocate memory to array instead of [NSArray arrayWithObjects:];
. Without memory allocation it wont reload rows....use array=[NSArray alloc] initWithObjects];
...instead of the above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With