tableview is connected to datasource and delegate - how - ios

i am new to iOS, this may seem like a basic question. I have been working through this tutorial and I have no idea how the tableview is connected to the code. The sample project can be downloaded here.
I was my understanding that you need to extend UITableViewDelegate, UITableViewDataSource in the code, then in the storyboard you can drag from the tableview to them.
But what is perplexing is that the sample project does not extend UITableViewDelegate, UITableViewDataSource at all, therefore, how is the tableview in the story board connected to the code ?

Datasource is used to supply data and delegate is used to supply behaviour. UITableView asks your datasource every time it needs data to display. It provides a lot of flexibility for how you choose to represent your underlying data model. You simply define specific methods to use in order to get table information, and iOS can call them when it needs to know something like the number of rows in a section, or the content of a particular row.
You will probably implement your own delegate mechanism in the future. It is a great design pattern which handles interaction/data transfer between objects.

Because, the basic class adopts from UITableViewController
#interface WTTableViewController : UITableViewController
In your storyboard you just ctrl-drag from tableview to viewController and choose delegate and dataSource.

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDataSource_Protocol/ you should refer this it will help u alot

If you working with TableViewController, UITableViewDelegate and UITableViewDataSource are connected to table automatically. In case you are working with ViewControler which contains TableView, you have to add UITableViewDelegate and UITableViewDataSource like this: #interface myViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>. In new versions of Xcode you can drag delegate and data source to table, and if you want to set delegate programmatically, you can add this two lines of codes:
[tableView setDelegate:self];
[tableView setDataSource:self];
or, equally:
tableview.delegate = self;
tableview.dataSource = self;

In viewcontroller.h file e
declare the delegate and datasource method.
UIViewController<UITableViewDataSource,UITableViewDelegate>
then
connect the delegate and datasource method with viewController so
in viewController.m file
- (void)viewDidLoad {
tableview.delegate=self;
tableview.dataSource=self;
}

In Objective-C/iOS you often implement things by having classes conform to protocols rather than subclassing classes. WTTableViewController conforms to the protocols
UITableViewDataSource: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDataSource_Protocol/
and
UITableViewDelegate: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/
In MainStoryboard.storyboard you can see in the inspector to the right that the TableViewController has a custom class of WTTableViewController
The relevant methods in WTTableViewController.m:
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"WeatherCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
}
Here's some general info about protocols: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithProtocols/WorkingwithProtocols.html
I suggest reading a beginners book or tutorial on iOS to pick up the overall design principles otherwise many things can be quite confusing.

Related

Adding UITableView to ViewController

I haven't developed any iOS apps in a while. I am fine with both swift and Objective-C but what I find different is adding a UITableView to ViewController. Before, I used to add a UITableView to ViewController, add the required datasource methods and the typical UITableViewCell object, and return the cell in cellForRowAtIndexPath:, which would display empty cells depending on the number of rows I return. Now, I did everything the same, but the UITableView is empty and when I scroll I see the lines but not my cell.textlabel.text value, which I set. It seems now I am supposed to add UITableViewCell to the UITableView and remove the
#pragma-mark TableView Datasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_formTitles count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell==NULL)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cell"];
}
cell.textLabel.text=[_formTitles objectAtIndex:indexPath.row];
cell.detailTextLabel.text=#"Kaushik";
return cell;
}
I can't find a simple post online regarding the same.
Can someone post what are the changes in the new iOS 9 in a simple manner?
Thank you
Right..For those of you who are still prefer to add a tableview to a viewcontroller.Here are the steps
In the ViewController drag and drop the tableview.Now instead of the lines which you see in the old Xcode.This time you would see a blank table view with the text " Table View prototype content" in the middle.
We usually create a tableviewcell only we doing anything different like adding more labels or image etc but hereafter it is required to add a tableviewcell in the tableview.Drag and drop a tableview onto the tableview.It will display as protype cells.One can select type of cell here itself as basic,value 1,value 2 or subtitle.Also NEED TO SET THE REUSE IDENTIFIER IN THE STORYBOARD UTILITIES PANEL ON YOUR RIGHT. At the end, you can select the tableview and add the missing constraints.
Implement the typical required datasource methods in your viewcontroller class.
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:#"cell"];
cell.textLabel.text=#"text";
cell.detailTextLabel.text=#"DetailedText";
return cell;
}
The change here is that we dont require the usual if(cell==nil)
{..}
piece of code since we added the prototype cells to the storyboard.
Don;t forget to connect the delegate and datasource of the tableview to the viewcontroller.
This should display the tableview in your viewcontroller but i am getting a space on top of the tableview which i don't know why.Anyway this is how you add a tableview to a view controller using Objective-C language in iOS 9.3.
FYI: Guys if i've missed anything, please mention it in the comments
Thank you
Put tableView to view controller and link it to viewcontroller.
link delegate and datasource of tableview to viewcontroller.
in
viewcontroller.h
#interface ViewController :<UITableViewDelegate, UITableViewDataSource>
in viewWillAppear put
tblService.delegate=self;
tblService.dataSource=self;
and your array elements.
5.Impliment your tableview delegate method and datasource method .
6.be sure you cell identifier equal the one put on storyboard.
this link will help you more to implement your first app and this video.

