Create custom class for Navigation Controller (Immediate crash) - iphone

I'm working on a Storyboards based app which constitutes of multiple view controllers linked via a Navigation Controller (initial view controller). This is used as the home screen. I'm using static cells and a static table view.
Now, I want to hook up some labels, buttons, etc as outlets/actions to "the .h" file and subsequently write custom methods in the .m file. But (as expected?) there's yet no custom class to select from in the identity inspector. So I created a new custom class as a subclass of UITableViewController (which seems to be the correct one?)
My new, custom class is now hooked up to my storyboard view controller. I then enter (what I believe to be) the correct numberOfSectionsInTableView: 1, and numberOfRowsInSection:5 (I got 1 section including 5 rows). When ran, it immediately crashes with the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
If return 0 sections and 0 rows, everything works fine (expect there are no rows displayed). Once I return anything > 0 in either of them, it crashes with this error.
What have I missed? Is there anything else I'm supposed to add to the file before it can be attached as a custom class and I can use it as any other custom class? Again, I've set up everything in the interface builder (storyboard), including the table cell rows, its styling, etc. What am I doing wrong?
My Table View Setup
My Table Cell Setup

Pretty sure this is the answer: in storyboard, make sure a prototype cell is painted on the table view (the one in the crashing view controller) and make sure it has a reuse identifier set to "Cell". Your code for rowAtIndexPath: is trying to dequeue a cell with that identifier and it's not there.
The identifier can be set using the attributes inspector (the middle tab on the upper right) in storyboard.

Related

IBOutlets in UITableView Static Cells

I created a UITableView (and related classes), with two sections:
the first one is a static custom cell
the second one is a prototype cell
To control the static cell, I implement a class, and in that class I added IBOutlet to an object (for example label) in the static cell. When I attempt to access that outlet in the class controlling the table view, I get this error:
outlet doesn't exist
I made a video, for explain better my problem.
Short version
UITableView cannot be at the same time a Static Cells and a Dynamic Prototypes content. You need to pick one or the other.
Because you need Dynamic Prototypes, use Dynamic Prototypes.
Long version
Steps to create Dynamic Prototypes:
Select your Table View in IB > Show Attributes Inspector > Table View > Content > Dynamic Prototypes
From the section IB > Show the Object Library, drag as many Table View Cell onto your table view as you have cell types.
If you have some cells that never change at run time, you can very well create 1 instance of each right there in IB ; they will behave like Static Cells
For the custom cells(†), the recommended practice is to create subclasses of UITableViewCell, and apply that custom class to the relevant cell or cells (IB > Show the Identity Inspector), and pick your Custom Class from the list. This of course implies you have created such sub-classes.
Do not forget to assign a unique identifier to each cell prototype, so that you can retrieve these at runtime using dequeueReusableCellUsingIdentifier;
With this setup, you can select your Table View Cell, IB > Show Assistant Editor, and control-drag references in your custom class. You may need to tell the Assistant Editor which file to pick.
(†) There are plenty of other ways to do this ; all outside of the scope of this response.

Subclassing a UITableViewContoller subclass created using Storyboard

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.

reuse view from storyboard

