I couldn't find an example with good explanation how to include UITableView in my project using MVC pattern.
Let's say that at the beginning I have only two files 'MainViewController' (:UIViewController), and 'MainModel' (:NSObject) containing my Array with data for cells.
Where I should have a reference to UITableView object, which file should be delegate for table, ... ?
Your MainViewController will have a view property that you should point to your UITableView instance. You can have any object be your delegate, but usually your delegate is the view controller that controls it, which would be your MainViewController.
That said, there's a subclass of UIViewController called UITableViewController that you should probably be using as the superclass of MainViewController. It has some automatic functionality for controlling UITableViews. In fact, instantiating a UITableViewController (or any its subclasses) will automatically create a UITableView and point to it in its view & tableView properties.
So your MainModel is the model. MainViewController is your controller. The tableView is your view. Add it as a property to your MainViewController and set your MainViewController to be the dataSource and delegate for the tableView. Then when you are populating the tableView, use the data in your MainModel. For instance:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[MainModel sharedInstance] myArray].count;
}
Or create an NSArray property on MainViewController and populate it from MainModel in viewDidLoad or viewWillAppear.
Related
I have a method inside my viewController (MainViewController.m) that accepts a parameter (Let's say it changes the background color of the viewController based on the number):
-(void) methodThatDoesSomething:(int)indexNumber {}
Inside this viewController I have a UITableView, but the delegate of this table is another class (TableDelegateClass.m). So inside this class I have didSelectRowAtIndexPath.
How do I call the "methodThatDoesSomething" from didSelectRowAtIndexPath? If I do it like this:
MainViewController* mainView = [[MainViewController alloc] init];
[mainView methodThatDoesSomething:indexPath.row];
It doesn't work. As another instance of the class is created and it doesn't after the viewController that I'm currently using.
I can easily do it if the delegate of the table is the MainViewController (just with this code inside didSelectRowAtIndexPath) :
[self methodThatDoesSomething:indexPath.row]
but I want to organize better the code and have different classes, one for the table and another for the viewController containing the table.
One simple solution could be to define your own delegate protocol for -(void) methodThatDoesSomething:(int)indexNumber {} so you can set TableDelegateClass delegate to your viewController and then call [delegate methodThatDoesSomething:indexPath.row]; (after checking respondsToSelector).
You can also pass the viewController instance when you create TableDelegateClass but this is a higher coupling level than delegate.
One way to solve your problem is to create a property of type MainViewController in the delegate class and assign the controller instance to it.
But maybe this problem that you are having is an indicator that the tableView delegate should indeed be the MainViewController and not another class?
I have a slide menu. It is implemented using component SWRevealViewController. To implement it I have 1 main UIViewController (VC) - SWRevealViewController. I have menu VC and I have push segues to navigate to different menu's VCs.
For the menu I use prototype cells with custom class for each menu.
My problem is that I need to call unwind segue to go to login VC, using alert view. To do that I try usual method [self performSegueWithIdentifier:#"unwSegReturnToLogin" sender:self]; on positive answer from the alert view (inside custom class for exit cell). I have such method declared in my login VC. I receive error during the compilation:
No visible #interface for 'tvcellExitMenuItem' declares
the selector 'performSegueWithIdentifier:sender:'
I suspect that the problem is that self in my case is table cell and that is not UIViewController.
How to refer parent VC if this is the case?
If not, please tell me where I am wrong in the logic.
A subview shouldn't have to know about its parent viewController. Instead, a common pattern that fits your need is the delegate pattern : define a delegate property & protocol for your cells' class.
// your cell class header might look like this
#class MyCellClass;
#protocol MyCellDelegate
- (void)onCellSelected:(MyCellClass *)cell;
#end
#interface MyCellClass
#property (nonatomic, weak) id<MyCellDelegate> delegate;
#end
For example, if your viewController is also your UITableViewDatasource , then in tableView:cellForRowAtIndexPath: you can set your cell's delegate to self, and call the segue methods in delegate method.
- (void)onCellSelected:(MyCellClass *)cell
{
// retrieve cell indexPath
NSIndexPath *cellIndexPath = [self.tableView indexPathForCell:cell];
// using indexPth, retrieve cell's data
// push segue with data selected
}
Of course, this is only an example, and there are other corrects ways to do that.
When I work with a TableViewController I am able to setup all my content in storyboards. Since I use Static Cells instead of Dynamic Properties for my table view, I find this method much more convenient and easier to implement. I hook up the new UITableView class and simply delete all the delegate methods. Works like a charm as ALL of the content / buttons are being setup in storyboards.
I am trying to accomplish the same result, except this time, I have to work within a ViewController and add a TableView as a subview. Once I hook up the right class, add my outlet connection and setup the following delegates:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MainCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
This works well if my TableView is set to Dynamic Properties :
But when I change the Table View content to Static Cells and delete the delegate method, my app crashes. So, How can I add a table view with Static Cells (That I can manipulate in storyboards) to my ViewController?
Here is what you can do. In your storyboard, create a parent view controller that contains all your non tableview views also, create a UITableViewController. In your parent view controller, create container view, delete the view controller it auto adds, and right click and drag from the container view to your UITableViewController to create an embed segue. Your end result should look something like this:
As far as I know, you can't do this directly. At least in iOS 6, you had to use a UITableViewController when using static cells. One way to use a static table view inside a UIViewController would be to add a container view in IB, and make the embedded controller a table view controller (delete the UIViewController you get automatically, drag in a UITableViewController, and hook it up with the embed segue). You can get a reference to this table view controller from the UIViewController by implementing prepareForSegue:sender:, and using the destinationViewController property (which will point to the table view controller).
You still need to do a couple of things:
Add <UITableViewDataSource, UITableViewDelegate> to your #interface declaration.
Then you can set these also in Interface Builder.
Implement cellForRowAtIndexPath and call the dequeueReusableCellWithIdentifier method to return the cell.
Sorry, I was wrong. The truth is you cannot use static cells without a UITableViewController. Sorry.
A solution could be that you create two controllers and just add the view of the table view controller to your other view controller.
I am addicted to use TableViewController in storyboard whose class directly inherits from UITableViewController and functions like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
are there by default and you just have to implement them. But now I am using a ViewController (inherits from UIViewController) in storyboard and using a UITableView control from Object library into the view controller by drag and drop. Obviously the class I would attach to my storyboard ViewController will be inherited from UIViewController. Now how do I interact with the table cells in the UITableView control in my view controller. Want to implement same row functions above.
Implement UITableViewDataSource and UITableViewDelegate protocols in your view controller .h file.
Have an outlet for the UITableView.
In viewDidLoad, set the tableView's datasource and delegate to self since the viewcontroller implements the protocol.
Then, write the cellForRowAtIndexPath method. It will be getting called.
You have to assign UITableViewDelegate and UITableViewDatasource protocols to your table and implement it. Methods in your question are methods from these protocols and they appears automatically when you use UITableViewController.
I have a UITableView that loads from a NIB file. The controller for the screen is a subclass of UIViewController that conforms to the UITableView delegate protocols. This is the 2nd screen in a stack of views managed by a UINavigationController.
In my viewWillAppear for the offending view I run two NSFetchRequests and update 2 of the 3 sections in my table with results from those NSFetchRequests. At the bottom of viewWillAppear I call
[self.myTable reloadData];
myTable is an IBOutlet to the UITableView for the screen.
For whatever reason the table doesn't reload the data and none of the delegate methods for the table get called when I come back up to it from views deeper in the Navigation Controller hierarchy.
How do I get the table to reload?
Thanks.
Instead of self.tableView, use:
[myTable reloadData];
So if you defined your IBOulet as myTable, then you need to use that name instead.
self.myTable would work if you initialized myTable like in your header:
self.myTable = [[UITableView alloc] initWithFrame:CGRectZero];
Have you also linked the UITableView to data source and delegate outlets in the nib file? You have to link these two in addition to the IBOutlet.