I'm playing with the cell identifier in a TableviewController, I'm using storyBoard with dynamic cells.
I set 2 cells with 2 identifiers and used a custom row height:
In my viewDidLoad, I just insert some checking information to a mutableArray:
- (void)viewDidLoad
{
[super viewDidLoad];
_arrayCellContent = [[NSMutableArray alloc]init];
for (int i=0; i<15; i++) {
if (i%2 == 0) {
[_arrayCellContent addObject:#"white"];
}
else
[_arrayCellContent addObject:#"Brown"];
}
}
My cellForRow delegate method is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* identifierCellSlim = #"cellSlim";
static NSString* identifierCellFat = #"cellFat";
UITableViewCell *cellSlim = [tableView dequeueReusableCellWithIdentifier:identifierCellSlim forIndexPath:indexPath];
UITableViewCell *cellFat = [tableView dequeueReusableCellWithIdentifier:identifierCellFat forIndexPath:indexPath];
if (indexPath.row%2 ==0) {
cellSlim.textLabel.text = self.arrayCellContent[indexPath.row];
return cellSlim;
}
// Configure the cell...
else {
cellFat.textLabel.text = self.arrayCellContent[indexPath.row];
cellFat.detailTextLabel.text = #"yalla";
return cellFat;
}
}
The final outcome is:
Even cell row heights, not costumed. Why is that? (I know I might work it out with the right delegate method, but I just want to know why the IB not doing his job)
Also in the beginning the cells appear to be in the right color but when I click some of them they will transform to the other cell,
Does clicking the cell change the indexPath ?
Example:
You are dequeuing both cells in the same method call.
You should check for which cell is necessary for the given NSIndexPath and dequeue the relevant cell.
Related
I have a setting screen that is a UITableView with rows of settings. When user open that screen I load stored settings and filled to UITextField etc... Everything was fine.
But there are some of the checkmark settings, I've been trying to check this in programmatically way but is not work, here is my code:
-(void)viewDidAppear:(BOOL)animated{
[self LoadCurrentRecord];
if(_previousValid)
{
NSIndexPath *regionFromData = [NSIndexPath indexPathForRow:_regionAutoCheck inSection:3];
[self.tableView cellForRowAtIndexPath:regionFromData].accessoryType = UITableViewCellAccessoryCheckmark;
}
[self.tableView reloadData];
}
In fact, I can see data can load by check to this category but I didn't see checkmark icon.
Any idea?
You must need to set it in this method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell; // Create Cell
if(_previousValid && indexPath.row == _regionAutoCheck && indexPath.section == 3 )
{
// SET CHECKMARK
} else {
// SET NONE
}
}
Here is how you should work with table- and collectionViews:
Have a collection of dedicated data-elements for each of which a cell will be used to display its data. In your case this would be some kind of Region object.
Implement the table/collectionview datasource methods that return number of cells in section and number of sections based on that collection.
Make sure you configure each cell ONLY in -tableView:cellForRowAtIndexPath: / -collectionView:cellForItemAtIndexPath:, just like PKT's answer. This cell might be reused, so assume any changes you made to this type of cell you need to update here.
When data changes, create an indexPath for the object that changes based on its position in the collection from 1. Then call '-reloadRowAtIndexPath:'/ -reloadItemAtIndexPath for that object. This will cause the tableView/collectionView to call -tableViewcellForRowAtIndexPath:/-`collectionView:cellForItemAtIndexPath:' again. As this method now contains the logic for configuring the cell based on your data-object, everything is in sync.
I can't stress enough how important 1. is. Simplest example is: each section is an array of data objects. If you have multiple sections, you can add each array to another array:
// one section:
NSArray *myData =
#[
#[dataItem1, dataItem2, dataItem3]
#[dataItem4, dataItem5, dataItem6]
];
- (NSUInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
return myData.count;
}
- (NSUInteger) tableView: (UITableView *) tableView numnberOfRowsInSection: (NSUInteger) section
{
NSArray *dataForSection = myData[section];
return dataForSection.count;
}
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath:
{
NSArray *dataForSection = myData[indexPath.section];
MyObject *dataObject = dataForSection[indexPath.row];
NSString * cellID = #"myCellID";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: cellID];
if (nil == cell)
{
// create a cell
// ...
}
// configure the cell based on the data object
if (dataObject.isBlue)
{
cell.contentView.backgroundColor = [UIColor blueColor];
}
else
{
// N.B.! don't forget the else clause, as cells are reused, so this
// cell might be recycled from a cell that was used to display
// the data of a blue data object before.
cell.contentView.backgroundColor = [UIColor blueColor];
}
return cell;
}
Finally, I've found a really simple solution for this case.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == _regionAutoCheck && indexPath.section == 3)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
cell.accessoryType = UITableViewCellAccessoryNone; // for other cases remove it
}
}
UPDATE
After that, add [self.tableView reloadData]; into -(void)viewDidAppear:(BOOL)animated and you will see it works.
That's all.
Thanks all for help
I am trying to update a specific dynamic cells font size from a -(void) method or some way without using cellForRowAtIndexPath. Is this possible?
Thanks
If you know the position of the index, as follows:
First create indexPath by row at position:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
Then access to cell:
MyCell *cell = (MyCell *)[self.tableView cellForRowAtIndexPath:indexPath];
Call
[self.view.tableView reloadRowsAtIndexPaths:#[your_index_path] withRowAnimation:UITableViewRowAnimationAutomatic];
inside your method. that will cause row update
You can not update a cell without having a call to: cellForRowAtIndexPath.
When you want to update font size in a cell, then reload the cell. In your cellForRowAtIndexPath method add a conditional statement to check the indexPath of the cell you want a different font size and edit different font size. Do not forget to add else condition so that when the cell reloads it will make the font size normal for other cells.
However if you want somehow. Then you can use global reference variable to the cell. But this will only work if the cell is displaying on screen.
Here is the example code:
#interface TableViewController ()
{
UITableViewCell *aCell;
}
#end
#implementation TableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *theCell = [tableView dequeueReusableCellWithIdentifier:#"TableViewCellIdentifier" forIndexPath:indexPath];
if(indexPath.row == SPECIFIC_ROW_NUMBER){
aCell = theCell;
theCell.tag = SPECIFIC_ROW_NUMBER;
}else if (theCell.tag == SPECIFIC_ROW_NUMBER){
// If the cell is dequeued
aCell = nil;
theCell.tag = 99999; // some unused row number
}
}
- (void)someMethod {
if(theCell != nil){
aCell.titleLabel.font = NEW_FONT;
}
}
#end
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 have a tableview that has a UISegmentedControl as the tableHeaderView for my tableViewController. I have two arrays as properties that will populate the cells based on which index is selected in the segmentedController. When the two arrays have a different number of items in them, I get an out of bounds exception, which I would expect. When the number of items is the same in each array, I can reload the tableView data and everything works fine. How can I prevent the controller from trying to reuse a cell when the cell isn't needed anymore?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Downloads Cell" forIndexPath:indexPath];
// 1st option selected
if (self.downloadTypeSelector.selectedSegmentIndex == 0) {
if (indexPath.row < self.talkDownloads.count) {
// Configure the cell...
Talk *talk = [self.talkDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = talk.title;
cell.detailTextLabel.text = talk.speaker;
}
// 2nd option selected
} else if (self.downloadTypeSelector.selectedSegmentIndex == 1) {
if (indexPath.row < self.speechesDownloads.count) {
Speech *speech = [self.speechesDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = speech.title;
cell.detailTextLabel.text = speech.speaker;
}
}
return cell;
}
I see that you have a condition that checks downloadTypeSelector.selectedSegmentIndex and uses the respective array accordingly, which is fine.
Do you have the same if else condition in - tableView:numberOfRowsInSection: that checks the selectedSegmentIndex and returns the correct count of the respective array?
If that works then I think checking if indexPath.row < array.count will be obsolete too.
Cheers!
I have created a tableview with multiple prototype (4) cells in order to display different content in each cell but being a newb - I am not clear on how to then code this in the tableviewcontroller to pull data into the multiple cells (just using multiple simple arrays with one data point for each label for now to test it) but I can only get the data to populate in the first cell - and not clear how to code to get the data into the remaining cells. I created 4 separate customtableviewcell files as well. Can someone point me in the right direction on how to code so I can get data into the four separate prototype cells? I need it to be able scroll as it won;t all fit on the the screen which is why I chose table view to do this - but there will only ever be these four sections in the view (with different data depending on what you pushed to get here) should I not be using tableview? if I should be using something different like a view controller with 4 views instead? will it scroll so the user can see all sections? Thanks in advance for any help and suggestions.
You should assign unique Identifier to the each cell in the Storyboard. Then, you can populate appropriate cells like this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section)
{
case 0:
{
MyCustomCell1 *cell = [tableView dequeueReusableCellWithIdentifier:#"cell_1"];
// Configure cell
return cell;
}
case 1:
...
default: return nil;
}
}
Consider creating custom subclasses of UITableViewCell to provide handy IBOutlets.
(1) You need 4 cells. So, prepare 4 custom cells by creating subclass of UITableViewCell. You will know how to create custom cell by search on google.
(2) Set number of sections to 1.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
(3) Set desired height for each cell
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch(indexPath.row)
{
case 0:
{
return 40;
}
case 1:
{
return 50;
}
case 2:
{
return 30;
}
case 3:
{
return 45;
}
default:
{
return 0; // Default case
}
}
}
(4) Set contents of each cell. The data will come from your data source i.e. Array or Dictionary.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"CellIdentifier%d%d",indexPath.section,indexPath.row];
if(indexPath.row == 0)
{
CustomCell1 *objCustomCell1 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(objCustomCell1 == nil)
{
objCustomCell1 = [[CustomCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
objCustomCell1.selectionStyle = UITableViewCellSelectionStyleNone;
}
// Set row specific data here...
NSDictionary *dicObj = [arrYourDataSource objectAtIndex:indexPath.row];
objCustomCell1.myLabel.text = [dicObj objectForKey:#"your key"];
return objCustomCell1;
}
else if(indexPath.row == 1)
{
CustomCell2 *objCustomCell2 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(objCustomCell2 == nil)
{
objCustomCell2 = [[CustomCell2 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
objCustomCell2.selectionStyle = UITableViewCellSelectionStyleNone;
}
// Set row specific data here...
NSDictionary *dicObj = [arrYourDataSource objectAtIndex:indexPath.row];
UIImage *theImage = [UIImage imageNamed:[dicObj objectForKey:#"your key"]];
objCustomCell2.myImageView.image = theImage;
return objCustomCell2;
}
// Do same for remaining 2 rows.
return nil;
}
First do this and then add comment. We will move ahead.