I'm learning Swift. Sometimes I see that Main.Storyboard is used to set up the tableView delegate and dataSource (ctrl+click and so on). Sometimes I see that it's done through coding instead like so:
// create the variable for the tableview
IBOutlet weak var someTableView : UITableView!
// setup delegate and datasource
sefl.someTableView.delegate = self
self.someTableView.datasource = self
I do understand how it works with second way. But it's difficult to realize how it works through Main.Storyboard with no IBOutlet setup.
Thank you for your responses!
It works exactly the same way :)
Let me refresh the principle:
You have a class, provided by Apple in this case, that has to work for a lot of scenario. Apple decided to use a kind of inversion of control, called delegation, where the workflow is inverted (hence the name) : instead of the view controller giving order to the tableView by calling methods on it, it is the tableView that goes and fetch its orders from the controller by calling methods on it.
In order to achieve this kind of IoC (inversion of control), the TableView MUST know the "address" of the object it has to ask for its order. Like you have to know your boss' email address to ask him stuff. So, the UITableView class as a property called dataSource that means to store that address.
Now as a ViewController programmer, you have to set this property to be the address of the view controller that will give the order to that tableview.
2 ways of doing it :
in code : in the view controller, you have a property pointing to the tableview (if linked from the storyboard, it is called indeed an IBOutlet, but doesn't have to be) and you set it's delegate property to self. (meaning 'hey tableView, your boss is myself)
Or you do it in the storyboard, because the graphic template for the tableView let you ctrl+drag from the tableView to the ViewController and set the datasource connection. In this case it will be the storyboard who will have to find the address of the tableView (since it is the one creating it, it's kinda easy) and setting it's delegate property to be the address of the view controller (meaning hey tableView, your boss will be this guy)
Either way, the viewController has to be ready to answer all the question from the TableView, so conforming to the UITableViewDataSource protocol.
the delegate scenario is the same.
It is important to understand how the loading of views works in iOS. Your xib will be translated into the hierarchy of views and they are loaded onto the memory. When you make an IBOutlet of those views, you'll have a reference of that loaded view in your code. If you do not create an IBOutlet, it doesn't mean that the view isn't there. It is there and it is in the memory.
When you set the delegate and the dataSource of a tableView and when the tableView is loaded onto the memory, it sets the delegate and the dataSource of the loaded tableView to the class as specified by you. It doesn't matter if you do not have a reference to it.
Related
I am pretty new to iOS. I have some data that is essentially a tree structure which is fetched from an external database so its depth is not defined at compile time for the app. I would like to display this information using UITableViewControllers inside of a UINavigationController. At this stage, I have embedded a UITableViewController in a UINavigationController in my storyboard, and have written a subclass of UITableViewController to display the first level of the tree. How do I programmatically segue to a new instance of the subclass of UITableViewController that I have created while also creating references for prototype cells I have defined in the storyboard. Is it possible to set up this kind of structure in iOS with UITableViewControllers and a UINavigationController?
Any help would be much appreciated.
Thanks!
In your TableView delegate, put some code into the didSelectRowAtIndexPath: method to create a new instance of your tableViewController subclass. Either pass it whatever data it needs to populate its cells, or possibly use your top level TableView delegate/datasource as the delegate/datasource for every new instance (you will have to keep track of where in your data hierarchy). You can then use self.navigationController pushViewController:animated: to transition to the new tableViewController. If you use the same subclass at each level, the cellForRowAtIndexPath: method will use the same cell reuse identifier (unless you code otherwise), so your prototype cells should be used at every level.
Most of my app is created programmatically, but I have a handful of complex views that are created using Interface Builder and loaded programmatically.
In one view I have a UITableViewController subclass created programmatically, and its tableView property is assigned to the UITableView instance in the XIB, referenced as an IBOutlet. This happens in the XIB's UIViewController's viewDidLoad, and the table view displays correctly.
At one point, I need to change the entire contents of the table view. Ideally, I would like to disconnect that view from the first UITableViewController and connect it to a different one that will feed it different data. Attempting to assign the view to the new controller's tableView property results in "A view can only be associated with at most one view controller at a time!" Assigning the previous controller's tableView to nil before assigning the new one avoids the error, but the table view disappears from the screen.
Honestly, I know the table view instance is usually managed by the UITableViewController. I can't find anything in the docs about setTableView automatically setting the back references for delegate and dataSource in an assigned tableView, so I'm surprised my externally-created tableView works in the first place. In fact, the TVC docs seem to discuss UITableViewController's tableView property as readonly, even though it is not tagged as such.
I'm sure I don't have all your information, but on the surface it seems that you are making this more complicated than it needs to be. Why don't you just use the current UITableViewController and corresponding UITableView and reload it with "new data". Alternatively, just programmatically create another UITableViewController and use that for your new data. Is there a compelling reason to recycle the UITableView?
The answer is no, and here's why:
I checked with the debugger, and the normal setTableView does set the view property on self as well as setting the delegate and dataSource properties on the new incoming tableView. This is not documented by Apple, but it was the assumed behaviour and so it was all well and good.
What I did not realize is that the default setView (from UIViewController) will remove an existing view from its superview before setting the new one. To prevent the "one view controller at a time" runtime error I was setting the old TVC's tableView to nil, which caused the tableView I intended to reuse to be removed from the view hierarchy. To avoid this behaviour, one would have to override setView, which is more complicated than I care to get into for this task.
Reusing views (in this case, a UITableView instance) is not supported by Apple. It is not mentioned in the UITableViewController documentation, but it is mentioned in the UITableViewController documentation regarding the view property. Arguably, UITableViewController's tableView property should be marked readonly by Apple, with its tableView solely managed internally, but there's probably a good reason why its not.
I'm relatively new to Xcode.
I am trying to do this in Xcode's Interface Builder as much as possible. Because I am working with legacy code, Storyboards are not an option.
I'm working with a XIB I'll call DualTVController. It is based on a simple UIViewContoller.
In this XIB I have two UITableViews, the LeftTV and the RightTV. In IB, I am able to assign the delegate/datasource of the LeftTV to the File's Owner, which is the class representation of the XIB. This works fine.
I want to assign the delegate/datasource of the RightTV in IB. So far, I've successfully been able to use the LeftTV for this, but that's just confusing.
Instead, I have created a UITableViewController subclass called RightDelegate, and assigned it as the class of a simple Object I create through the IB. I made it follow the UITableViewDataSource and UITableViewDelegate protocols. This allows me to hook up the delegate/datasource of RightTV to it in IB, but when I try to run it, I get a BAD_ACCESS error.
I have the same delegate code in RightDelegate as I do in LeftTV (which works). Can anyone suggest the problem? Am I responsible for instantiating RightDelegate in code?
Thanks for any help.
note: I know you want to do this in IB, but in this case this is probably best handled in code. setting delegates for a single tableview in IB is simple, but eaisly overlooked. Since the delegates are so important, I usually keep them in code so there less mistakes.
when you are going to use more than one tableview there are a couple of ways to manage the delegates.
a) manage both delegates in your view controller with logic on each delegate callback to determine which tableview is asking (ends up messy)
b) split delegates by leaving one in your vc and putting the other in its own separate controller (somewhat confusing)
c) split delegates by using a separate tableview controller instance for each tableview. This is probably the best choice but it does require 3 controllers - your initial view controller that will init the xib file and then two separate tableview controllers - leftTable, rightTable.
in your main view controller, make sure you setup a property for each tableview:
then include your view controllers
then initialize the tableview controllers and set the delegates
//Example
self.rtv = [[rightTableViewController alloc] init]; //create a strong property for this
self.rtv.delegate = self; //let it talk back to you if needed
self.ltv = [[leftTableViewController alloc] init];
self.ltv.delegate = self;
self.rightTableView.delegate = rtv;
self.rightTableView.datasource = rtv;
self.leftTableView.delegate = ltv;
self.leftTableView.datasource = ltv;
Then just add code in each tableview controller to manage the tableviews.
If you need the tableview controller to call your main view controller then use delegate callbacks.
hope this helps, best of luck.
I followed this discussion: UITableView issue when using separate delegate/dataSource, and even though I found it super informative and useful, I have a follow up question:
So I've got a similar setup where I have a UITableView within the UIView. The UIView is controlled by it's own UIViewController (let's call it MyUIViewController) and also I moved the delegate and datasource for the UITableView into a separate subclass of UITableViewController (say, MyUITableViewController).
Everything works fine with the tableView.
The question is, how do I make a callback from MyUITableViewController to the MyUIViewController? For example, if the user selects a cell under the tableView which triggers didSelectRowAtIndexPath, I need to update something on the UIView which is actually a parent of this UITableView. So I basically need to send a message to MyUITableViewController. How would I do that?
You can use a delegate pattern in this case. I have a similar answer with sample code in this SO . In your case, the parent view controller is MyUIViewController. And the child view controller is MyUITableViewController.
In the Object Library of Xcode, there are two options one can use to create table view - table view and table view controller.
What is the difference between the two and when would they be used ?
A TableViewController is a ViewController with a TableView built in. This will have the delegate methods needed already declared and setup. This VC is already a TableView delegate and datasource. It cannot be resized. Upside is ease of use, downside is very limited flexibility.
A TableView is just that a TableView (subclass of UIView). It can be added to a ViewController and resized, used alongside another view based object, etc. The upside is the flexibility, the downside is that you have to setup the delegate and datasource methods yourself (in my opinion, well worth the time to get the flexibility).
One other note is that when using the new Static TableView cells (part of iOS5), you have to use a TableViewController.
The UITableViewController is a subclass of the UIViewController. It already assumes you will have UITableView as your rootView, so you already have access from the code to a tableView (self.tableView). It implements the UITableViewDataSource and the UITableViewDelegate protocol. It also gives you alot of methods for you to override. It allows you to not depend on XIB file, because you already know what you will have (UITableView as a rootView).
The UITableView is just UIView, normally you will have to comply to the protocols I have referenced above in your UIViewController in order to populate (data source) and work with it (delegate), and you probably have to create an IBOutlet for your UITableView.
On one hand you have speed but you are not as flexible as the other path. On the other you have the opposite.