UITableViewCell checkmark change on select - objective-c

Am I correct in thinking that to change the checkmark for "on" to "off", I must change the CellAccessoryType between none and checkmark on the didSelectRowAtIndexPath?
Because I have done this but I have noticed the behaviour is not perfectly identical to like the checkmark cells on the auto lock settings on the iphone.
Or is there some other way checkmarks are meant to be handled?

Keep a property in your view controller called selectedRow, which represents the index of a row that represents the checked item in a table section.
In your view controller's -tableView:cellForRowAtIndexPath: delegate method, set the accessoryType of the cell to UITableViewCellAccessoryCheckmark if the cell's indexPath.row equals the selectedRow value. Otherwise, set it to UITableViewCellAccessoryNone.
In your view controller's -tableView:didSelectRowAtIndexPath: delegate method, set the selectedRow value to the indexPath.row that is selected, e.g.: self.selectedRow = indexPath.row

Another solution:
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *oldIndex = [self.tableView indexPathForSelectedRow];
[self.tableView cellForRowAtIndexPath:oldIndex].accessoryType = UITableViewCellAccessoryNone;
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
return indexPath;
}
And yeah: you don't have to check if oldIndex is nil :)
Swift 3 and later:
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if let oldIndex = tableView.indexPathForSelectedRow {
tableView.cellForRow(at: oldIndex)?.accessoryType = .none
}
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
return indexPath
}

swift:
var selectedCell:UITableViewCell?{
willSet{
selectedCell?.accessoryType = .None
newValue?.accessoryType = .Checkmark
}
}
then:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedCell = self.tableView.cellForRowAtIndexPath(indexPath)
self.mapView.selectAnnotation(self.mapView.annotations[indexPath.row], animated: true)
}

Zyphrax suggested a great solution that worked great for me! And if you need to clear the previous selected row, just use:
[self.tableView reloadData];
in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

For swift
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}

It should be
didHighlightRowAtIndexPath
instead of
tableView:didSelectRowAtIndexPath

Alex answer worked for me only after adding reload table ,
in .h
{
int selectedCell;
}
#property(nonatomic,readwrite)int selectedCell;
in .m
*cellForRowAtIndexPath*
if(indexPath.row == selectedCell)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.selected = YES;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selected = NO;
}
and in anywhere didHighlightRowAtIndexPath or didSelectRowAtIndexPath
self.selectedCell = indexPath.row;
[self.tableView reloadData];

If you want to use your custom image as accessoryType use below code
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIImageView *imgCheckMark = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"select_icon.png"]];
[imgCheckMark setFrame:CGRectMake(self.view.frame.size.width - 30, 25, 14, 18)];
imgCheckMark.tag = 1000+indexPath.row;
NSIndexPath *oldIndex = [self.tblSelectService indexPathForSelectedRow];
[self.tblSelectService cellForRowAtIndexPath:oldIndex].accessoryView = UITableViewCellAccessoryNone;
[self.tblSelectService cellForRowAtIndexPath:indexPath].accessoryView = imgCheckMark;
return indexPath;
}

Related

How to Set TableViewCell Focus colour in tvOS

Default focus color of UITableViewCell is white colour.
How can we change the focus colour of UITableView cell?
You can set this as in the code below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// ...
cell.focusStyle = UITableViewCellFocusStyleCustom;
// ...
return cell;
}
- (void)tableView:(UITableView *)tableView didUpdateFocusInContext:(UITableViewFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if ([context.previouslyFocusedView isKindOfClass:[UITableViewCell class]])
context.previouslyFocusedView.backgroundColor = [UIColor clearColor];
if ([context.nextFocusedView isKindOfClass:[UITableViewCell class]])
context.nextFocusedView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3];
}
It is a swift code. Please convert to Objective-C and try it. It maybe help you.
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
if let nextFoc = context.nextFocusedView as? YourCellName{
nextFoc.backgroundColor = UIColor.redColor()
}
if let prevFocus = context.previouslyFocusedView as? YourCellName{
prevFocus.backgroundColor = UIColor.clearColor()
}
Look it a screen shot.
If you want to maintain the fancy parallax cells you can first set a background view on your custom cell:
self.backgroundView = UIView()
And in your viewcontroller with the tableview do something like this:
func tableView(tableView: UITableView, didUpdateFocusInContext context: UITableViewFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
(context.nextFocusedView as! MyCustomTableviewCell).backgroundView!.backgroundColor = UIColor.purpleColor()
if let prev = context.previouslyFocusedView as? MyCustomTableviewCell {
// Set the color back to whatever it was, in this case I have a black background with black cells
prev.backgroundView?.backgroundColor = UIColor.blackColor()
}
}
If you are using custom tableViewCell, then you can use the delegate method of the tableViewCell as below,
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if (self.focused)
{
[self showHighlightedCellStyle];//set highlighted bg color
}
else
{
[self showNormalCellStyle];//reset the bg color
}
}

