Using a dynamic, custom cell prototype that I design in, say, UITableViewController A (in Interface Builder/Storyboard), works really well with dequeuing the cell (through its identifier, cellA) and such in cellForRow... I use a custom class (UITableViewCell subclass, let's name it MyCustomCell) to link up the labels and image thumb and it works all pretty well and straight forward in my UITableViewController A.
Now I create a UITableViewController B (in Storyboard), which happens to have the exact same design/functionality for its custom cells (dynamic cells). So I switch the class of these cells to the MyCustomCell and give it a new identifier, cellB.
In UITableViewController B, I dequeue the cell in cellForRow... and use the new identifier cellB. Note: Using cellA here leads to a crash, more or less obviously.
But when the table view shows up, while running the app, the UITableViewController A works just fine, and the almost identical UITableViewController B does not work (empty cells).
In Storyboard, it looks sort of off a bit, because the custom cell is designable within the UITableViewController A but in UITableViewController B, it's just a simple, plain cell. Despite the class associated to MyCustomCell.
How would one avoid copy&pasting these cells to the other controller (and therefore heavily going back and forth between copies when making design changes) – and rather just properly re-use it?
What you are doing is correct. I don't know why its not working, it may be some problem with reloading the tableview; check with your datasource and the datasource method.
- (NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger) section
rather what you can do is: drag a
Viewcontroller
and then drag a
tableview
and make it dynamic and do the thing that you did earlier; it worked fine for me when I did so myself.
Saying the Cell is from a your custom class doesn't mean it's "designed" the same. What you really try to achieve here (and what I'm looking for) is some king of "Contained" Cell, but this is only doable with a ContainerViewController in iOS 6 I think. The other option is to use a XIB for that one Cell — that should work just as good, but then you lose the benefits of having an overview in the Storyboard.
I had a similar problem, I had created a custom cell with a uitextfield inside it. It was working well in the first tableview but not in the second. I figured out that the textfield's delegate was not connected to the custom cell. I connected the two in storyboard by making a connection from textfields delegate outlet back to the textfield.
Related
I am working on an app that has several prototype cells in one view. This worked well for easily altering the appearance of the app while in development using the storyboard. However, now I'm adding search (filtering) capability. I would like the appearance of the tableview to remain unchanged, just filter out some of the results.
My understanding is that I have to create new cells to do this. Is this correct? If it is, is there a way to create a cell with all the properties of my prototype cells. As it is now, the newly created (search result) cells have default settings.
Thanks.
You can certainly use copy and paste. Create a xib file (an empty one), and copy the cell you want from your table view in the storyboard, and then paste it into the xib file. In the viewDidLoad method for your table data source, register that nib file:
[self.searchDisplayController.searchResultsTableView registerNib:[UINib nibWithNibName:#"SearchCell" bundle:nil] forCellReuseIdentifier:#"SearchCell"];
Then in the cellForRowAtIndexPath method, you just dequeue a cell with that identifier for your search results table view.
The thing to understand clearly is that the table view that appears when you are doing a search with the UISearchDisplayController conglomerate is not your table view. It is a different table view, and you do not have a UITableViewController managing it - the UISearchDisplayController does that. Thus you must take other measures if you want that different table view to look like your table view.
EDIT: On the whole (and after the little exchange with rdelmar in the comments on his answer), I tend to think the easiest solution is to abandon the use of cell prototypes altogether. If you design the cell in a nib (xib), you can then use that cell both for the real table and for the search results table. In both cases you register the nib with the respective table view - and then dequeue just does the right thing all by itself, in both cases, with no change in the code.
You can see me doing something similar here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch21p632searchableTable/p536p550searchableTable/RootViewController.m
... except that in that case I'm registering the same cell class for both tables, not the same nib. But it all comes down to the same thing. However, note that I do not start with a storyboard, so I never fell into the trap of using a prototype cell in the first place.
I currently have two UITableViewControllers, one with the prototype of the custom cell, and the other one not. Since both of them are connected through a push segue from the first to the second. I pass the selected custom cell to the second tableView.
Everything works fine, except that when I come back to the first view, the selected (and passed) cell is hidden, but when I scroll it enough to hide the blank space and scrolling back it shows.
How can I fix this?
The Big Nerd Ranch Guide says basically to stay away from Storyboard for just this kind of reason.
Amongst other things about Storyboards, it says:
"Overall storyboards make easy code easier and difficult code more difficult".
Not sure you should be passing a cell through the push segue.
When not using storyboard, you would typically create a new file of type UITableViewCell called e..g MyCustomerTableviewCell.
Then in your UITableView viewDidLoad method, create a new Nib and add it to the tableView,
UINib *nib = [UINib nibWithNibName:#"MyCustomTableViewCell" bundle:nil];
// register this nib that contains the cell
[[self tableView] registerNib:nib forCellReuseIdentifier:#"MyCustomTableViewTableViewCell"];
Then create a new empty XIB file (file->new->User Interface->empty) and call it MyCustomTableViewCell.xib.
Drop a UITableViewCell into the interface builder space, change it from UITableViewCell type to MyCustomTableViewCell, make sure it's File's Owner is MyCustomTableViewCell.
Then in your UITableView file, do this...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomTableViewTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"MyCustomTableViewCell"];
...
return cell;
}
You can probably do this as part of a storyboard project anyway (though I haven't tried missing XIB and Storyboard in one project).
If I understand correctly what you are doing, you are passing a reference of the tapped cell to the second view controller, and then you are showing it inside the table view of the second controller passing the same reference in the cellForRowAtIndexPath method.
It doesn't seems a good choice :-)
First: because that cell was allocated and queued for use in the first view controller. At any time, the cell could receive a prepareForReuse message by the first view controller.
Second: when you show the cell in the second table view, the cell is removed from the first table view.
If you don't want to create another prototype cell in the second view controller, you can, for example, allocate a new cell in the first one, copy all properties from the tapped one to the new, and then pass the new to the second viewcontroller.
This is the first solution I've thought about, but sure there are other better solutions :-)
In my application, I thought it would be a good idea to create a subclass of UITableViewContoller (call it GenericTableViewContoller) which has a few prototype cells created in Storyboard that are reused throughout the app.
Now, what I wanted to do was to create a subclass of GenericTableViewContoller (say SpecialTableViewController) that has some other prototype cells created in Storyboard that are used only in SpecialTableViewController.
Thus, I have two scenes in my main storyboard, one for GenericTableViewContoller and one for SpecialTableViewController, each with their own set of prototype cells.
The problem I am running into, is that SpecialTableViewController gets nil prototype cells when I call dequeueReusableCellWithIdentifier: for a cell identifier declared in the GenericTableViewContoller scene. Cells declared in the SpecialTableViewController scene dequeue just fine.
Is this expected behavior (I have a strange suspicion it is)? Do I have to call registerNib:forCellReuseIdentifier: in SpecialTableViewController to actually get them registered from the other scene? Any simple way to get around this?
Is this expected behavior (I have a strange suspicion it is)?
Yeah, this is the expected behavior. The core concept here is that the storyboard is a object-instance designer, not a class designer. For example, you're free to make three scenes, each with the same view controller class, but all with different view layouts.
There isn't an simple way to share prototype table cell layouts between different table view instances short of putting them into their own XIB, and using -registerNib:forCellReuseIdentifier: and manual segues.
I am new to Cocoa and Core Data and I've encountered a weird problem. I successfully created the Core Data model, imported the data, made the UI (Navigation controller, tableViewController with searcDdisplayController) but now I'm stuck at one problem. If I implement a UITableViewController (with no UITableView in the nib file), the results fetched from fetchedResultsController are weird - the row count is correct (500 rows), but they are repeating themselves - only 8 different rows.
However, I was able to fix this problem by adding #synthesize tableView; in the .m file.
Then i encountered another problem - when i click on a row and push the details view, then click the back button on the Navigation Controller, the selected row in the tableView is still selected.
I hope I am being clear on what's wrong. Let me recap:
If I don't put the #synthesize tableView; on the top of the implementation file, the deselection of the row is working fine, but the results are wrong - 8 rows (out of 500) repeating in the tableView.
If I put the #synthesize in the file, the data in the tableView is correct, but there is no animation when i get back to the tableView with the navigation controller.
I also tried to put a UITableView in the nib file of the listviewcontroller, but the results were the same - no deselection of the row. I also tried to deselect row in the viewWillAppear delegate, but the indexPath of the indexPathForSelectedRow is null.
Oh, and I'm using an NSFetchedResultsController. Like I said - the fetched results are correct, but it seems that they're not properly fed to the tableview (if it is not synthesized..)
Thanks!
I might be able to solve your row highlighting issue but you will need to include some code examples so we can help you with the other items...
To deselect a row you can call the method [deselectRowAtIndexPath:animated:] this will allow you to deselect the row before or after you push your detail view controller from your tableView selection delegate. Table View Reference
Thanks for responding!
Hmm..I may have found a solution, but I'm not sure it's correct.. I am implementing the UITableViewController in my new class, but I'm actually using a new UITableView (tableView2) for the data fetching. In the nib file i created the UITableViewController, set the Class name to my custom class BUT I didn't connect the Table View in the Interface Builder with my newly created tableView2. I hope you'll be able to understand.. It's quite complicated, this whole stuff :S
So if I get this right - if I implement an UITableViewController, my class automatically gets a self.tableView? But, like I said in my question, if I don't synthesize the property in the .m file, the controller can't deselect the row automatically..but if I do, the data isn't correct and is repeating..Maybe there is a problem in the cellForRowAtIndexPath, where i get my cells from self.tableView..but It's not logical :S
Anyway, now the row de-selection animation works without my interference and the data displayed is correct. I'm suspecting that the UITableViewController has some other methods that need to be overwritten if using the NSFetchedResultsController.
Do you think my method is incorrect? Having a new UITableView in my custom UITableViewController class?
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 UITextFields 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 combinationsTableViewController 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 UITextFields 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.
I had the same issue and fixed it by adding nil into an array as the last item.
It was crashing for this:
pairs = [[NSArray alloc] initWithObjects:#"EUR/USD",#"USD/JPY",#"GBP/USD",#"USD/CHF",#"USD/CAD"];
but not for this:
pairs = [[NSArray alloc] initWithObjects:#"EUR/USD",#"USD/JPY",#"GBP/USD",#"USD/CHF",#"USD/CAD",nil];
Hope this helps.
I had this same problem. I had a UITableViewController sub-class in a nib file, but I had only declared the UITableView as an outlet/property, which the tableview's UITableViewController was getting released while still in use.
The quick fix was simply to add an outlet to the File's Owner class that referenced the embedded UITableViewController.