empty grid of UItableView using storyboard

I try to display objects of an array in a tableview.
This is a single screen app, I'm using storyboard.
When running - the tableview appears as an empty grid. the data of the objects is not displayed. what may cause this?
Having 2 required methods numberOfRowsInSection and cellForRowAtIndexPath I suspect the last one.
The .h file contains this row:
#interface MYViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
I set the delegate & datasource via the code as you suggested. still empty grid.
This is my implementation:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [listRecipes.recipeArray count]; }
-(UITableViewCell )tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString cellIdentifier = #"ListCellIdentifier"; MYCustomListCell* listCell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; NSString* dataString = listRecipes.recipeArray[indexPath.row]; listCell.listName.text = dataString; listCell.imageView.image = [UIImage imageNamed:dataString]; return listCell; }
I try to display data from an array in MVC .
If the array is local - the data is displayed
Can you please advise what to do?
Thank you in advanced.
Always remember to hook up your table's delegate and datasources.
Being a UIElement on a UIViewController that conforms to these two protocols is not enough for Xcode to assume that that UIViewController should be the delegate and/or datasource for the table.
Right click on your tableview, drag from delegate & datasource to your UIViewController.
You can also set these programmatically via:
self.table.datasource = self;
self.table.delegate = self;
And if you're still having trouble after this, you'll have to show us how you're implementing the datasource protocol methods.
Check if your initialisation of tableview delegate and datasource would be before the recipeArray is initialised. In this case you will have to tableView.reload() after the items are added to recipeArray .

Objective-C: How to declare/define method used on tableView