I have a tableview with custom section headers. The view for the section header is defined in the storyboard and wired to an instance variable. Is there a way to request a new instance of the view from the storyboard?
In the past I have done this by having the section header defined in its own xib file and getting a new instance by using
[[NSBundle mainBundle] loadNibNamed:#"TimerViewSectionHeader" owner:self options:nil];
UIView *newHeaderView = self.sectionHeaderView;
I dont' think there is a way to do that. Best bet is to put the tableview custom header view in a separate nib and load it like you did in your code sample whenever you need to use it.
I tried to do the same thing and ran into the same problem.
I like to work with storyboards a lot and was impressed how fast I could create a working UI. However, as soon as you need to re-use views it makes a lot of sense to put those into a separate nib along with its UIViewController subclass.
You can then place a generic UIView in all the places where your re-used view should go and add the view using your ViewController:
[myReusableViewController loadView];
[myReusableViewController viewDidLoad]; // You have to handle view callbacks yourself.
[self.myReusableViewPlaceholder addSubview:myResusableViewController.view];
[myReusableViewController viewWillAppear:YES];
So to sum it up:
Use storyboard, it's great
Create the scaffold of your application in the storyboard, along with any static view (like About screens etc.)
Create re-used views in a custom nib + UIViewController subclass and add UIView placeholders in your storyboard.
In another answer I thought about some Pros and Cons of Storyboard
The solution I've come up with for this is as follows:
I have a tableview with multiple prototype cells that displays complex data. There is a segue to a detail view, and a transaction process view.
This first tableview has a search button that displays a new tableview with the results. It needs the same functionality as the main tableview that pushes it; including segues to the detail and transaction progress views so:
On storyboard, select and copy your main tableview. Deselect and paste. Create a push segue from your main tableview to your 2nd tableview; or from where ever you want to navigate to it from. Modify the 2nd tableview as you like. IE: If it requires some UI changes no problem.
Create a new viewcontroller class that is a subclass of the viewcontroller running the main tableview.
Override the data delegate in your subclass to serve up the subset of data you want.
Back in the storyboard, select your 2nd tableview controller and in the identity inspector select your subclass as the custom class.
For this solution to work smoothly, your app really needs to be managing data for the views. You could use prepareforsegue to pass data from 1st tableview to the second, but I've found the app data model far more flexible from numerous points of view.
Unless you have buttons that push to the sub views via segue, your subclass will need to override functions that push via segues with identities. NB Segues must have unique identifiers if you id them at all.
It took a lot of trial and error to figure this out, but once you understand the concept, it's a relatively smooth solution that is quite adaptable and not so bad to implement.
I am not sure about just views, but the way that I was able to get view controllers out of my storyboard is as follows.
UIViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"IdentifierName"];
From here, perhaps you might be able to use this similarly to how it was once done with nibs.
I've been able to reuse a view in the storyboard just by connecting a transition from one tableview into the one I want to reuse.
so my tableview that I want to reuse is pointed to twice.
It sort of works but the problem I'm running into it setting a variable (using instantiateViewControllerWithIdentifier) in my app delegate to my table view that is getting reused.
It seems that if I reuse it, the storyboard is creating 2 instances of my tableview and the one I get with instantiateViewControllerWithIdentifier isn't the one I want.
I'm not really sure if this is the proper way to do it. But I assume many others are doing this somehow. With the custom table cells in storyboard I suspect lots of people want to reuse their views.
For example: We want to reuse the view(include subviews) in storyboard shown below.
The best solution I know so far is clip and paste the view related code to the New Singe View file without losing the information.
Detailed steps are as follows
Step 1: Rename the view we want reuse. Just prepare for step 2.
Step 2: Open storyboard as source code in order to clip the XML code we need
Step 3、4: Search and clip the code we need
Step 4.5(Not needed): Open as Interface Builder to see the view removed
Step 5、6: New XXX.xib and paste the code we clipped just now
Step 7: Important. Insert code<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> to XXX.xib source code.
Warning: Do this before open it as Interface Builder! Otherwise, you will see wrong size and layout waring.
[![step 7][9]][9]
Step 8: New XXX.swift to connect the XXX.xib
[![step 8][10]][10]
Step 9: Add the view anywhere we want
[![step 9][11]][11]
I get warning: "You need at least 10 reputation to post more than 8 links."
Can you support me to upload the remaining 3 screenshots?

issue with tab bar view displaying a compound view

I created a tab bar application, and I make the first tab a table. So I create a tableView controller, and go about setting the class identity of the view controller for the first tab to my tableView controller.
This works fine, and I see the contents of the table filling up the whole screen.
However, this is not what I actually want in the end goal - I would like a compound window having multiple views:
- the aforementioned table
- a custom view with data in it
So what I do is create a nib for this content (call it contentNib), change the tab's class from the tableView controller to a generic UIViewController, and set the nib of that tab to this new contentNib.
In this new contentNib I drag on a tableView and set File's Owner to the TableViewController. I then link the dataSource and delegate to file's owner (which is TableViewController).
Surprisingly this does not work and I receive the error:
**Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x3b0f910'**
This is bewildering to me since the file's owner is the TableViewController, which has been assigned to be both the dataSource and delegate. Does someone have either insight into my confusions, or a link to an example of how to have a compound view include a tableView?
*****update**** I see this in the Apple TableView programming guide:
"Note: You should use a UIViewController subclass rather than a subclass of UITableViewController to manage a table view if the view to be managed is composed of multiple subviews, one of which is a table view. The default behavior of the UITableViewController class is to make the table view fill the screen between the navigation bar and the tab bar (if either are present)."* <----- I don't really get what this is telling me to do though... if someone can explain or point me to an example I'd be much appreciated!
The File's Owner should be a class that you instantiate. For example, in your contentNib file, you should alloc and init a table view, passing the #"contentNib" as the argument to initWithNibNamed:bundle:. It sounds like you're not doing this, as the File's Owner, while set in IB to a UITableViewController, is actually just a UIViewController.
The setting in IB is just to let IB know what outlets and actions to make available; you are responsible for providing the proper object.

UITableView scrolling crashes App

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.