Cannot link my table view controller on storyboard to my code

I am writing an app in XCode6. Currently I have "SelectionTableViewController.h" and "SelectionTableViewController.m" such that you could add/remove checkmarks on select. Also, I have a table view controller segue in the storyboard, that is triggered by a static cell in the previous table view controller. I set up the trigger in storyboard so I did not write the code for "prepare for segue" or anything.
I want the cell to be checked by default, so I have done the following:
Changed the view controller to "SelectionTableViewController"
Set the identifier of prototype cell on my storyboard to "SelectionCell"
Changed the cell's background color to orange and accessory to checkmark
Below is my SelectionTableViewController.m:
#implementation SelectionTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:#"SelectionCell"];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO];
self.navigationItem.title = #"Select";
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[[MyStore sharedStore] allCategories] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSString *category = [[[MyStore sharedStore] allCategories] objectAtIndex:section];
return [[[MyStore sharedStore] allNamesForCategory: category] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath(NSIndexPath*)indexPath
{
UITableViewCell *cell=[tableViewdequeueReusableCellWithIdentifier:#"SelectionCell"forIndexPath:indexPath];
// Configure the cell...
NSString *category = [[[MyStore sharedStore] allCategories] objectAtIndex:indexPath.section];
NSArray *items = [[MyStore sharedStore] allNamesForCategory: category];
cell.textLabel.text = [items objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[MyStore sharedStore] allCategories] objectAtIndex:section];
}
#end
My program runs on the simulator, and the items listed in the cells are correct. However the cells does not have the default checkmark on the cell before I do a selection. Nor are the cells orange. I know I can set up the default checkmark and the background color in code easily, but I just want to figure out how to do this in the interface builder, as I would be dealing with the UI a lot when the program is set up.
Hope someone can help me on that as I'm kinda new to iOS programming and this is the first time I have ever used Storyboard. Thanks!
The problem with your implementation is tableView:didSelectRowAtIndexPath will only be triggered by user's interaction only, therefore the initial selection doesn't show. You will need to make tableView aware of the selection state during initialisation too, else de-selection will not function as expected.
class ViewController: UITableViewController {
var selectedItem: Int = 5
func updateSelection(selected: Bool, forCell cell: UITableViewCell) {
cell.accessoryType = selected ? .Checkmark : .None
// update other cell appearances here..
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Note: happens after the initial tableView.reloadData()
// Let tableView know who's selected before we appear, so that tableView is aware of the selection state.
// Doing so will enable tableView to know which cell to deselect after a new selection.
tableView.selectRowAtIndexPath(NSIndexPath(forItem: selectedItem, inSection: 0), animated: true, scrollPosition: .Middle)
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
cell.textLabel?.text = "Item \(indexPath.item)"
// Setup the selection appearance during cell creation
updateSelection(indexPath.item == selectedItem, forCell: cell)
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Keep track of the selection, and update cell appearance
selectedItem = indexPath.item
updateSelection(true, forCell: tableView.cellForRowAtIndexPath(indexPath)!)
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
// As above
updateSelection(false, forCell: tableView.cellForRowAtIndexPath(indexPath)!)
}
}
I've figured it out myself! I deleted the code that registered "Selection Cell". Then I did some minor changes to the cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath(NSIndexPath*)indexPath
{
Static NSString *selectionCell = #"SelectionCell";
UITableViewCell *cell=[tableViewdequeueReusableCellWithIdentifier:selectionCell forIndexPath:indexPath];
// Omit...
}

Adding unknown number of rows to 'Static Cells' UITableView

I have a static table created in Interface Builder with 6 sections all with different amounts of rows.
I now want to add a 7th section with a varying number of rows.
First off, as soon as I uncomment the standard table delegate methods that are inserted by Xcode, I get a crash at self.tableView.tableHeaderView = containerView; where I have added a header to the table.
More importantly i'm getting a crash with the following code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 7;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section==6) {
return 4;
} else {
return [super tableView:tableView numberOfRowsInSection:section];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{/*
if (indexPath.section == 6) {
static NSString *CellIdentifier = #"cellWireless";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
return cell;
}*/
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
How do I correctly leave the existing sections as they are, but add an extra one with a few cells?
To add dynamic cells to a static cells table you have to override every UITableView delegate method that has an indexPath.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
-(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
.
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
int section = indexPath.section;
// if dynamic section make all rows the same height as row 0
if (section == self.dynamicSection) {
return [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
int section = indexPath.section;
// if dynamic section make all rows the same indentation level as row 0
if (section == self.dynamicSection) {
return [super tableView:tableView indentationLevelForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
} else {
return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == self.dynamicSection ) {
return [self.dataListArray count];
} else {
return [super tableView:tableView numberOfRowsInSection:section];
}
}
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
int section = indexPath.section;
int row = indexPath.row;
if (section == self.dynamicSection) {
// make dynamic row's cell
static NSString *CellIdentifier = #"Dynamic Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [self.dataListArray objectAtIndex:row];
return cell;
} else {
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
}
Only once you have every method overridden will your table start to work. For any referencing the static section, just refer to [super].
Darren's answer gave me the idea for what worked for me, however I didn't have to go so far as to implement every single tableView delegate method. You really only need to override numberOfRowsInSection and cellForRowAtIndexPath.
First I defined a static table in Interface Builder with 4 sections, 2 to 4 cells per section. I wanted section 0, 2 and 3 to be static and look exactly as they did in IB, but I wanted section 1 to have a custom number of rows with a custom display in each cell based on an array of values I had.
In the view controller for the static table, override the number of cells returned for your dynamic section, but accept the defaults for all other sections (they'll fall back to the IB values). Do the same for cellForRowAtIndexPath and return the [super] implementation for all sections except section 1.
#implementation myMostlyStaticTableViewController
#synthesize myFancyArray;
- (NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger) section
{
if (section == 1)
return [myFancyArray count]; // the number of rows in section 1
else
return [super tableView:tableView numberOfRowsInSection:section];
}
- (UITableViewCell *) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath
{
// for cells not in section 1, rely on the IB definition of the cell
if (indexPath.section != 1)
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
// configure a task status cell for section 1
MyCustomTableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier:#"myCustomCell"];
if (!cell)
{
// create a cell
cell = [[MyCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"myCustomCell"];
}
cell.myCustomLabel.text = [myFancyArray objectAtIndex:indexPath.row];
return cell;
}
#end
And of course you need a custom cell:
#implementation MyCustomTableViewCell
- (UITableViewCell *) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
// initialize cell and add observers
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (!self)
return self;
self.clipsToBounds = YES;
self.selectionStyle = UITableViewCellSelectionStyleNone;
// configure up some interesting display properties inside the cell
_label = [[UILabel alloc] initWithFrame:CGRectMake(20, 9, 147, 26)];
_label.font = [UIFont fontWithName:#"HelveticaNeue-Medium" size:17];
_label.textColor = [UIColor colorWithWhite:0.2 alpha:1];
[self.contentView addSubview:_label];
return self;
}
#end
I will post answer in Swift, but it should work in Objective-C as well.
In my experience, it was enough to override these methods in UITableViewController:
tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
tableView(tableView: UITableView, indentationLevelForRowAtIndexPath indexPath: NSIndexPath) -> Int
If you want to have custom table view cell in your table view, you need to crate subclass of UITableViewCell also with nib, and register it to your table view.
My whole controller looks like this:
var data = ["Ahoj", "Hola", "Hello"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "reuseIdentifier")
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 1 {
return data.count
}
return super.tableView(tableView, numberOfRowsInSection: section)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! CustomCell
cell.titleLabel.text = data[indexPath.row]
return cell
}
return super.tableView(tableView, cellForRowAtIndexPath: indexPath)
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 44
}
override func tableView(tableView: UITableView, indentationLevelForRowAtIndexPath indexPath: NSIndexPath) -> Int {
return 0
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if indexPath.section == 1 {
print(data[indexPath.row])
}
}
#IBAction func addItem() {
data.append("Item \(data.count)")
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: data.count - 1, inSection: 1)], withRowAnimation: .Left)
tableView.endUpdates()
}
#IBAction func removeItem() {
if data.count > 0 {
data.removeLast()
tableView.beginUpdates()
tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: data.count, inSection: 1)], withRowAnimation: .Left)
tableView.endUpdates()
}
}
I thought I'd add an updated answer based on #Darren's excellent answer. Most of the delegate methods are not required. So, I just added the required ones. You can easily add a custom cell if you wish, even using a nib file. The image shows a static table with 3 sections. The final section is run time dynamic. This is extremely handy. This is working in ios7 BTW.
#define DYNAMIC_SECTION 2
#import "MyTableViewController.h"
#interface MyTableViewController ()
#property (strong, nonatomic)NSArray *myArray;
#end
#implementation MyTableViewController
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
_myArray = #[#"ONE", #"TWO", #"THREE", #"FOUR"];
}
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [super numberOfSectionsInTableView:tableView];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section != DYNAMIC_SECTION) {
return [super tableView:tableView numberOfRowsInSection:section];
}
return [self.myArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section != DYNAMIC_SECTION) {
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
static NSString *id = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:id];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:id];
}
cell.textLabel.text = self.myArray[indexPath.row];
return cell;
}
// required
-(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
int section = indexPath.section;
if (section == DYNAMIC_SECTION) {
return [super tableView:tableView indentationLevelForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
} else {
return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath];
}
}
// Not required
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section != DYNAMIC_SECTION) {
return [super tableView:tableView titleForHeaderInSection:section];
}
return #"some title";
}
I think you are going to have to make your UITableView dynamic. Being that you have an "unknown"number of rows, you will most likely set the delegate method to something like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [someArray count];
}
I discovered something pretty interesting I think and it's more worth an answer than a "comment". I had this static tableView with dynamic rows working, and then it stopped working. The reason is simple. I previously had
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]
and later decided I wanted/needed a Custom Cell that I'd design in my StoryBoard and only set outlets to my UITableView Subclass. So I used the other technique
[super tableView:tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.section]];
The problem here seems to be that this cell gets reused and thus you'll only see one of the cells at a time. Sometime you'll even see none, they'll all be empty! If you scroll you'll see the other cells shortly appearing then disappearing (more like flickering!).
This drove me seriously nuts, until I realized what was (im)possible. Furthermore, do not try to do
[super.tableView dequeueReusableCellWithIdentifier:CellIdentifier]
because as mentioned by other people this always returns nil in a static tableView.
———
So I'm unsure what to do. I guess I'll use the "static prototyped" route, which consists of
Using a Prototype Table View with Cell Identifiers like "31" for Section 3 Row 1. I can then do something like
NSString *identifier = [NSString stringWithFormat:#"%d%d", indexPath.section, indexPath.row];
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
Use Prototype Cells as well for the Headers. I use "Cell1-Header" for the Cell Identifier of the header of the section 1 and then have something like
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
NSString *identifier = [NSString stringWithFormat:#"Cell%d-Header", section];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
return cell.contentView;
}
The basic thing to take here is that you can always starts with a static tableView, but the moment where you realize you're gonna need something dynamic, swap it to Prototype (it will keep your rows although I don't remember what it does with the sections!) and use this KISS technique.
I think I found a better and easier solution, with "fantom" sections or rows in IB.
In case you know the maximum number of cells you would use in section 7(lets say 10), you should set the number of rows to 10, when you configure section 7 in IB.
You aren't forced to use all 10 rows in section, this can be set by
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section.
For example if you return 5 when section == 6(actually the 7th section), then only 5 rows will be displayed.
I admit that's not dynamic in the absolute sense of the word, but perhaps resolves most of the cases.

