I HAVE READ apple documentation and it's not understandable for such a beginner in Objective-C as me. I'm trying to implement multicolumn UITableView following this link example and it just doesn't work so i need to comprehend how cellForRowAtIndexPath work, cause for me personally this method seems pretty complicated.
1) What does it return? UITableViewCell? But why does it look so odd?
-(UITableViewCell *)tableView:(UITableView *)tableView
What is that? Could you please explain?
2) How does it get called and what is more important how am i to connect it to a certain UITableView??? What if i have two UITableView's named firstTableView and secondTableView and i want them to be different (to perform cellForRowAtIndexPath differently)? How am i supposed to link my UITableViews to this
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
the method accepts NSIndexPath, not UITableView. What am i gonna do?
I'll try and break it down (example from documention)
/*
* The cellForRowAtIndexPath takes for argument the tableView (so if the same object
* is delegate for several tableViews it can identify which one is asking for a cell),
* and an indexPath which determines which row and section the cell is returned for.
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/*
* This is an important bit, it asks the table view if it has any available cells
* already created which it is not using (if they are offScreen), so that it can
* reuse them (saving the time of alloc/init/load from xib a new cell ).
* The identifier is there to differentiate between different types of cells
* (you can display different types of cells in the same table view)
*/
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
/*
* If the cell is nil it means no cell was available for reuse and that we should
* create a new one.
*/
if (cell == nil) {
/*
* Actually create a new cell (with an identifier so that it can be dequeued).
*/
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
/*
* Now that we have a cell we can configure it to display the data corresponding to
* this row/section
*/
NSDictionary *item = (NSDictionary *)[self.content objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:#"mainTitleKey"];
cell.detailTextLabel.text = [item objectForKey:#"secondaryTitleKey"];
NSString *path = [[NSBundle mainBundle] pathForResource:[item objectForKey:#"imageKey"] ofType:#"png"];
UIImage *theImage = [UIImage imageWithContentsOfFile:path];
cell.imageView.image = theImage;
/* Now that the cell is configured we return it to the table view so that it can display it */
return cell;
}
This is a DataSource method so it will be called on whichever object has declared itself as the DataSource of the UITableView. It is called when the table view actually needs to display the cell onscreen, based on the number of rows and sections (which you specify in other DataSource methods).
1) The function returns a cell for a table view yes? So, the returned object is of type UITableViewCell. These are the objects that you see in the table's rows. This function basically returns a cell, for a table view.
But you might ask, how the function would know what cell to return for what row, which is answered in the 2nd question
2)NSIndexPath is essentially two things-
Your Section
Your row
Because your table might be divided to many sections and each with its own rows, this NSIndexPath will help you identify precisely which section and which row. They are both integers. If you're a beginner, I would say try with just one section.
It is called if you implement the UITableViewDataSource protocol in your view controller. A simpler way would be to add a UITableViewController class. I strongly recommend this because it Apple has some code written for you to easily implement the functions that can describe a table. Anyway, if you choose to implement this protocol yourself, you need to create a UITableViewCell object and return it for whatever row. Have a look at its class reference to understand re-usablity because the cells that are displayed in the table view are reused again and again(this is a very efficient design btw).
As for when you have two table views, look at the method. The table view is passed to it, so you should not have a problem with respect to that.
Basically it's designing your cell, The cellforrowatindexpath is called for each cell and the cell number is found by indexpath.row and section number by indexpath.section . Here you can use a label, button or textfied image anything that you want which are updated for all rows in the table.
Answer for second question
In cell for row at index path use an if statement
In Objective C
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(tableView == firstTableView)
{
//code for first table view
[cell.contentView addSubview: someView];
}
if(tableview == secondTableView)
{
//code for secondTableView
[cell.contentView addSubview: someView];
}
return cell;
}
In Swift 3.0
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
if(tableView == firstTableView) {
//code for first table view
}
if(tableview == secondTableView) {
//code for secondTableView
}
return cell
}
Related
I am using a custom cell class in a tableview controller.
When I include a statement in the tableviewcontroller in cellForRowAtIndexPath NSLog(#"method called"): it does not seem to get called.
Is it possible that this method is not called when you have a custom cell?
Edit:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cell for row at index path called");
NSDictionary *item= [self.getItems objectAtIndex:indexPath.row];
//This sets place in storyboard VC
IDTVCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.item = item;
if (cell == nil) {
cell = [[IDTVCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"Cell"];
}
return cell;
}
cellForRowAtIndexPath is not called if no rows are returned.
-tableView:cellForRowAtIndexPath: is not getting called
That is what happened in my case.
It can also not get returned if you reload table on wrong thread and in certain other scenarios.
cellForRowAtIndexPath: not called
However, a custom cell per se does not cause this..
To answer your question - Yes, it is.
There could be n-number of reasons why cellForRowAtIndexPath: is not getting called. This may be because delegate / dataSource is not set or UITableView frame is not set... etc. etc.
You should easily find a solution with more online research and closure look at your code.
I have a custom UITableViewCell with a UITextField (which is linked to the custom cells class). I am trying to access the textField from my VC class.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
menuCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if ([indexpath row] == 2) {
menuCell.nameTextField.delegate = self;
}
return cell;
}
-(void) textFieldDidEndEditing:(UITextField*) textfield
{
}
How do I get the textFields text from textFieldDidEndEditing?
Depending on where you want to access this text depends on how difficult it is.
Want to access the text in cellForRowAtIndex - (very easy)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
menuCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if ([indexpath row] == 2) {
menuCell.nameTextField.delegate = self;
}
NSString * text = menuCell.nameTextField.text;
return cell;
If you want to access the text anywhere in the VC and the menuCell is unique (there is only one of them) - (medium difficult)
In your header file add the custom cell as a class
#class menuCell;
This means you can set it a variable in the interface
menuCell * _menuCell;
Next in cellForRowAtIndex you want to allocate this custom cell
- (UITableViewCell *)tableView:(UITableView *)tableView_ cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == bCustomCellSection) {
if (!_menuCell) {
_menuCell = [tableView_ dequeueReusableCellWithIdentifier:bProfileNameCell];
_menuCell.nameTextField.delegate = self;
}
_menuCell.nameTextField.placeholder = #"Name";
_menuCell.selectionStyle = UITableViewCellSelectionStyleNone;
return _menuCell;
}
...
}
This means that we now have access to the menu cell from anywhere in the VC and can get the text by calling
_menuCell.nameTextField.text
Multiple custom cells with multiple textfields - (tough)
I have never done this but would probably do it one of two ways
a) Create an array and as we are creating the custom cells add a pointer to the textFields to the array each time. We can then access the textField we want from that array
For this method I would add the custom cells to a mutable array defined in the interface
NSMutableArray * cellsArray;
remember to initialise it in viewDidLoad
cellsArray = [NSMutableArray new];
Then in cellForRowAtIndex i would add the cell each time
menuCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
[cellsArray addObject: menuCell];
This obviously depends on how many sections we have. If we have more than one section it gets more complicated again:
Then we would need to add an array for each section to an overall array. This is quite complicated and could have a whole question on its own, there is a good link of how to do this here:
Once you have an array of cells (or an array of arrays of cells) you can call the cell you want based on the indexPath and get the textField
b) Call a pointer to the specific cell we want
menuCell * menuCell = [self tableView:table cellForRowAtIndexPath:indexPath];
and then get the textField from this cell as we did previously.
Remember you can calculate your own indexPath if you want to create one outside of cellForRow:
NSIndexPath * indexPath = [self.tableView indexPathForCell:cell];
This method is pretty good if you want to access a specific cell but a bit cumbersome if you want to access it a lot and keep having to call this code all over your VC
Hope this helps
If you are asking how to get the text from the delegate method textFieldDidEndEditing, then you simply do this:
-(void) textFieldDidEndEditing:(UITextField*) textfield
{
NSString *textFieldText = textfield.text;
}
However, if you have multiple textFields and you want to know what textfield is calling the delegate, you could tag your textField:
[myTextField setTag:indexPath.row]
and then put a if statement in the delegate textFieldDidEndEditing like this:
-(void) textFieldDidEndEditing:(UITextField*) textfield
{
if(textfield.tag == index0) do something..
else if(textfield.tag == index1) do something..
}
I am trying to get a URL from a cell. To do this, I am using NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; and then would like to do something like NSURL *url = self.finalURL[indexPath.row] but because indexPath.row is only for Arrays, this doesn't work. Is there a way to achieve the same thing as indexPath.row but for objects not in an array.
Here is how I am saving the url:
cell.finalURL = self.finalURL;
A cell doesn't have a URL, unless you create a subclass of the cell and add that property to is. Conventionally, you will have an array of objects, strings, dictionaries, etc., and that is your tableView's data source.
If I had an array with three NSURLs in it called myArray that contained google, amazon, and bing, and I wanted to display three cells with the respective labels matching the items in the array, I would implement the following code:
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// we only want a single section for this example
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// this tells the tableView that you will have as many cells as you have items in the myArray array
return myArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// first we try to reuse a cell (if you don't understand this google it, there's a million resources)
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
// if we were unable to reuse a cell
if (cell == nil) {
// we want to create one
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
// here is where we do ANY code that is generic to every cell, such as setting the font,
// text color, etc...
cell.textLabel.textColor = [UIColor redColor];
}
// here is where we do ANY code that is unique to each cell - traits that are based on your data source
// that you want to be different for each cell
// first get the URL object associated with this row
NSURL *URL = myArray[indexPath.row];
// then set the text label's text to the string value of the URL
cell.textLabel.text = [URL absoluteString];
// now return this freshly customized cell
return cell;
}
That, along with the rest of the default tableview code and setting up the array itself, results in the following:
When a user taps on a cell you can access the URL in the array and do something with it like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// first deselect the row so it doesn't stay highlighted
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// get the URL associated with this cell by using indexPath.row as an index on your
// data source array, if you tapped the first cell, it will give you back the first URL in your array...
NSURL *selectedURL = myArray[indexPath.row];
// do something with that URL here...
}
Think of your table view's data source as a bunch of little cubbies. You can create the data source in a million different ways, but once you have it you basically take the items and place them in numbered cubbies. Your table view create's itself based on what's in those cubbies, so to make the first cell it looks in the first cubbie, and so on, and later on when a user selects a cell from that tableview, all the table view does is tell you the cubbie number that was selected, and it's your job to use that information to retrieve the data from that specific cubbie and do what you need to with it. Hope that helps!
i have designed a pop over (tableview style) for ipad. Now like in asp.net i want a option at the end, so that if user clicks it he can enter value into it. Can it be done?If so how? I have played with tableviewcellstyles and got a text box as well but for all options. If anybody has done, can you give me some ideas. Thanks.
EDIT : I should have mentioned i am using Monodevelop to develop this app.
One approach, assuming you're using a Storyboard to create view and set properties:
In your UITableView object properties set "content" property to "Dynamic Prototypes".
Then add an additional Table View Cell to your table from the storyboard editor's UI object list (just like adding another label or button or whatever).
You should now have two TableViewCell objects in your table view. If not, add another.
Give both table view cell objects different identifiers (that is, set property "identifier" for each to different strings).
Set up the first cell to display your normal content.
Set up the second cell to display your unique "last row" layout.
In your code, in cellForRowAtIndexPath return the "main" cell for most rows, but the alternate cell for the last row:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// rows are 0-indexed
if (indexPath.row == myDataSource.count) {
NSString *CellIdentifier = #"AlternateCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// populate contents...
}
else {
NSString *CellIdentifier = #"NormalRow";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// populate contents...
}
}
For that to work, you'll have to tell the TableView that there's one more row than you have actual data for:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return myDataSource.count +1;
}
Say I have a table with 10 static cells in it, is there a way to select a certain cell programmatically?
I've tried this
UITableViewCell *cell = [self.tableView.subviews objectAtIndex:indexPath.row];
but that does not actually return a table cell it seems.
this seems to crash my code
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
Im trying to set the individual heights for the static cells in code. An option would be to make outlets for each individual static cell, but that seems silly.
To access statically created cells, try this:
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
This works for static cells. So, if you're in the...
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
return cell;
}
... delegate, you can access all statically configured cells using the above declaration. From there, you can do what ever you want with "cell".
I had a ViewController that had two UITableViews on it. One of them had cells defined statically, with a Storyboard, and the other had cells defined dynamically using code. Given I was using the same ViewController as delegate for both tables, I needed to prevent new cells from being created where cellForRowAtIndexPath was being called where cells had already been created.
In your case, you need to gain programmatic access to your cells.
Have fun.
Create an #IBOutlet.
This will work even if you re-arrange your static cells programmatically.
You can try this...
UITableViewCell *cell = (UITableViewCell*)[yourTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:rowvalue inSection:0]];
If you need accessing the cell object, then using UITableViewCell method cellForRowAtIndexPath is quite appropriate.
That may either just pass the cell, if it is visible, or call the delegate method cellForRowAtIndexPath (do not mix them up) which you should provide. If that one crashes then dig deeper and investigate the root cause of the crash.
Use table view delegate method
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger height;
if(0 == indexPath.row)
{
height = 44;
}
else
{
height = 50;
}
return height;
}
This is a Swift 2.3. Solution.
The UITableViewController is created in IB.
/*
NOTE
The custom static cells must be
In the IB tableview if one is being used
They also must be updated to be MYCustomTableViewCell
instead of UITableViewCell
*/
import UIKit
class MYCustomTableVC: UITableViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
// NO Nib registration is needed
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath) as! MYCustomTableViewCell
// MYCustomTableViewCell can be created programmatically
// without a Xib file
return cell
}
}