I am relatively new in iOS, I have found an example of accordion table view that I need. But the thing is that example is TableViewController and I need to implement TableView inside viewController. Can you please help me. What I have tried to do is create tableView, declare it in the h file as
#property (strong, nonatomic) IBOutlet UITableView *MyTb;
and then change all appearances of self.tableView to self.MyTb but it did not work, oh also changed
AccordionTableViewController : UITableViewController
to
AccordionTableViewController : UIViewController
So here is the original code, please help me and provide some guiadance to it, thanks.
h file:
#import <UIKit/UIKit.h>
#interface AccordionTableViewController : UITableViewController {
NSArray *topItems;
NSMutableArray *subItems; // array of arrays
int currentExpandedIndex;
}
#end
m file:
#import "AccordionTableViewController.h"
#define NUM_TOP_ITEMS 20
#define NUM_SUBITEMS 6
#implementation AccordionTableViewController
- (id)init {
self = [super init];
if (self) {
topItems = [[NSArray alloc] initWithArray:[self topLevelItems]];
subItems = [NSMutableArray new];
currentExpandedIndex = -1;
for (int i = 0; i < [topItems count]; i++) {
[subItems addObject:[self subItems]];
}
}
return self;
}
#pragma mark - Data generators
- (NSArray *)topLevelItems {
NSMutableArray *items = [NSMutableArray array];
for (int i = 0; i < NUM_TOP_ITEMS; i++) {
[items addObject:[NSString stringWithFormat:#"Item %d", i + 1]];
}
return items;
}
- (NSArray *)subItems {
NSMutableArray *items = [NSMutableArray array];
int numItems = arc4random() % NUM_SUBITEMS + 2;
for (int i = 0; i < numItems; i++) {
[items addObject:[NSString stringWithFormat:#"SubItem %d", i + 1]];
}
return items;
}
#pragma mark - View management
- (void)viewDidLoad {
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [topItems count] + ((currentExpandedIndex > -1) ? [[subItems objectAtIndex:currentExpandedIndex] count] : 0);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ParentCellIdentifier = #"ParentCell";
static NSString *ChildCellIdentifier = #"ChildCell";
BOOL isChild =
currentExpandedIndex > -1
&& indexPath.row > currentExpandedIndex
&& indexPath.row <= currentExpandedIndex + [[subItems objectAtIndex:currentExpandedIndex] count];
UITableViewCell *cell;
if (isChild) {
cell = [tableView dequeueReusableCellWithIdentifier:ChildCellIdentifier];
}
else {
cell = [tableView dequeueReusableCellWithIdentifier:ParentCellIdentifier];
}
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ParentCellIdentifier] autorelease];
}
if (isChild) {
cell.detailTextLabel.text = [[subItems objectAtIndex:currentExpandedIndex] objectAtIndex:indexPath.row - currentExpandedIndex - 1];
}
else {
int topIndex = (currentExpandedIndex > -1 && indexPath.row > currentExpandedIndex)
? indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count]
: indexPath.row;
cell.textLabel.text = [topItems objectAtIndex:topIndex];
cell.detailTextLabel.text = #"";
}
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL isChild =
currentExpandedIndex > -1
&& indexPath.row > currentExpandedIndex
&& indexPath.row <= currentExpandedIndex + [[subItems objectAtIndex:currentExpandedIndex] count];
if (isChild) {
NSLog(#"A child was tapped, do what you will with it");
return;
}
[self.tableView beginUpdates];
if (currentExpandedIndex == indexPath.row) {
[self collapseSubItemsAtIndex:currentExpandedIndex];
currentExpandedIndex = -1;
}
else {
BOOL shouldCollapse = currentExpandedIndex > -1;
if (shouldCollapse) {
[self collapseSubItemsAtIndex:currentExpandedIndex];
}
currentExpandedIndex = (shouldCollapse && indexPath.row > currentExpandedIndex) ? indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count] : indexPath.row;
[self expandItemAtIndex:currentExpandedIndex];
}
[self.tableView endUpdates];
}
- (void)expandItemAtIndex:(int)index
{
NSMutableArray *indexPaths = [NSMutableArray new];
NSArray *currentSubItems = [subItems objectAtIndex:index];
int insertPos = index + 1;
for (int i = 0; i < [currentSubItems count]; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:insertPos++ inSection:0]];
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
//[indexPaths release];
}
- (void)collapseSubItemsAtIndex:(int)index
{
NSMutableArray *indexPaths = [NSMutableArray new];
for (int i = index + 1; i <= index + [[subItems objectAtIndex:index] count]; i++)
{
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
//[indexPaths release];
}
- (void)dealloc
{
[topItems release];
[subItems release];
[super dealloc];
}
#end
You must set your view controller as delegate and datasource of your tableview:
#interface AccordionTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
in your header file and in viewDidLoad:
-(void)viewDidLoad {
[super viewDidLoad];
self.MyTb.delegate = self;
self.MyTb.dataSource = self;
}
Or you can set the datasource and delegate in Storyboard. You must also make sure you have connected the table view to MyTb IBOutlet in storyboard.
Related
Hi in my array there is huge amount of data and I have to display it in UITableView. But the condition here is I have to display only 5 records initially,then once the user scroll down I have to load more records.I tried searching it but didn't get any useful answer please help me and I agree that we have to use ((void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath )
Another way
we can adjust the return value of tableView:numberOfRowsInSection: method, every time we want to insert ten rows, you can plus 5 to the return value.
but i am not understand how to do this please help me
my code:
#import "TableViewController.h"
#interface TableViewController ()
{
UITableView * tableView;
NSUInteger reloads_;
NSMutableArray * MainArray;
int scrollValue;
long dataLimit;
NSArray * yourDataSource;
}
#end
#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MainArray = [[NSMutableArray alloc]initWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",nil];
tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:tableView];
}
#pragma mark - UITableViewDataSource
// number of section(s), now I assume there is only 1 section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)theTableView
{
return 1;
}
// number of row in the section, I assume there is only 1 row
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section
{
return yourDataSource.count;
}
// the cell will be returned to the tableView
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newFriendCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [yourDataSource objectAtIndex:indexPath.row];
return cell;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
[self estimatedTotalData];
}
- (void)estimatedTotalData
{
long currentRow = ((NSIndexPath *)[[tableView indexPathsForVisibleRows] lastObject]).row;
// 25 here is the initial data count
long estimateDataCount = 25;
while (currentRow > estimateDataCount)
{
estimateDataCount+=25;
}
dataLimit = estimateDataCount;
if (dataLimit == currentRow+1)
{
dataLimit+=25;
if (dataLimit != estimateDataCount)
{
//[self requestForData]; or load necessary data
// take not that dataLimit is the total data that must be displayed.
NSArray *yourLocalData = #[#"1", #"2", #"3", #"4", #"5"];
// just add more sample objects
yourDataSource = [self setsLimitForObject: yourLocalData limit: dataLimit];
[tableView reloadData];
}
}
}
- (NSArray *)setsLimitForObject:(id)object limit:(long)limit
{
if ([object isKindOfClass:[NSArray class]])
{
NSMutableArray *tempArray = [object mutableCopy];
NSRange range = NSMakeRange(0, limit);
if (tempArray.count >= range.length)
return [tempArray subarrayWithRange:range];
else
{
NSLog(#"Out of bounds");
return object;
}
} else NSLog(#"Sets Log: Cannot set limit for object %#", [object class]);
return nil;
}
#end
Try this approach, this is more like a paging...
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self estimatedTotalData];
}
- (void)estimatedTotalData
{
long currentRow = ((NSIndexPath *)[[YourTableView indexPathsForVisibleRows] lastObject]).row;
// 25 here is the initial data count
long estimateDataCount = 25;
while (currentRow > estimateDataCount)
{
estimateDataCount+=25;
}
dataLimit = estimateDataCount;
if (dataLimit == currentRow+1)
{
dataLimit+=25;
if (dataLimit != estimateDataCount)
{
//[self requestForData]; or load necessary data
// take not that dataLimit is the total data that must be displayed.
NSArray *yourLocalData = #[#"1", #"2", #"3", #"4", #"5"];
// just add more sample objects
yourDataSource = [self setsLimitForObject: yourLocalData limit: dataLimit];
[yourTable reloadData];
}
}
}
Update:
- (NSArray *)setsLimitForObject:(id)object limit:(long)limit
{
if ([object isKindOfClass:[NSArray class]])
{
NSMutableArray *tempArray = [object mutableCopy];
NSRange range = NSMakeRange(0, limit);
if (tempArray.count >= range.length)
return [tempArray subarrayWithRange:range];
else
{
NSLog(#"Out of bounds");
return object;
}
} else NSLog(#"Sets Log: Cannot set limit for object %#", [object class]);
return nil;
}
You can use below code for your cellForRowAtIndexPath,
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newFriendCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [mainArray objectAtIndex:indexPath.row];
if (indexPath.row % 5 == 0) { // this if loop will get execute at every fifth row
// if you are loading data from API response then call API here and add next set of data into your existing array.
// then just reload your tableview by it's name e.g. [theTableView reloadData];
}
return cell;
}
and for your numberOfRowsInSection, instead of fixed 5 rows, just use mainArray.count like below,
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section
{
return mainArray.count;
}
I'm using a combination of SWRevealViewController, A ContentViewController and A NavigationViewController (Navigation).
The storyboard is as follows:
On TableViewCell click I can get the navigation text. What I want to be able to do is to pass this information back into itself. Setting the title of the ContentViewController to the selected cell and loading other desired data.
How can I fulfil this functionality?
Here is my latest code within my NavigationViewController.m file:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL isChild = currentExpandedIndex > -1 && indexPath.row > currentExpandedIndex && indexPath.row <= currentExpandedIndex + [[subItems objectAtIndex:currentExpandedIndex] count];
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
if (isChild) {
NSString *cellText = selectedCell.detailTextLabel.text;
NSLog(#"%# tapped",cellText);
return;
}
else{
NSString *cellText = selectedCell.textLabel.text;
NSLog(#"%# tapped",cellText);
if([cellText isEqualToString:#"Sign Out"]){
[self performSegueWithIdentifier:#"goToHome" sender:self];
}
else if ([cellText isEqualToString:#"Sign In"]){
[self performSegueWithIdentifier:#"goToHome" sender:self];
}
else if ([cellText isEqualToString:#"Festival Map"]){
[self performSegueWithIdentifier:#"festivalMap" sender:self];
}
}
[self.tableView beginUpdates];
if (currentExpandedIndex == indexPath.row) {
[self collapseSubItemsAtIndex:currentExpandedIndex];
currentExpandedIndex = -1;
}
else {
BOOL shouldCollapse = currentExpandedIndex > -1;
if (shouldCollapse) {
[self collapseSubItemsAtIndex:currentExpandedIndex];
}
currentExpandedIndex = (shouldCollapse && indexPath.row > currentExpandedIndex) ? indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count] : indexPath.row;
[self expandItemAtIndex:currentExpandedIndex];
}
[self.tableView endUpdates];
}
- (void)expandItemAtIndex:(int)index {
NSMutableArray *indexPaths = [NSMutableArray new];
NSArray *currentSubItems = [subItems objectAtIndex:index];
int insertPos = index + 1;
for (int i = 0; i < [currentSubItems count]; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:insertPos++ inSection:0]];
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
}
- (void)collapseSubItemsAtIndex:(int)index {
NSMutableArray *indexPaths = [NSMutableArray new];
for (int i = index + 1; i <= index + [[subItems objectAtIndex:index] count]; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"festivalMap"]){
NSLog(#"TEST");
NSString * title = #"CHANGE";
ViewController * view = [segue destinationViewController];
view.navigationItem.title = title;
}
}
And here is the code for my ContentViewController.m file:
#import "ContentViewController.h"
#import "SWRevealViewController.h"
#import "Function.h"
#interface ContentViewController ()
#end
#implementation ContentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_barButton.target = self.revealViewController;
_barButton.action = #selector(revealToggle:);
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
#end
Thanks
You can Try this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"yoursegue"]){
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
ContentViewController*contentView = (ContentViewController*)segue.destinationViewController;
contentView.cellText = selectedCell.textLabel.text;
NSLog(#"cellText = %#",selectedCell.textLabel.text;);
}
}
However, it is not good practice to access value from the user interface or build the logic around it, instead you should be using the datasource.This violate MVC principles.
I have this code and I am trying to understand it. This has an int value 'currentExpandIndex' ,I couldnt figure out why it is changing because I dont find proper initiation of it. The int is first given the value -1,but later in code the int value changes according to the indexpath. I coldnt find the relation between the int and the indexpath declared in the code.Kindly tell me,why
the code is:
#interface AccordionTableViewController : UITableViewController {
NSArray *topItems;
NSMutableArray *subItems; // array of arrays
int currentExpandedIndex;
}
#end
//.m file
- (id)init {
self = [super init];
if (self) {
topItems = [[NSArray alloc] initWithArray:[self topLevelItems]];
subItems = [NSMutableArray new];
currentExpandedIndex = -1;
NSLog(#"currenyt index -init is %d",currentExpandedIndex);
for (int i = 0; i < [topItems count]; i++) {
[subItems addObject:[self subItems]];
}
}
return self;
}
#pragma mark - Data generators
- (NSArray *)topLevelItems {
NSMutableArray *items = [NSMutableArray array];
for (int i = 0; i < NUM_TOP_ITEMS; i++) {
[items addObject:[NSString stringWithFormat:#"Item %d", i + 1]];
}
return items;
}
- (NSArray *)subItems {
NSMutableArray *items = [NSMutableArray array];
int numItems = arc4random() % NUM_SUBITEMS + 2;
for (int i = 0; i < numItems; i++) {
[items addObject:[NSString stringWithFormat:#"SubItem %d", i + 1]];
}
return items;
}
#pragma mark - View management
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"currenyt index -view did load is %d",currentExpandedIndex);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"currenyt index -no of rows in section is %d",currentExpandedIndex);
return [topItems count] + ((currentExpandedIndex > -1) ? [[subItems objectAtIndex:currentExpandedIndex] count] : 0);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ParentCellIdentifier = #"ParentCell";
static NSString *ChildCellIdentifier = #"ChildCell";
NSLog(#"currenyt index-cell for row at index is %d",currentExpandedIndex);
BOOL isChild =
currentExpandedIndex > -1
&& indexPath.row > currentExpandedIndex
&& indexPath.row <= currentExpandedIndex + [[subItems objectAtIndex:currentExpandedIndex] count];
UITableViewCell *cell;
if (isChild) {
cell = [tableView dequeueReusableCellWithIdentifier:ChildCellIdentifier];
}
else {
cell = [tableView dequeueReusableCellWithIdentifier:ParentCellIdentifier];
}
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ParentCellIdentifier] autorelease];
}
if (isChild) {
cell.detailTextLabel.text = [[subItems objectAtIndex:currentExpandedIndex] objectAtIndex:indexPath.row - currentExpandedIndex - 1];
}
else {
int topIndex = (currentExpandedIndex > -1 && indexPath.row > currentExpandedIndex)
? indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count]
: indexPath.row;
cell.textLabel.text = [topItems objectAtIndex:topIndex];
cell.detailTextLabel.text = #"";
}
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL isChild =
currentExpandedIndex > -1
&& indexPath.row > currentExpandedIndex
&& indexPath.row <= currentExpandedIndex + [[subItems objectAtIndex:currentExpandedIndex] count];
if (isChild) {
NSLog(#"A child was tapped, do what you will with it");
NSLog(#"currenyt index -did select is %d",currentExpandedIndex);
return;
}
NSLog(#"currenyt index -did select out is %d",currentExpandedIndex);
[self.tableView beginUpdates];
if (currentExpandedIndex == indexPath.row) {
[self collapseSubItemsAtIndex:currentExpandedIndex];
currentExpandedIndex = -1;
}
else {
BOOL shouldCollapse = currentExpandedIndex > -1;
if (shouldCollapse) {
[self collapseSubItemsAtIndex:currentExpandedIndex];
}
currentExpandedIndex = (shouldCollapse && indexPath.row > currentExpandedIndex) ? indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count] : indexPath.row;
[self expandItemAtIndex:currentExpandedIndex];
}
[self.tableView endUpdates];
}
- (void)expandItemAtIndex:(int)index {
NSMutableArray *indexPaths = [NSMutableArray new];
NSArray *currentSubItems = [subItems objectAtIndex:index];
int insertPos = index + 1;
for (int i = 0; i < [currentSubItems count]; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:insertPos++ inSection:0]];
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
//[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[indexPaths release];
}
- (void)collapseSubItemsAtIndex:(int)index {
NSMutableArray *indexPaths = [NSMutableArray new];
for (int i = index + 1; i <= index + [[subItems objectAtIndex:index] count]; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[indexPaths release];
}
In didSelectRowAtIndexPath: you have this line:
currentExpandedIndex =
(shouldCollapse && indexPath.row > currentExpandedIndex) ?
indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count] :
indexPath.row;
This line assign indexPath.row or indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count] to currentExpandedIndex.
currentExpandedIndex = (shouldCollapse && indexPath.row > currentExpandedIndex) ? indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count] : indexPath.row;
On each didSelectRow it will change.
First, set a breakpoint in -init. Depending on how you create this controller, it might not be called, in which case it will be 0. Try -initWithCoder: instead if this is the case. Otherwise, I only see this set in one place: -tableView:didSelectRowAtIndexPath:, where it can be set to the row or indexPath.row - [[subItems objectAtIndex:currentExpandedIndex] count]. Depending on what else you are doing with the table, this might be a good job for sections or expandable individual rows to simplify.
I am able to expand and collapse the tableView sections successfully however I am not able to do it for individual sections so far.So all the sections collapse or expand at the same time, which is because I call [tableView reloadData] .So How can I expand or collapse a particular section?
Here is how I am doing it currently.
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
headerLabel = [[UILabel alloc]init];
headerLabel.tag = section;
headerLabel.userInteractionEnabled = YES;
headerLabel.backgroundColor = [[UIColor grayColor]colorWithAlphaComponent:0.2];
headerLabel.text = [menuCategoryArray objectAtIndex:section];
headerLabel.frame = CGRectMake(5, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height);
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(headerClicked:)];
tapGesture.cancelsTouchesInView = NO;
[headerLabel addGestureRecognizer:tapGesture];
return headerLabel;
}
-(void)headerClicked:(UIGestureRecognizer*)sender
{
if (!isShowingList) {
isShowingList=YES;
[self.menuTableView reloadData];
UILabel *lbl = (UILabel*)sender.view;
NSLog(#"header no : %d", lbl.tag);
}else{
isShowingList=NO;
[self.menuTableView reloadData];
UILabel *lbl = (UILabel*)sender.view;
NSLog(#"header no : %d", lbl.tag);
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (isShowingList) {
return [[[[jsonArray valueForKey:#"menus"] valueForKey:#"menuName"] objectAtIndex:section] count];
}else{
return 0;
}
return 0;
}
First take isShowingList as
#property (nonatomic, strong) NSMutableArray *isShowingList;
And for identifying previously opened section you need another property
#property (nonatomic, assign) NSInteger openSectionIndex;
when you have the data initialized, isShowingList in you case, initialize isShowingList array before reloading table
self.isShowingList = [NSMutableArray array];
if (jsonArray && [jsonArray valueForKey:#"menus"] && [[jsonArray valueForKey:#"menus"] valueForKey:#"menuName"]) {
for (int i = 0; i < [[[jsonArray valueForKey:#"menus"] valueForKey:#"menuName"] count]; i++) {
[self.isShowingList addObject:[NSNumber numberWithBool:NO]];
}
}
and initialize openSectionIndex in viewDidLoad() like this
self.openSectionIndex = NSNotFound;
and your code should be changed like this
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([[self.isShowingList objectAtIndex:section] boolValue]) {
return [[[[jsonArray valueForKey:#"menus"] valueForKey:#"menuName"] objectAtIndex:section] count];
} else {
return 0;
}
return 0;
}
-(void)headerClicked:(UIGestureRecognizer*)sender {
UILabel *lbl = (UILabel*)sender.view;
NSLog(#"header no : %d", lbl.tag);
if ([[self.isShowingList objectAtIndex:lbl.tag] boolValue]) {
[self closeSection:lbl.tag];
} else {
[self openSection:lbl.tag];
}
}
//methods for expanding and collapsing sections
- (void)openSection:(NSInteger)section {
[self.isShowingList replaceObjectAtIndex:section withObject:[NSNumber numberWithBool:YES]];
NSInteger countOfRowsToInsert = [[[[jsonArray valueForKey:#"menus"] valueForKey:#"menuName"] objectAtIndex:section] count];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]];
}
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
NSInteger previousOpenSectionIndex = self.openSectionIndex;
if (previousOpenSectionIndex != NSNotFound) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.menuTableView reloadSections:[NSIndexSet indexSetWithIndex:previousOpenSectionIndex] withRowAnimation:UITableViewRowAnimationNone];
});
[self.isShowingList replaceObjectAtIndex:previousOpenSectionIndex withObject:[NSNumber numberWithBool:NO]];
NSInteger countOfRowsToDelete = [[[[jsonArray valueForKey:#"menus"] valueForKey:#"menuName"] objectAtIndex:previousOpenSectionIndex] count];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenSectionIndex]];
}
}
// Apply the updates.
[self.menuTableView beginUpdates];
[self.menuTableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationAutomatic];
[self.menuTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationAutomatic];
[self.menuTableView endUpdates];
self.openSectionIndex = section;
}
- (void)closeSection:(NSInteger)section {
[self.isShowingList replaceObjectAtIndex:section withObject:[NSNumber numberWithBool:NO]];
NSInteger countOfRowsToDelete = [self.menuTableView numberOfRowsInSection:section];
if (countOfRowsToDelete > 0) {
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:section]];
}
[self.menuTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
}
self.openSectionIndex = NSNotFound;
}
You really would be better off looking at using a 'tableView update block'. Please take a look at this viewController for an answer I posted very recently. The updateBlock allows you to manipulate some variable or other which affects the dataSource, and instruct the table to add/remove rows/sections in order to reflect that change. Note that when you call the endUpdates method the table must not conflict with the model or you'll get an exception.
#import "ViewController.h"
//dont worry, the header is empty except for import <UIKit/UIKit.h>, this is a subclass on UIViewController
#interface ViewController ()<UITableViewDataSource, UITableViewDelegate>
#property (weak, nonatomic) UITableView *tableView;
#end
#implementation ViewController
{
//ivars
BOOL sectionIsOpen[4]; //we will use this BOOL array to keep track of the open/closed state for each section. Obviously I have the number of sections fixed at 4 here, but you could make a more dynamic array with malloc() if neccesary..
}
- (void)viewDidLoad {
[super viewDidLoad];
UITableView *tv = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
tv.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
tv.dataSource = self;
tv.delegate = self;
[self.view addSubview:tv];
self.tableView = tv;
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark - UITableViewDataSource
-(NSInteger )numberOfSectionsInTableView:(UITableView *)tableView{
return 4;
}
-(NSInteger )tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return ((sectionIsOpen[section]) ? [self numberOfRowsInSection:section] : 0);
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
//put your switch() here...
return [NSString stringWithFormat:#"I am section %i", (int)section ];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellId = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}
//etc etc decorate your cell...
cell.textLabel.text = [NSString stringWithFormat:#"cell %i / %i", (int)indexPath.section, (int)indexPath.row ];
return cell;
}
#pragma mark - UITableViewDelegate
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
const CGRect fr = CGRectMake(0, 0, 320.0, 40.0 );
UIButton *btn = [[UIButton alloc]initWithFrame:fr];
[btn setTitle:[self tableView:tableView titleForHeaderInSection:section] forState:UIControlStateNormal ];
[btn setTag:section];
[btn addTarget:self action:#selector(sectionOpenToggle:) forControlEvents:UIControlEventTouchUpInside];
// add an image, colour etc if you like
return btn;
}
#pragma mark - tableViewHelpers
//the number of rows in sectionX when it is open...
-(NSInteger )numberOfRowsInSection:(NSInteger )section{
//get your count from your model
return section + 1;
}
//opening/closing a section
-(void )setSection:(NSInteger )section toOpen:(BOOL )open{
if (open != sectionIsOpen[section]) {
//build an array of indexPath objects
NSMutableArray *indxPths = [NSMutableArray array];
for (NSInteger row = 0; row < [self numberOfRowsInSection:section]; row ++) {
[indxPths addObject: [NSIndexPath indexPathForRow:row inSection:section ]
];
}
[self.tableView beginUpdates];
if (open) {
[self.tableView insertRowsAtIndexPaths:indxPths withRowAnimation:UITableViewRowAnimationFade];
//nb there is a large ENUM of tableViewRowAnimation types to experiment with..
}else{
[self.tableView deleteRowsAtIndexPaths:indxPths withRowAnimation:UITableViewRowAnimationFade];
}
sectionIsOpen[section] = open;
[self.tableView endUpdates];
}
}
-(void )sectionOpenToggle:(id )sender{
[self setSection:[sender tag] toOpen: !sectionIsOpen[[sender tag]] ];
}
// open/close all sections.
-(void )setAllSectionsOpen:(BOOL )open{
for (NSInteger section = 0; section < [self numberOfSectionsInTableView:self.tableView]; section ++) {
[self setSection:section toOpen:open];
}
}
//these two for your convenience, hook up to navbar items etc..
-(IBAction)openAllSections:(id)sender{
[self setAllSectionsOpen:YES];
}
-(IBAction)closeAllSections:(id)sender{
[self setAllSectionsOpen:NO];
}
#end
I'm willing to change a specific header view of my UITableView when I click a row.
I've read all posts about it yet. I tried "reloadData", "setNeedDisplay", "reloadSections:withRowAnimation:", and several others ideas... there is nothing to do. My header view either doesn't update or it does weird things like updating only when I move the table view (which is not what I'm willing to achieve).
My code looks like this for now (regarding the UITableView delegates methods):
-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
if(tableView==_storeTableView){
return [_storeDataArray count];
} else {
return 1;
}
}
-(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section {
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:section];
if (!headerModel.headerView) {
NSString *shelfName = headerModel.shelf;
headerModel.headerView = [[[HouraStoreHeaderView alloc] initWithFrame:CGRectMake(0.0, 0.0, _storeTableView.bounds.size.width, 80) title:shelfName section:section subheaderNumber:([headerModel.openedSubHeaders count]-1) delegate:self] autorelease];
}
return headerModel.headerView;
} else {
return nil;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:section];
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
NSInteger numberOfRowsInSection = [[myDict allKeys] count];
return headerModel.open ? numberOfRowsInSection : 0;
} else if(tableView==_searchTableView){
return [_resultArray count];
} else {
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
cell.accessoryView=[[[HouraStoreCellView alloc] initWithFrame:CGRectMake(0.0, 0.0, _storeTableView.bounds.size.width, 50) title:[[myDict allKeys] objectAtIndex:indexPath.row]] autorelease];
return cell;
} else if (tableView==_searchTableView) {
cell.textLabel.text = [_resultArray objectAtIndex:indexPath.row];
return cell;
} else {
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:section];
NSInteger height = 59.0 + ([headerModel.openedSubHeaders count]-1)*41.0;
return height;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
if ([[myDict objectForKey:[[myDict allKeys] objectAtIndex:indexPath.row]] isKindOfClass:[NSDictionary class]]) {
[self cellOpened:indexPath];
} else {
[_activityIndicatorView startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_listProductsFoundedFinished:)
name:HouraSearchProductsDone
object:nil];
NSString *searchString = [[myDict allKeys] objectAtIndex:indexPath.row];
searchString = [searchString stringByReplacingOccurrencesOfString:#"\"" withString:#"\\u0022"];
[_singleton.util beginSearchProducts:searchString context:#"2"];
}
} else if(tableView==_searchTableView){
_searchBar.text = [_resultArray objectAtIndex:indexPath.row];
[_searchBar resignFirstResponder];
[_activityIndicatorView startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_listProductsFoundedFinished:)
name:HouraSearchProductsDone
object:nil];
[_singleton.util beginSearchProducts:_searchBar.text context:#"2"];
}
}
-(void)headerView:(HouraStoreHeaderView*)headerView headerOpened:(NSInteger)headerOpened {
if (self.openSectionIndex!=NSNotFound) {
[self closeAllHeaders];
}
//[self closeAllHeaders];
HouraStoreHeaderModel *headerModel =nil;
headerModel = [self.headerInfoArray objectAtIndex:headerOpened];
headerModel.open = YES;
headerModel.headerView.disclosureButton.selected = YES;
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
NSInteger countOfRowsToInsert = [[myDict allKeys] count];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:headerOpened]];
}
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
NSInteger previousOpenSectionIndex = self.openSectionIndex;
if (previousOpenSectionIndex != NSNotFound) {
HouraStoreHeaderModel *previousHeaderModel = [self.headerInfoArray objectAtIndex:previousOpenSectionIndex];
previousHeaderModel.open = NO;
previousHeaderModel.headerView.disclosureButton.selected = NO;
[previousHeaderModel.headerView toggleOpenWithUserAction:NO];
NSInteger countOfRowsToDelete = [[[_storeDataDict objectForKey:previousHeaderModel.shelf ] allKeys] count];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenSectionIndex]];
}
}
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (previousOpenSectionIndex == NSNotFound || headerOpened < previousOpenSectionIndex) {
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
} else {
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
[_storeTableView beginUpdates];
[_storeTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[_storeTableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[_storeTableView endUpdates];
self.openSectionIndex = headerOpened;
}
-(void)headerView:(HouraStoreHeaderView*)headerView headerClosed:(NSInteger)headerClosed {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:headerClosed];
headerModel.open = NO;
headerModel.headerView.disclosureButton.selected = NO;
[headerModel cleanOpenedSubHeaders];
[self.headerInfoArray replaceObjectAtIndex:headerClosed withObject:headerModel];
NSInteger countOfRowsToDelete = [_storeTableView numberOfRowsInSection:headerClosed];
if (countOfRowsToDelete > 0) {
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:headerClosed]];
}
[_storeTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
}
self.openSectionIndex = NSNotFound;
}
-(void)cellOpened:(NSIndexPath*)indexPath {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
[self headerView:headerModel.headerView headerClosed:indexPath.section];
[headerModel addOpenedSubHeaders:[[[_storeDataDict objectForKey:headerModel.shelf] allKeys] objectAtIndex:indexPath.row]];
[self.headerInfoArray replaceObjectAtIndex:indexPath.section withObject:headerModel];
headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
[self headerView:headerModel.headerView headerOpened:indexPath.section];
}
-(void)closeAllHeaders {
for (NSInteger i = 0; i < [self.headerInfoArray count]; i++) {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:i];
[self headerView:headerModel.headerView headerClosed:i];
}
}
What I'd like to do is, when I click a row, the section header update so it contains a new button with the row text. Then I dismiss the row and reload new datas in the section rows. I managed to handle the rows perfectly. But I can't find a way to get this header view updated.
Thx for any idea.
You just change it directly. I created an instance variable in the header file for a label that I will put in the header's view I'll create:
#interface MainViewController : UITableViewController {
// creating my datasource array instance variable
NSArray *_items;
// this is the label I will add to the header view when I create it
UILabel *_headerLabel;
}
#end
And in my tableView when they select a row I call a function that simply changes the text on the label:
#implementation MainViewController
- (id)init {
self = [super initWithStyle:UITableViewStyleGrouped];
/ filling my datasource with test strings
_items = #[#"one", #"two"];
return self;
}
- (void)changeHeaderLabel:(NSString *)newLabel {
// when this function gets called and is passed a string, I will simply
// set the text on the label to the new string and viola!
_headerLabel.text = newLabel;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// this table will only have a single section for demo purposes
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// return the count of my datasource array
return _items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// attempt to create a cell by reusing one with a given identifier
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
// if I wasn't able to reuse one
if (cell == nil) {
// create one from scratch with that identifier
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
// now simply set the text on the cell from my data source array of strings
cell.textLabel.text = _items[indexPath.row];
// and return the cell
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// deselect the row so the cell automatically fades out after selection
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// here you could do one of two things, either get a reference to the cell itself,
// and then get the value stored in it's textLabel
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *newHeaderTitleString = selectedCell.textLabel.text;
// OR you can get it right from your datasource
NSString *newHeaderTitleString = _items[indexPath.row];
// then just call the above function with the string as the single param
[self changeHeaderLabel:newHeaderTitleString];
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
// here I just create a view that will span the whole frame and is an arbitrary height
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 80)];
// set the background color to clear
headerView.backgroundColor = [UIColor clearColor];
// then I initialize my instance variable with a frame that's centered in the view
// for aesthetic purposes
_headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, self.view.frame.size.width - 10, 80)];
// then I set the text color, add an autoresizing mask so if the view rotates
// it still remains centered properly, set the text to some starting value,
// and add it to the headerView I previously created
_headerLabel.textColor = [UIColor darkGrayColor];
_headerLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_headerLabel.text = #"Before";
[headerView addSubview:_headerLabel];
// then I return the headerView
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
// return an arbitrary height here for testing
return 80;
}
That results in the following:
If you have any questions let me know! This is just a quick example to demonstrate it, but you may want to customize the view in a different way altogether. This should at least solve your problem and give you a starting point to work from.
Have you tried reloadRowsAtIndexPaths:withRowAnimation: where you set the row property of the NSIndexPath passed in as NSNotFound? So reloading just the header of section 3, for instance would look like
NSIndexPath * headerIndexPath = [NSIndexPath indexPathForRow: NSNotFound section:3];
[self.tableView reloadRowsAtIndexPaths:#[headerIndexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
I guarantee nothing, but I'm pretty sure it used to work before, because I used it a couple of times.
But even if it works, it's still a hack that might get broken by Apple any time.
edit
Ok, never mind. I tried this with iOS 7 in Xcode 5 and for some reason, even with NSNotFound as the row number, it still reloads the whole sections (with all its cells). So this does not work any more, damn.