Separate UITableView Controller/Delegate Reference Quandary - iphone

I've been following the guide in the following (StackOverflow question), which has been very helpful, and by completing the steps I've managed to successfully have a UITableView with a separate controller and delegate/datasource.
However, even though everything is working, I'm unable to reference or change anything instance of the UITableview.
For example: let's say the UIViewController for the table is TableViewController and the separate delegate/datasource class is TableDelegate. I have everything hooked up in interface builder as in the previous SO question. In TableDelegate.m, within viewDidLoad, I put the following:
self.navigationItem.leftBarButtonItem = self.editButtonItem;
And nothing changes in the UITableView as it should. This includes pushing UIViewControllers, which also has no effect as if the code wasn't even there, although when I NSLog(), it appears it is being run.
The odd part, however, is that even though that doesn't work, if I set the table's alpha property to 0, it works.
Thanks for any help in advance, let me know if you need any more information to solve the problem.

From your comments, it looks like both TableViewController and TableDelegate are subclasses of UIViewController. If this is the case, make sure both are being added to the navigation stack by pushing them onto a UINavigationController or adding them to a UITabBarController: otherwise, viewDidLoad may not be called.
As an aside, I don't think it's a good idea to have your UITableViewDataSource be a UIViewController subclass when you've already got a UITableViewController to handle that table view.

Related

Programmatic instantiation of custom UITableViewController with cell prototypes created using Storyboard

I have a segue (called "ToSettingsSegue") that pushes a custom view controller (SettingsTableViewController) onto a UINavigationController's stack. SettingsTableViewController has prototype cells ("prototypeSliderCell") set up. It goes without saying that this is setup in a storyboard.
Another section of code pushes SettingsTableViewController onto the UINavigationController's stack programmatically. In SettingTableViewController's -tableView:cellForRowAtIndexPath: method, -dequeueResusableCellWithIdentifier: returns nil when this is done programmatically.
I assume this occurs due to the prototype cells not being available programmatically.
My workaround? Calling -perfromeSegueWithIdentifier:sender: and sending "ToSettingsSegue" and self.
Is there a better solution to this problem? I cannot help but feel that this is dirty.
When you instantiate your SettingsTableViewController in code rather than through a storyboard segue the prototype cell definition is not available since it is only defined in your storyboard.
The "workaround" you describe should work fine, and I see nothing dirty about it. There are plenty of instances where you'll want to programmatically trigger a storyboard segue to happen, and that's what performSegueWithIdentifier:sender: was designed to handle.

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?

Making a tableviewcell scroll up on displaying keyboard

I have a UITableViewCell with UITextField. In the edit mode, when the keyboard comes up, the row I am trying to edit gets overlapped by the keyboard. Ideally the row should scroll up so that I can edit the cell.
I have searched stackoverflow throughly and found several different solutions. Most of them has to do with calculations to move or scroll the view up. Now there is a sample code from apple called TaggedLocations. They have the exact same behavior. And there is no code doing any complex calculations to move the view up.
I also thoroughly checked the IB interface and could not find any fancy thing going on either. If you download and run the code, it beautifully pushes up the row to a perfect position for the edit.
Does anybody know what trick is there in the TaggedLocations project which does this so elegantly? Location of the project:
http://developer.apple.com/library/ios/#samplecode/TaggedLocations/Introduction/Intro.html%23//apple_ref/doc/uid/DTS40008914
For the complex calculations I was referring, look at the following thread for example:
How to make a UITextField move up when keyboard is present?
thanks
This line in RootViewController.m is what causes the scroll:
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
Put that in your cellForRowAtIndexPath: method and it should work.
I figured out the problem.
Initially I had
TabBarController
- UIViewController
-- UINavigationController
--- UITableViewController
- UIViewController
- -UINavigationController
--- UITableViewController
With this kind of a hierarchy the table cells do not roll up when keyboard is displayed. But if I change the hierarchy to the following the rolling up happens just fine without any additional code!
TabBarController
- UINavigationController
-- UITableViewController
- UINavigationController
-- UITableViewController
When I duplicated the Apple code, I somehow messed up the hierarchy. Now on switching to the new hierarchy, it works fine.
No additional code or calculation necessary.
or call viewWillAppear if you have your own hierarchy of controllers... then table views will automatically scroll cells to be visible when keyboard appears.
I had a similar confusion with Apple's sample code TaggedLocations, although in my case I realized that my table view was being managed by a UIViewController subclass as opposed to a UITableViewController subclass, and UITableViewController apparently manages the scrolling for you. It works quite well, so a lot of these complex calculations are unnecessary. I wish I didn't spend so much time trying to figure them out.
I don't see why the hierarchy you describe would matter. As long as the UITableViewController is managing the text fields, it should automatically take care of the scrolling to avoid the keyboard.

NIB File Doesn't Display at Runtime

I am pulling my hair out on this one. I have a NavigationController with two levels of TableViews. Each TableView is in its own NIB file. The first level simply displays a list. Upon selecting a cell, it takes the user to a second level TableView with a more detailed list. It is on this second level TableView that I want to display a search bar (actually I am using a SearchDisplayController as well). I have added it to the TableView because I want the SearchBar to scroll with the table.
Below, I am displaying two screenshots. The first is the second level tableview in InterfaceBuidler. The second is the second level tableview at runtime. For some reason, the SearchBar doesn't display at runtime.
I have tried creating a completely new project from scratch and the same things happens. I don't understand why the SearchBar doesn't display on a NIB pushed on the NavigationController.
Before you ask, if I put the SearchBar on the first level TableView, it shows up just fine. Yes, I am adding it to the TableView itself, so it is a part of the view that should be displayed.
Help! What am I doing wrong?
This is what actually displays after the XIB is pushed...
Okay, I'm going to have to answer my own question. I thought about deleting it, but perhaps this could help someone else. I really pulled my hair out on this one. It wasn't because of anything I was doing wrong so much as a fundamental misunderstanding of how iPhone development works.
In a nutshell, the problem was with this line of code, which instantiated the TableViewController:
self.downloadDetailViewController = [[DownloadDetailTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
When you use XCode to create a new class, you can use the following:
"Add -> New File -> Cocoa Touch Class -> UIViewController subclass -> UITableViewController
subclass AND With XIB for user interface"
The problem is that the NIB has absolutely nothing to do with the UITableViewController until you tell your code to use it. To me, this seems like a bug in XCode or at the very least something that is counterintuitive. When the development environment creates all three files together, it would only make sense that they would work together, but they don't.
Instead, the solution is to modify the line of code as follows:
self.downloadDetailViewController = [[DownloadDetailTableViewController alloc] initWithNibName:#"SecondaryView" bundle:[NSBundle mainBundle]];
Hopefully this can help someone else...
You could also set Nib Name property on your Download View Controller under the Tab Bar Controller, in fact that and coupled with setting the class with your class name will automatically create the view for you and you don't have to manually create it.
I think the problem is that you are adding the searchbar to the tableView, so it is not displaying.
Try to add that searchBar with out adding to the table view, that means move the table view some what down and add the search bar to the view(not the tableview).

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.