My initial problem of multiple line selection in a UITableView has been answered in this question. But the answer left me at a point where I can't go on on my own, as I am very new to Objective C and iOS development.
Following daxnitros answer, I want to implement the code he/she suggested:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([tableView indexPathIsSelected:indexPath]) {
[tableView removeIndexPathFromSelection:indexPath];
} else {
[tableView addIndexPathToSelection:indexPath];
}
// Update the cell's appearance somewhere here
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
I still need the methods and I thought I can do it for indexPathIsSelected (for example) like this:
#interface MyTableViewController ()
- (BOOL)indexPathIsSelected:(NSIndexPath *)indexPath;
#end
#implementation MyTableViewController
// ...
- (BOOL)indexPathIsSelected:(NSIndexPath *)indexPath
{
BOOL bIsSelected = NO;
// ...
return bIsSelected;
}
#end
But that doesn't work. The error message is: No visible #interface for 'UITableView' declares the selector 'indexPathIsSelected:' Note: The same happens, if I declare the method in the .h file's interface instead.
Now, what baffles me, is this: [tableView indexPathIsSelected:indexPath] is somehow called on the tableView object and I don't even know why. Is that something I have to take into account in my method declaration/definition? I feel really stupid right now, that I can't even write a method by seeing its invocation.
How do I define and declare the method indexPathIsSelected correctly, so I can use it properly?
In your didSelectRowAtIndexPath, the variable tableView is a UITableView.
Your implementation for indexPathIsSelected is in class MyTableViewController, which is probably a UITableViewController.
UITableViewController and UITableView are different classes.
So you can't find the method indexPathIsSelected on UITableView because it's not implemented there, it's implemented on MyTableViewController which is a different class.
SO... I'm going to take an educated guess and assume that didSelectRowAtIndexPath is part of class MyTableViewController. If this is the case, then
[self indexPathIsSelected:indexPath]
may be the answer (i.e. call indexPathIsSelected in self rather than the table view).
The error message you're seeing is the key to the problem. The method indexPathIsSelected is implemented in your custom class MyTableViewController. However, the UITableView you have is apparently still a basic UITableView. At the very least you'll need to go into the storyboard and set the custom class of the table view controller object to MyTableViewController.
To do this, open the storyboard (or nib) and select the table view controller. Then in the identity inspector (on the right hand side, typically), under custom class, select MyTableViewController from the drop down.

Overriding of Tableview Datasource/Delegate Methods

I have one parent class with one tableview.
That class is the delegate and datasource of that tableview as well.
Now I subclassed (derived) that class and made a child class.
I have one tableview in child class too.
Then I defined delegate and datasource functions in that child class, but it overrides parent class tableview data source/delegate methods.
But I want both of them to be separate.
However my Requirement is as Follows :
I want to retain a search bar and side button, on the top of all the viewControllers that search bar includes , a recent searches terms table underneath that.
So i thought of defining parent class for that and subclass other viewControllers from that class.
Am i doing it the right way ?
I assume you are talking about a view controller class. If I understood you right, then you are about to mess it up. Delegation is a way to avoid subclassing. Of course you can subclass the delegate - no problem. But you want a table view in the super class that owns a table in its view. And you want a subclass that has another table in its view plus the table that the superclass owns.
That is not impossible. But from your subclass' point of view, your subclass owns two table views. Even that is possible. Your view controller is the delegate of two tables (regardless of where in the view hierarchy they are declared and instanciated). When you now override the delegate and data source methods theny your subclass must either:
Determine which table it is dealing with/being called from. And then serve both tables appropriately.
Determine wich table it is dealing with/being called from. And then serve "its own" table appropriately and calls [super sameMehtod:withSamePamaters] to ensure that the superclas can still provide the data and server as delegate.
Which of both is smarter depends on the context and what you are about to achieve in detail.
A way of determinnig which table's delegate was called can be done by tagging the table views (do not use 0 as tag) or by comparing the tableView parameter of the delegate method with the corresponding properties (IBOutlets in this case). (In other cases you can compare the sender parameter with the IBOutlets. But tagging is probably easier to understand when reading the code later.)
Let's look at an example of the UITableViewDataSourceDelegat:
Your superclass implements:
#interface MySuperTableViewController:UITableViewController <UITableViewDelegate>
// There will be something in here.
// But it inherits self.tableView from UITableViewController anyway. We leave it with that.
#end
#implementation MySuperTableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// This method creates or re-uses a cell object and sets its properties accordingly.
}
#end
And your subclass:
#interface MySubTableViewController : MySuperTableViewController // no need to declare the delegate here, it is inherited anyway
#property (weak, nonatomic) IBOutlet UITableView *mySecondTableView; // self.table will be used by the superclass already.
#end
#implementation MySubTableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.table) { // This call refers to the one talbe that is managed by super
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
// This method now creates or re-uses a cell object and sets its properties accordingly.
// You may want to check wether tableView == self.mySecondTableView etc.
}
#end
(This comes from scratch, not syntax checked etc. Do not expect this to run properly right away :)
BUT ... please re-consider your class structure. I am afraid you are getting lost in some rather unlogical class hierarchy. There is nothing wrong with having two talbes managed by a common view controller even without this subclassing-thing. And there is nothing wrong with using multiple tables in a view where each of the tables has its own delegate (can be a view controller). Since iOS 5 (or was it introduces with 6) we can use the UIContainerView for that purpose and nicely build it up in IB/storyboard.
try this,
ViewController.h
IBOutlet UITableView *firstTable;
IBOutlet UITableView *secondTable;
ViewController.m
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
if (tableView == firstTable) {
return 1;
}
else if(tableView == secondTable)
{
return 1;
}
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
if (tableView == firstTable) {
return [arrItems count];
} else if(tableView == secondTable)
{
return [arrData count];
}
return 0;
}
etc etc ....