How to pass changes in CustomCell data from didSelectRowAtIndexPath to heightForRowAtIndexPath

I'm trying to get the cell to collapse if it was already expanded. This code is expanding the row when clicked, but the bool cell.cell_expanded is always FALSE in heightForRowAtIndexPath. I've tried [tableView reloadData] but that didn't do the trick. Any idea?
I read that using heightForRowAtIndexPath can have a huge impact against performance, so if possible I would like to remove this method call. Is it possible to refactor this functionality elsewhere?
Here's what I've got:
CustomCell.h
#interface CustomCell : UITableViewCell
{
UIImageView *cell_arrow;
BOOL cell_expanded;
}
#property (nonatomic, strong) UIImageView *cell_arrow;
#property (nonatomic) BOOL cell_expanded;
FirstViewController.m
-(void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedCellIndexPath = indexPath;
CustomCell *cell = (CustomCell *)[tv cellForRowAtIndexPath:indexPath];
NSString *arrow = nil;
if (cell.cell_expanded == TRUE) {
arrow = #"arrow_down.png";
cell.cell_expanded = FALSE;
}
else {
arrow = #"arrow_up.png";
cell.cell_expanded = TRUE;
}
cell.cell_arrow.image = [UIImage imageNamed:arrow];
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
-(CGFloat)tableView:(UITableView *)tv heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = (CustomCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
if (selectedCellIndexPath != nil) {
if (indexPath.row == selectedCellIndexPath.row) {
NSLog(#"cell_expanded = %d for row %i", cell.cell_expanded, indexPath.row);
CGSize theSize = [cell.cell_body.text sizeWithFont:[UIFont systemFontOfSize:12.0f] constrainedToSize:CGSizeMake(265.0f, 9999.0f) lineBreakMode:UILineBreakModeWordWrap];
if (cell.cell_expanded == TRUE) {
NSLog(#"open it");
return theSize.height + 16;
} else {
NSLog(#"close it");
return 44;
}
}
else {
return 44;
}
}
return 44;
}
Okay, a couple of things here:
1) Just call -reloadData instead of -end/beginUpdates.
2) You obviously didn't really understand how an UITableView works. UITableView reuses its cell. This means that you probably have like 5 cell instances. You should probably read the documents about it. To solve your problem you should save the expanded variable in the instance variable represented by the UITableView.

Why is detailTextLabel not visible?

detailTextLabel is not visible (code below). Can you tell me why?
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
NSString *cellValue = [myListArray objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
cell.detailTextLabel.text = #"Hello "; // This is not visible
cell.image = [myListArrayImages objectAtIndex:indexPath.row];
return cell;
}
The detailTextLabel is not displayed for cells with the UITableViewCellStyleDefault style. init the UITableViewCell with UITableViewCellStyleSubtitle instead and you should see your detailTextLabel.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
Or if you are using Interface Builder, change the Style cell property to Subtitle. :)
In order to solve it programmatically:
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "identifier")
I have used this and it worked for me:
// programming mark ----- ----- ---- ----- ----- ---- ----- ----- ----
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellIdentifier: String = "CellIdentifier"
var cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier) as? UITableViewCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: CellIdentifier)
}
cell!.textLabel!.text = "Title"
cell!.detailTextLabel!.text = "Value"
return cell!
}
Swift 5
You can enable this inside cellForRowAt method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
cell.textLabel?.text = qus[indexPath.row]
cell.detailTextLabel?.text = ans[indexPath.row]
return cell
}
I just want to mention that the wording in the UITableViewCell class reference can be a little bit confusing on this issue:
(After describing each cell type)
"Discussion
In all these cell styles, the larger of the text labels is accessed via the textLabel property and the smaller via the detailTextLabel property."
It might seem that it's saying all of the cell types include a detailTextLabel, but if you carefully read through them it's only the default type that does not have a detailTextLabel.