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...
}
Related
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
}
}
The login and signing up process work well in my code, therefore i have an existing User class, but i'm getting this error Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: ' setObjectForKey: object cannot be nil (key: classname)'
First throw call stack: when i launch the simulator. So i don't understand which class missing or nil, because i've set the User class to query on. (I wanna display the usernames in a list)
#interface Contacts ()
#end
#implementation Contacts
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom the table
// The className to query on
self.parseClassName = #"User";
// The key of the PFObject to display in the label of the default cell style
self.textKey = #"username";
// The title for this table in the Navigation Controller.
self.title = #"Title";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = YES;
// The number of objects to show per page
self.objectsPerPage = 5;
}
return self;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
PFUser *currentUser = [PFUser currentUser];
if (currentUser) {
NSLog(#"Current user: %#", currentUser.username);
} else {
[self performSegueWithIdentifier:#"goLogin" sender:self];
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - Parse
- (void)objectsDidLoad:(NSError *)error {
[super objectsDidLoad:error];
// This method is called every time objects are loaded from Parse via the PFQuery
}
- (void)objectsWillLoad {
[super objectsWillLoad];
// This method is called before a PFQuery is fired to get more objects
}
// Override to customize what kind of query to perform on the class. The default is to query for
// all objects ordered by createdAt descending.
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
[query orderByAscending:#"priority"];
return query;
}
// Override to customize the look of a cell representing an object. The default is to display
// a UITableViewCellStyleDefault style cell with the label being the first key in the object.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"contactCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell
cell.textLabel.text = [object objectForKey:#"text"];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Priority: %#", [object objectForKey:#"priority"]];
return cell;
}
/*
// Override if you need to change the ordering of objects in the table.
- (PFObject *)objectAtIndex:(NSIndexPath *)indexPath {
return [objects objectAtIndex:indexPath.row];
}
*/
/*
// Override to customize the look of the cell that allows the user to load the next page of objects.
// The default implementation is a UITableViewCellStyleDefault cell with simple labels.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForNextPageAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"NextPage";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = #"Load more...";
return cell;
}
*/
#pragma mark - Table view data source
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqual:#"goLogin"]) {
[segue.destinationViewController setHidesBottomBarWhenPushed:YES];
}
}
- (IBAction)logoutButton:(id)sender {
[PFUser logOut];
[self performSegueWithIdentifier:#"goLogin" sender:self]; }
#end
Are you sure the initWithStyle method is called? If you're using storyboards, you need to use initWithCoder instead of initWithStyle.
Update
Also, replace
// The className to query on
self.parseClassName = #"User";
with
// The className to query on
self.parseClassName = #"_User";
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.
I have UIViewController with a button, button opens a firstTableViewController -> tabViewController (with 3 tabs) -> one of the tabs has a secondTableViewController -> UIViewController.
Everything is in storyboard.
In firstTableViewController the prepareForSeque works fine. I don't even have didSelectRow...
But on secondTableViewController the prepareForSeque doesn't fire. So I added didSelectRowAtIndexPath as follows. But still the prepareForSegue doesn't work.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"ShowComment" sender:self];
}
-(void) performSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
[self prepareForSegue:self.storyboard sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//When a row is selected, the segue creates the detail view controller as the destination.
//Set the detail view controller's detail item to the item associated with the selected row.
NSLog(#"test");
if ([[segue identifier] isEqualToString:#"ShowComment"]) {
}
}
I found the issue. I did not have an identifier for the cell. Selected the Prototype Cells in the storyboard, in the attributes inspector -> identifier entered 'Cell1'.
In the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell1";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Note *note = [notes objectAtIndex:indexPath.row];
cell.textLabel.text = note.title;
return cell;
}
voila, it worked.
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;
}