UIViewController, UITableView subclassing - my solution feels dirty

I have a a number of UIViewControllers in my project that implement the UITableViewDataSource and UITableViewDelegate protocols. In Interface Builder I have removed the UIView and replaced it with a subclassed UITableView. In my subclassed UITableView I set a custom backgroundView.
#interface FFTableView : UITableView
#end
#implementation FFTableView
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder]; // Required. setFrame will be called during this method.
self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.png"]];;
return self;
}
#end
This all works fine. I have half a dozen or so of these UIViewControllers that all have subclassed UITableViews and they all draw my background image. My background image is dark, so I need to draw my section headers so that they are visible. Googling I find How to change font color of the title in grouped type UITableView? and I implement viewForHeaderInSection in my subclas.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
...
My viewForHeaderInSection is not called. Of course, when I thought about it for a moment, it makes sense. My UIViewControllers are the objects that implement UITableViewDataSource and UITableViewDelegate, and when I put viewForHeaderInSection in one of my UIViewControllers it works just fine.
But, I have half a dozen of these UIViewControllers, all are subclassed to different classes, implementing different functionality. So I decide to put a class inbetween my subclassed UIViewControllers and UIViewController. This way the common code of setting the appearance of the section headers will be in one spot, not in half a dozen spots.
So I write:
#interface FFViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#end
and in here I implement viewForHeaderInSection:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
if (sectionTitle == nil) {
return nil;
}
...
and change my other subclassed controllers to descend from FFViewController, here's one:
#interface FooDetailsViewController : FFViewController
It seems a little odd, having appearance code in 2 places, but it is better than having copies of the same coded scattered all over the place. In FooDetailsViewController I implement some of the Table Protocol methods, but not viewForHeaderInSection. I also get a warning on FFViewController since it doesn't implement all the protocols (which is on purpose, the child class, FooDetailsViewController in this example, fills out the protocol.)
So what's the question?
Not all of the other subclassed UIViewControllers respond to titleForHeaderInSection, so of course when I run I crash on those view controllers. So I try to see if titleForHeaderInSection is implemented:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (![self respondsToSelector:#selector(titleForHeaderInSection)]) {
return nil;
}
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
if (sectionTitle == nil) {
return nil;
}
And respondsToSelector always returns false. So I can kill that section and just force all my subclasses to implement titleForHeaderInSection but that seems wrong.
What's a good way out of this situation? I'm already dislikeing the solution because:
The code has warnings. (But I can always do How to avoid "incomplete implementation" warning in partial base class to stop them)
My appearance code is in 2 separate classes
I need to implement titleForHeaderInSection in code that doesn't need it since it doesn't have sections headers
(Thanks for reading this far!)
In your respondsToSelector code you have given the wrong name for the selector you are testing for. It should be:
if (![self respondsToSelector:#selector(tableView:titleForHeaderInSection:)]) {
return nil;
}
If you make that change your test should work, and your subclasses can then optionally implement tableView:titleForHeaderInSection: or not.

Resources