Some overrided UITableView methods not being called after app relaunch - ios

All my overrided UITableView methods are being called when I first run the program. But if I terminate the app in the simulator and re-launch from the simulator home screen, some of the overridden methods are not being called (ex. commitEditingStyle) and instead the preset method is called.
Is there a reason why they wouldn't be called after app relaunch but would work after a clear run?
My table code:
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// set toolbar delegate for editing toolbar style
self.aToolbar.delegate = self;
numberOfSections = 1; // for editing: initial number of sections
// for testing
NSLog(#"%#", ptrBookmarks1);
// load bookmarks segment bar with user's most recent segment selections
bookmarksSegmentControl.selectedSegmentIndex = selectedBookmarksSegmentIndex;
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
//allow row selection during editing.
//if the "Add New Song" row is selected we can trigger an insert.
//rather than forcing the using to click the (+) icon directly
self.aTableView.allowsSelectionDuringEditing = YES;
}
- (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 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
// selected table data
NSMutableArray *tableData;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
tableData = ptrBookmarks1;
break;
case 1: // Bookmarks 2 selected
tableData = ptrBookmarks2;
break;
case 2: // My Songs selected
tableData = ptrMySongs;
break;
}
if ([self isEditing]) // the current view is in editing mode, return count + an extra row
return [tableData count] + 1;
else // return count
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
// selected table data
NSMutableArray *tableData;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
tableData = ptrBookmarks1;
break;
case 1: // Bookmarks 2 selected
tableData = ptrBookmarks2;
break;
case 2: // My Songs selected
tableData = ptrMySongs;
break;
}
//if number of rows is greater than the total number of rows in the data set
//and this view is in editing mode.
//Initialize the cell for "Add New Song"
//there will be an extra row once SetEditing: is called
if(indexPath.row >= tableData.count && [self isEditing]){
cell.textLabel.text = #"Add New Song";
//cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // display arrow accessory
}
else {
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
//cell.accessoryType = UITableViewCellAccessoryNone; // hide arrow accessory
}
return cell;
}
//VIEW CONTROLLER METHOD: IMPORTANT
//this is a method of the view controller
//if we use apple's editing button as follows:
//self.navigationItem.rightBarButtonItem = self.editButtonItem
//then this method will be called automatically for us.
//if we are using a button callback or similar method,
//then we need to call setEditing: manually on the view
-(void) setEditing:(BOOL)editing animated:(BOOL)animated{
//wrap our code in an if statement
//only run the code if we are not swipe deleting a row.
//if we were called due to a swipeDelete action, ignore it
if(isSwipeDeleting == NO){
//call parent
[aTableView setEditing:editing];
[super setEditing:editing animated:animated];
// selected table data
NSMutableArray *tableData;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
tableData = ptrBookmarks1;
break;
case 1: // Bookmarks 2 selected
tableData = ptrBookmarks2;
break;
case 2: // My Songs selected
tableData = ptrMySongs;
break;
}
//if editing mode
if(editing){
//batch the table view changes so that everything happens at once
[self.aTableView beginUpdates];
//for each section, insert a row at the end of the table
for(int i = 0; i < numberOfSections; i++){
//create an index path for the new row
NSIndexPath *path = [NSIndexPath indexPathForRow:tableData.count inSection:i];
//insert the NSIndexPath to create a new row. NOTE: this method takes an array of paths
[self.aTableView insertRowsAtIndexPaths:#[path] withRowAnimation:UITableViewRowAnimationAutomatic];
}
//animate the changes now
[self.aTableView endUpdates];
}else{
//batch the table view changes so that everything happens at once
[self.aTableView beginUpdates];
//for each section, insert a row at the end of the table
for(int i = 0; i < numberOfSections; i++){
//create an index path for the new row
NSIndexPath *path = [NSIndexPath indexPathForRow:tableData.count inSection:i];
//insert the NSIndexPath to create a new row. NOTE: this method takes an array of paths
[self.aTableView deleteRowsAtIndexPaths:#[path] withRowAnimation:UITableViewRowAnimationAutomatic];
}
//animate the changes now
[self.aTableView endUpdates];
}
}
}
//DELEGATE METHOD:
//this method will be called for every row and allows us to set the
//editing syle icon(Delete,Insert)
-(UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
// selected table data
NSMutableArray *tableData;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
tableData = ptrBookmarks1;
break;
case 1: // Bookmarks 2 selected
tableData = ptrBookmarks2;
break;
case 2: // My Songs selected
tableData = ptrMySongs;
break;
}
// Detemine if it's in editing mode
if (self.editing) {
//use the + icon(add icon) on row
//if this is the additional row created in setEditing:animated:
if(indexPath.row >= tableData.count){
return UITableViewCellEditingStyleInsert;
}
else {
//use the delete icon on this row
return UITableViewCellEditingStyleDelete;
}
}
else
return UITableViewCellEditingStyleNone;
}
//handle the deletion/insertion
//this method is called when the delete or insert icon has been press.
//we should update our dataSource by inserting or removing the selected INDEX
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// selected table data
NSMutableArray *tableData;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
tableData = ptrBookmarks1;
break;
case 1: // Bookmarks 2 selected
tableData = ptrBookmarks2;
break;
case 2: // My Songs selected
tableData = ptrMySongs;
break;
}
if (editingStyle == UITableViewCellEditingStyleDelete) { // the delete icon was clicked
// delete pre-test
NSLog(#"Array before delete: %#", tableData);
NSLog(#"Directory before delete: ");
[self readDirectoryContents];
// if deleting a song or its pages that's in view, switch view to previous MySong if exists, otherwise About page/a blank page
////////insert code here////////
// remove file and its annotations from directory (if deleting from My Songs)
if (tableData == ptrMySongs) {
[self deleteImportedSongsheet:tableData[indexPath.row]];
}
//remove row from tableData
[tableData removeObjectAtIndex:indexPath.row];
//remove the row in the tableView
[self.aTableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
// delete post-test
NSLog(#"Array after delete: %#", tableData);
NSLog(#"Directory after delete: ");
[self readDirectoryContents];
[self.aTableView reloadData];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) { // the insert icon was clicked
/*
//add a new row to the datasource
[tableData addObject:#"New Icon"];
//insert a row in the tableView because the plusIcon was clicked.
[self.aTableView insertRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];*/
self.editing = false; // turn off editing
// ---SEGUE TO SONG IMPORT OPTIONS TABLE---
SongImportView *songImportView =
[[SongImportView alloc]
initWithNibName:#"SongImportView" bundle:nil];
// forward the My Songs array
songImportView.ptrMySongs = ptrMySongs;
// set delegate
songImportView.delegate = self;
// push SongImportView
[self.navigationController pushViewController:songImportView animated:YES];
[SongImportView release];
}
}
//if we are in editing mode we do not want to perform Seque Transition
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"MyDetailView"] && [self isEditing]) {
return NO;
}
return YES;
}
//this method is called when the user swipes to delete a row.
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath{
isSwipeDeleting = YES;//user just swipe to delete a row
}
//when the user cancel the swipe or click the delete button
//this method is call
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath{
isSwipeDeleting = NO;//swipe to delete ended. No longer showing the DELETE button in cell
}
// 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 NO;
//return YES;
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
// MUST CHANGE canMoveRowAtIndexPath TO ENABLE THIS
NSString *item;
// selected table data
NSMutableArray *tableData;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
tableData = ptrBookmarks1;
break;
case 1: // Bookmarks 2 selected
tableData = ptrBookmarks2;
break;
case 2: // My Songs selected
tableData = ptrMySongs;
break;
}
item = [[tableData objectAtIndex:fromIndexPath.row] retain];
[tableData removeObjectAtIndex:fromIndexPath.row];
[tableData insertObject:item atIndex:toIndexPath.row];
[item release];
}
#pragma mark - Table view delegate
//DELEGATE METHOD:
//the user selected a row
//In order for the user to perform an INSERTION action on a row,
//they have to click the + icon icon. We can implement this method
//so that they can click anywhere on the "Add New Song" to add a new row
//tableView.allowsSelectionDuringEditing = YES; must be set
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//deselect the selected row with animatiion
[self.aTableView deselectRowAtIndexPath:indexPath animated:YES];
// selected table data
NSMutableArray *tableData;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
tableData = ptrBookmarks1;
break;
case 1: // Bookmarks 2 selected
tableData = ptrBookmarks2;
break;
case 2: // My Songs selected
tableData = ptrMySongs;
break;
}
//if the selected row was the "Add New Song" row call tableView:commitEditingStyle:
//to add a new row
if (indexPath.row >= tableData.count && [self isEditing]) {
[self tableView:tableView commitEditingStyle:UITableViewCellEditingStyleInsert forRowAtIndexPath:indexPath];
}
else { // otherwise do regular table item selection
[self.delegate didTapBookmarksTable:[tableData objectAtIndex:indexPath.row]];
}
}
// to scroll the table to the top row that was last viewed
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:YES];
NSIndexPath *iP;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
if ([ptrBookmarks1 count] > 0) { // make sure that there is something in the table before scrolling to it, otherwise will crash
iP = [NSIndexPath indexPathForRow:bookmark1listPositionRow inSection:bookmark1listPositionSection];
[aTableView scrollToRowAtIndexPath:iP atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
break;
case 1: // Bookmarks 2 selected
if ([ptrBookmarks2 count] > 0) { // make sure that there is something in the table before scrolling to it, otherwise will crash
iP = [NSIndexPath indexPathForRow:bookmark2listPositionRow inSection:bookmark2listPositionSection];
[aTableView scrollToRowAtIndexPath:iP atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
break;
case 2: // My Songs selected
if ([ptrMySongs count] > 0) { // make sure that there is something in the table before scrolling to it, otherwise will crash
iP = [NSIndexPath indexPathForRow:mysongslistPositionRow inSection:mysongslistPositionSection];
[aTableView scrollToRowAtIndexPath:iP atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
break;
}
}
// remember the top row that was last viewed
- (void)viewDidDisappear:(BOOL)animated {
NSIndexPath *ip;
switch (bookmarksSegmentControl.selectedSegmentIndex) {
case 0: // Bookmarks 1 selected
if ([ptrBookmarks1 count] > 0) { // make sure that there is something in the table before scrolling to it, otherwise will crash
ip = [[aTableView indexPathsForVisibleRows] objectAtIndex:0];
bookmark1listPositionSection = ip.section;
bookmark1listPositionRow = ip.row;
}
break;
case 1: // Bookmarks 2 selected
if ([ptrBookmarks2 count] > 0) { // make sure that there is something in the table before scrolling to it, otherwise will crash
ip = [[aTableView indexPathsForVisibleRows] objectAtIndex:0];
bookmark2listPositionSection = ip.section;
bookmark2listPositionRow = ip.row;
}
break;
case 2: // My Songs selected
if ([ptrMySongs count] > 0) { // make sure that there is something in the table before scrolling to it, otherwise will crash
ip = [[aTableView indexPathsForVisibleRows] objectAtIndex:0];
mysongslistPositionSection = ip.section;
mysongslistPositionRow = ip.row;
}
break;
}
}

Related

Unable to add and delete rows simultaneously

I am following a tutorial,where I can expand the table view by adding some sub-cells and collapse the table by removing the sub-cells. I am trying to change how the expand operation should execute. When I tap on a row,it expand and shows the sub-cells,and when I tap on other row,the previous expanded row should close. I am not able to do this . I tried the following but I couldnt make the one row expandable at a time,and the other row should close when one expands.
Note: This code works fine,but to collapse the row,we need to tap twice on the same row. I am trying to collapse when other parent is tapped.
Here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Node *node = [self nodeForIndexPath:indexPath];
// differenet table view cell prototypes for different node levels
UITableViewCell *cell;
if(node.level == 0)
{
cell = [tableView dequeueReusableCellWithIdentifier:#"level1cell" forIndexPath:indexPath];
}
else
{
cell = [tableView dequeueReusableCellWithIdentifier:#"level2cell" forIndexPath:indexPath];
}
// set the nodes title as row text
cell.textLabel.text = node.title;
// attach the nodeId for later lookup when selected
cell.tag = node.nodeId;
// Configure the cell...
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Node *node = [self nodeForIndexPath:indexPath];
// NSLog(#"node id is %ld level is %ld and indexpath.row is %d",(long)node.nodeId,(long)node.level,indexPath.row);
node.expanded = !node.expanded;
if (node.level==0) {
NSLog(#"you tapped parent");
//now check other parents are expanded or not
}else{
NSLog(#"you tapped child");
}
//insertion always happen after deletion
// NSLog(#"you touched at %#,index row is %d",indexPath,indexPath.row);
if(node.expanded )
{
// add n rows
NSMutableArray *indexPaths = [NSMutableArray array];
for(NSInteger i=indexPath.row; i< indexPath.row+node.subNodes.count; i++)
{
[indexPaths addObject:[NSIndexPath indexPathForRow:i+1 inSection:0]];
}
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
else
{
//you store the node refe after row is expanded
// delete n rows
NSMutableArray *indexPaths = [NSMutableArray array];
for(NSInteger i=indexPath.row; i< indexPath.row+node.subNodes.count; i++)
{
[indexPaths addObject:[NSIndexPath indexPathForRow:i+1 inSection:0]];
}
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
}
#pragma mark - Private helper
-(Node*) nodeForIndexPath:(NSIndexPath*)indexPath
{
int idx = 0;
for(Node *node in _nodes)
{
if(idx == indexPath.row)
{
return node;
}
idx++;
if(node.expanded)
{
for (Node *subNode in node.subNodes)
{
if(idx == indexPath.row)
{
return subNode;
}
idx++;
}
}
}
return nil;
}
KMAccordionTableViewController can helps you?
https://github.com/klevison/KMAccordionTableViewController
Hm... If your current code is working, but you need to tap twice to collapse the open/selected row, perhaps it's because didDeselectRowAtIndexPath: is being called during that first tap in place of didSelectRowAtIndexPath: in order to deselect the selected row. I'd recommend configuring your tableview to allow for multiple selection so that didSelectRowAtIndexPath: is called every time, ex:
- (void)viewDidLoad {
[super viewDidLoad];
tableView.allowsMultipleSelection = YES;
}

How to add cells to custom sections in UITableView

I have three sections in my UITableView and when I create objects to go into this UITableView, I want to specify which section I want it to go into. I do this by adding it to one of three arrays. Note all objects are initially added to a holder NSMutableArray called objects.
for (Profile *p in self.objects) {
if ([p.type isEqualToString:#"eol"]) {
[self.eolobjects addObject:p];
}
else if ([p.type isEqualToString:#"ae"]) {
[self.aeobjects addObject:p];
}
else if ([p.type isEqualToString:#"mw"]) {
[self.mwobjects addObject:p];
}
The problem arises when I want to segue to a detailViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Profile *object = self.objects[indexPath.row];
[[segue destinationViewController] setDetailItem:object];
}
}
Because of the line:
Profile *object = self.objects[indexPath.row];
If I click (for example)the first object in any section, I will always create an object of the item at the first index of the objects array, not the object in the first index of the array which populates the section I am clicking in. The same is true if I change self.objects to any of my three other arrays.
Is there an easier way to add cells to sections in a UITableView or is there a way to fix my problem? Thanks
My data source methods look like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 3;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section ==0) {
return #"Evolution of Life";
}
else if (section==1){
return #"Active Earth";
}
else if (section==2){
return #"Mineral Wealth";
}
return #"";
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
switch (section) {
case 0: return self.eolobjects.count; break;
case 1: return self.aeobjects.count; break;
case 2: return self.mwobjects.count; break;
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (indexPath.section == 0) {
Profile *profile = self.eolobjects[indexPath.row];
cell.textLabel.text = [profile name];
return cell;
}
else if (indexPath.section ==1){
Profile *profile = self.aeobjects[indexPath.row];
cell.textLabel.text = [profile name];
return cell;
}
else if (indexPath.section ==2){
Profile *profile = self.mwobjects[indexPath.row];
cell.textLabel.text = [profile name];
return cell;
}
return cell;
}
You have two solutions
Try this
Profile *object =nil;
switch (indexPath.) {
case 0: object = self.eolobjects[indexPath.row]; break;
case 1: object = self.aeobjects[indexPath.row]; break;
case 2: object = self.mwobjects[indexPath.row]; break;
}
Or you can put all the 3 arrays in one array and access them easily like this allObjects[indexPath.section][indexPath.row]

Static Cells - Hiding using the heightforRowatIndexPath method

Hi guy I'm trying to hide some of the rows in section 1 (Second section) depending on what type of feedback the user has selected:
I'm using static cells but at the moment nothing is being removed when I select one of the options in the TypeVC. There are no errors at the moment but having a guess I think its something to do with the logical operators I'm using in the switch statement. Sorry for dumping my code but as I'm very new to IOS I don't know what exactly you guys would need to see.
[1]
if (variable == (1|2|3)){}
I'm used to Java and I use this kind statement quite frequently as it saves writing. Is this how to do it in objective-c?
[2]
Where and how have I gone wrong here trying to get the cells to dissapear?
FeedbackTableViewController:
#import "FeedbackTableViewController.h"
#import "TypeTableViewController.h"
#interface FeedbackTableViewController ()
#property NSInteger index;
#end
#implementation FeedbackTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated{
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSIndexPath *) tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Type: %i",_type);
if (indexPath.section == 0 && indexPath.row == 0)
[self performSegueWithIdentifier:#"showTypeVC" sender:self];
return indexPath;
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
TypeTableViewController *tvc = [segue destinationViewController];
tvc.indexchoosen = _index;
}
//- (UITableViewCell *)tableView:(UITableView *)tableView
// cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
// UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
//
// if (indexPath.row==0) cell.textLabel.text = _typeString;
// else if (indexPath.row)
//
// return cell;
//}
- (CGFloat) tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"section: %i row:%i",indexPath.section, indexPath.row);
if (indexPath.section == 1) {
switch (_type) {
case 0:
if (indexPath.row==(2|3|4))return 0;
break;
case 1:
if (indexPath.row==(0|1|4))return 0;
break;
case 2:
if (indexPath.row==(0|1|2|3))return 0;
break;
case 3:
return 0;
break;
case 4:
return 0;
break;
case 5:
return 0;
break;
default:
return 0;
break;
}
}
return 43;
}
- (IBAction)unwindtypeVC:(UIStoryboardSegue *)segue { }
#end
TypeTableViewController:
#import "TypeTableViewController.h"
#import "FeedbackTableViewController.h"
#interface TypeTableViewController ()
#end
#implementation TypeTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_typeoptions = #[#"Routing Issues",
#"Wrongly Labelled Location",
#"Missing Location",
#"Comment on Useability",
#"Suggestions",
#"General Feedback"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 6;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.typeoptions[indexPath.row];
return cell;
}
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
_indexchoosen = indexPath.row;
return indexPath;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
NSString *string = _typeoptions[_indexchoosen];
FeedbackTableViewController *fvc1 = [segue destinationViewController];
fvc1.typeString.text = _typeoptions[_indexchoosen];
fvc1.type = _indexchoosen;
}
#end
I'm open to better ideas on achieving what I want to achieve also so if you would consider telling me a more efficient way of doing this I would be grateful. I know delegates might be an option however I'm not confident with them yet and would thought this would be easier for me.
For [1], try this and see it yourself:
int test = 3;
if(test == (1 | 2))
NSLog(#"_MEH_");
Since it's bitwise OR operation, 0010 | 0001 equals to 0011, which is equal to 3. Hence, I would not advise you to use an operation like that. (If that's not intentional, of course).
For [2], you should use deleteRowsAtIndexPaths:withRowAnimation: call for UITableView in order to delete rows.
For example;
[self.tableView beginUpdates];
NSIndexPath* rowToDelete = [NSIndexPath indexPathForRow:0 inSection:0]; // For showing purposes only.
NSArray* indexArray = [NSArray arrayWithObjects:rowToDelete, nil];
[self.tableView deleteRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationMiddle];
[self.tableView endUpdates];
Also, don't forget to update your data source accordingly. You may want to remove some objects from
self.typeoptions
array.
P.S: Another addition, you should also change tableView:numberOfRowsInSection: since there will be less rows than 6.
I actually managed to use this method of changing the row heights to 0.
In order to do it effectively I had to to remove the placeholder/any initial text in the rows that I didn't want shown. This required some storyboard connections which you will see named as _feedbackText _startLocation etc. When a user selected a new row, they would perform a segue to the original feedback form and therefore the viewDidAppear was called. I used this to call the [self.tableView reloadData]. Originally the change in the variable _type would not actually change anything but the heightForRowAtIndexPath is recalled when the data is reloaded.
I'm sure that using the delete row at indexPath would have worked also but I wanted to store the information that the user may have typed before they changed the type of feedback.
The New Method:
- (CGFloat) tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSInteger i = indexPath.row;
if (indexPath.section == 1) {
switch (_type) {
case 0:
_startLocation.placeholder = #"Start Location:";
_destination.placeholder = #"Destination:";
_locationName.placeholder = #"";
_correctName.placeholder = #"";
_missingLocation.placeholder = #"";
if (i==2||i==3||i==4) return 0;
break;
case 1:
_startLocation.placeholder = #"";
_destination.placeholder = #"";
_locationName.placeholder = #"Location Name:";
_correctName.placeholder = #"Correct Name:";
_missingLocation.placeholder = #"";
if (i==0||i==1||i==4)return 0;
break;
case 2:
_startLocation.placeholder = #"";
_destination.placeholder = #"";
_locationName.placeholder = #"";
_correctName.placeholder = #"";
_missingLocation.placeholder = #"Missing Location:";
if (i==0||i==1||i==2||i==3)return 0;
break;
case 3:
return 0;
break;
case 4:
return 0;
break;
case 5:
return 0;
break;
default:
_startLocation.placeholder = #"";
_destination.placeholder = #"";
_locationName.placeholder = #"";
_correctName.placeholder = #"";
_missingLocation.placeholder = #"";
if (i==0||i==1||i==2||i==3||i==4)return 0;
break;
}
} else if (indexPath.section==2 && indexPath.row==2) return 240;
else if (indexPath.section==0 && indexPath.row==0) return 40;
return 30;
}
This will essentially hide but not get rid of the information in the text fields. This is very useful if you want to the keep any information the user typed in.
I hope this helps anyone trying to hide rows in a grouped static table view controller.

Assertion Failure when updating uitableview

I get this assertion when trying to update my tableview when clicking on a section header.
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2839.5/UITableView.m:1264
I am just trying to hide and show custom cells whenever I click on a section header view.
code works fine if I replace the update code with reload data. but that's not smooth :(
- (void)noteSectionHeader:(UTNoteSectionHeader *)noteSectionHeader sectionTapped:(NSInteger)section
{
UTNoteItem* noteItem = self.notes[section];
BOOL alreadySelected = noteItem.selected;
if (alreadySelected) {
self.selectedSection = NSNotFound;
[self setSelected:NO forSection:section];
}
else {
self.selectedSection = section;
[self setSelected:YES forSection:section];
}
[self updateSections];
}
- (void)setSelected:(BOOL)selected forSection:(NSInteger)section
{
UTNoteItem* noteItem = self.notes[section];
noteItem.selected = selected;
for (UTNoteItem* tmpItem in self.notes) {
if (tmpItem != noteItem) {
tmpItem.selected = NO;
}
}
}
- (void)updateSections
{
NSMutableArray* deletePaths = [[NSMutableArray alloc] init];
NSMutableArray* addPaths = [[NSMutableArray alloc] init];
for (UTNoteItem* item in self.notes) {
if (item.selected) {
[addPaths addObject:[NSIndexPath indexPathForRow:0 inSection:[self.notes indexOfObject:item]]];
}
else {
[deletePaths addObject:[NSIndexPath indexPathForRow:0 inSection:[self.notes indexOfObject:item]]];
}
}
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:deletePaths withRowAnimation:YES];
[self.tableView insertRowsAtIndexPaths:addPaths withRowAnimation:YES];
[self.tableView endUpdates];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.notes.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
UTNoteItem* itemNote = self.notes[section];
if (itemNote.selected) return 1;
return 0;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40;
}
EDIT:
Here is my new implementation:
-
(void)noteSectionHeader:(UTNoteSectionHeader *)noteSectionHeader sectionTapped:(NSInteger)section
{
/* Check if a section is opened */
if (self.selectedSection != NSNotFound) {
/* A section is open, get the item */
UTNoteItem* theItem = self.notes[self.selectedSection];
/* if the item is the section opened, close it */
if (self.selectedSection == section) {
theItem.selected = NO;
self.selectedSection = NSNotFound;
}
/* The item is not the section, so open it, and close the previous item */
else {
theItem.selected = YES;
UTNoteItem* prevItem = self.notes[self.selectedSection];
prevItem.selected = NO;
self.selectedSection = section;
}
}
/* Nothin is open, just open the section */
else {
self.selectedSection = section;
UTNoteItem* openItem = self.notes[self.selectedSection];
openItem.selected = YES;
}
/* Reload the selected section.. this will not reload the other sections? */
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationAutomatic];
}
I have had a similar problem, however I perform a reload like so:
- (void)noteSectionHeader:(UTNoteSectionHeader *)noteSectionHeader sectionTapped:(NSInteger)section
{
//check our action
if(<will hide section>) {
//hide section
<some action>
} else {
//show section
<some action>
}
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
}
and it reloads again differently with a forced update:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger nRows = 0; //no rows when closed
if(<check section is open>) {
nRows +=<number of data items shown>;
}
return nRows;
}
Where the indexpath.section is the section I wish to hide or show. it is smooth and stable.
Deleting and adding rows is a little dangerous in my opinion, tableviews are very good at doing animated reloads on individual sections or cells.

Why does Section data reset each time?

I'm quite new to iOS development (as you'll see from my code below). I like to help myself learn new languages by manipulating existing code to do something different. I've hit a bit of a blank with this one though. At the end of each section in a table view, the data it is using resets and it starts again, instead of carrying on. Can anyone tell me where the fault lies here please?
#import "RootViewController.h"
#import "DataController.h"
#import "DetailViewController.h"
#import "Play.h"
#implementation RootViewController
#synthesize dataController;
#synthesize play;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.title = NSLocalizedString(#"Plays", #"Master view navigation title");
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Only one section.
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
/*
The number of rows varies by section.
*/
NSInteger rows = 0;
switch (section) {
case 0:
rows = 3;
break;
case 1:
rows = 1;
break;
case 2:
rows = 2;
break;
default:
break;
}
return rows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath {
static NSString *CellIdentifier = #"PlayCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Get the object to display and set the value in the cell.
Play *playAtIndex = [dataController objectInListAtIndex:indexPath.row];
cell.textLabel.text = playAtIndex.title;
return cell;
}
// Section header titles
#pragma mark -
#pragma mark Section header titles
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:
(NSInteger)section {
NSString *secttitle = nil;
switch (section) {
case 0:
secttitle = NSLocalizedString(#"Comedy", #"Comedy section title");
break;
case 1:
secttitle = NSLocalizedString(#"Action", #"Action section title");
break;
case 2:
secttitle = NSLocalizedString(#"Drama", #"Drama section title");
break;
default:
break;
}
return secttitle;
}
// End of section header titles
#pragma mark -
#pragma mark Table view selection
- (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.
*/
if ([[segue identifier] isEqualToString:#"ShowSelectedPlay"]) {
NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
DetailViewController *detailViewController = [segue destinationViewController];
detailViewController.play = [dataController objectInListAtIndex:selectedRowIndex.row];
}
}
#end
Thanks for your replies so far. The Detail view works fine, and judging by your feedback so far it appears it's because this code isn't being evaluated, but I can't seem to work out where to incorporate it in the master view.
NSString *cellText = nil;
switch (indexPath.section) {
case 0:
cellText = play.date;
break;
case 1:
cellText = play.genre;
break;
case 2:
cellText = [play.characters objectAtIndex:indexPath.row];
break;
default:
break;
}
cell.textLabel.text = cellText;
return cell;
Because you're only using the indexPath.row to get your cell content. Your data model should also consider the indexPath.section:
Play *playAtIndex = [dataController objectInListAtIndex:indexPath.row];
cell.textLabel.text = playAtIndex.title;
i.e. you have a one-dimensional data model...
This line:
Play *playAtIndex = [dataController objectInListAtIndex:indexPath.row];
only takes into account the current indexPath's row, which also resets to zero at the beginning of each section. You'll need to use an array of arrays or otherwise account for the rows in previous sections to get the correct index.

Resources