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.
Related
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?
I have a UITableViewController, in which I added a UISearchBar as the tableHeaderView using Interface Builder. Then I added a UISearchDisplayController in the nib, and set up all the connections (delegate, searchResultsDelegate, searchContentsController, searchResultsDataSource, all connected to the UITableViewController).
I then implemented all the delegate and data source methods in my code.
It works like a charm, except for a weird bug: sometimes the search results table view won't scroll, and I can see the flash indicator of the main table view behind it. I NSLog'd the searchResultsTableView and apparently it's a subview of the main tableview, and I guess that's the reason behind the touch problems I described earlier.
What's my mistake? Is it possible to use a UITableViewController with UISearchDisplayController at all? If so, how do I set up it in such a way that the results table view doesn't get added as a subview of my main table view?
Update: I found this sample which uses UISearchDisplayController with UITableViewController and apparently the search table view gets added to the main table view in there as well. So now I don't think that's my problem.
The thing is that I can't find any substantial difference between what I'm doing and what that sample is doing. I'm just adding an UISearchBar as a UITableView header in a UITableViewController and adding a UISearchDisplayController to it... It's like iOS is confused between the main table and the search table when I try to scroll. Do you have any ideas?
Update: Added a 200 rep bounty. Please answer only if you know what you're talking about.
It was an extra self.tableView.scrollEnabled = YES which was called as soon as the search data request was finished. Hope it helps anyone with the same problem in the future.
A search display controller manages display of a search bar and a table view that displays the results of a search of data managed by another view controller.
Here the searchDisplaycontroller combines the searchbar and tabelview which showing the result data in the tableview so it won't required separate tableview.
While when you using UISerarchBar then it need to have atleast one UITableView, which can show results in the view. And here you can see the result without even adding the SearchDisplayController to view.
I think you might want to try adding a UISearchDisplayController IBOutlet to your class and connect that to your SearchdDisplayController in the nib file. I am amusing that that searchdisplay controller in your nib automatically connected it self to the default serachdisaplaycontreoller outlet in the inspector.
The search bar controller uses its own table view that is added on top of your own. If you set the delegate and dataSource of the UISearchDisplayController to your UITableViewController, then the table view controller has to make a distinction between the two table views in its delegate methods. If you don't do that properly, this may be causing your bug.
In other words:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.tableView) {
return number of rows in data model;
} else {
perform the search query;
return number of rows in search query;
}
}
You do something similar for all of the delegate and dataSource methods.
I'm not 100% sure that this is what's causing your problem, but without seeing more source it's hard to say.
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.
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?
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).