Creating sections within UICollectionView - ios

In my controller, I parse data from a JSON array and use it to populate a collection view. The code below successfully loads and displays the data, but fails to create individual sections.
As such, how do I modify the code to create sections for each object within the array. For example, if my array has a count of 50 urls, how do I create one section for each?
let lastItem = self.photos.count
self.photos.addObjectsFromArray(photoInfos)
let indexPaths = (lastItem..<self.photos.count).map { NSIndexPath(forItem: $0, inSection: 0) }
dispatch_async(dispatch_get_main_queue()) {
self.collectionView!.insertItemsAtIndexPaths(indexPaths)
}

Most of the time the apps use one section and many items in this section so that's why it may be a bit harder to find how to do your way.
It's all about how you use this 2 functions of the datasource:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return self.items.count
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
Normally you return 1 section and the array count in the numberOfItemsInSection, but if you switch them you would have exactly what you want.

here is my code is in Objective-C, I hope it'll helpful to you.
Here HeaderView is sub class of UICollectionReusableView.
Where you can set view as you like.
-(CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
return CGSizeMake(320.0f, 32.0f);
}
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
HeaderView *view=(HeaderView *)[historyCollection dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"headerView" forIndexPath:indexPath];
NSArray *viewsToRemove = [view subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
view.backgroundColor=[UIColor clearColor];
self.month = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, 320, 21)];
self.month.text =[[monthList objectAtIndex:indexPath.section] uppercaseString];
self.month.textColor = [UIColor greenColor];
self.month.font=[UIFont boldSystemFontOfSize:17];
[view addSubview:self.month];
return view;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView heightForHeaderInSection:(NSInteger)section {
if (section == 0) {
return 0;
}
else if (section == 1) {
return 40.0;
}
else {
return 50;
}
}

Here is the code that worked for me
create the header cell. To do which I created a custom cell class and a nib to do the customization of the cell in the graphic editor
In viewDidLoad
self.collectionView?.registerNib(UINib(nibName: "KlosetCollectionHeaderViewCell", bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderCell")
Then you add the delegate function
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> KlosetCollectionHeaderViewCell {
println("entring viewforsuppplementaryElementofKind")
var headerCell = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "HeaderCell", forIndexPath: indexPath) as? KlosetCollectionHeaderViewCell
return headerCell!
}
This will put the HeaderCell in the SectionView of the PFCollectionView
The controls that show in the cell you add them to the xib file as well as the outlets and actions

Related

Reuse error tableView with cell design programmatically

I have a problem with my tableView who I managed specially, i need to delete and add row really often. My cell are designed programmatically. I update my array who depend my cells and called self.tableView.reloadData() but this don't remove the cells I need and update the tableView like my array.
Cause to the reuse and my design of cell (programmatically) I need to check if the cell is always design or not. And the problem come from here.
When I called tableView.reloadData() my data are not properly reload, so I need to delete All view in the cells: indicate that the cells are not design, to design the new cell ... Of course I can just update the visible cells (with tableView.visibleCells), so this work but how can I update my other not-visible cells ?
Maybe I have an architecture problem? If so, what is the best way to delete and insert a row in the TableView with a indexPath defined? Or, how programmatically design the cell only one time?
Code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return user.lobbySurvey.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:"Celll") as! CardCell
for survey in user.lobbySurvey{
let index = user.lobbySurvey.index(where: {
//get the current index is nedeed else the cells reuse lazy
$0 === survey
})
if indexPath.row == index{
var surveyState : UserSurvey.state
surveyState = survey.state
switch surveyState{
case .selectSurvey:
cell.drawCard(statutOfCard: .selectSurvey)
case .goSurvey:
cell.drawCard(statutOfCard: .goSurvey(picture: survey.picture))
case .surveyEnded:
print("survey Ended")
case .surveyWork:
print("survey in progress to vote")
case .surveyWaiting:
cell.drawCard(statutOfCard: .surveyWaiting(selfSurveyId: survey.id, timeLeft: survey.timeLeft, picture: survey.picture))
case .buyStack:
cell.drawCard(statutOfCard: .buyStack(supView : self.view))
}
}
}
cell.delegate = self
cell.delegateCard = self
cell.layer.backgroundColor = UIColor.clear.cgColor
cell.backgroundColor = .clear
tableView.backgroundColor = .clear
tableView.layer.backgroundColor = UIColor.clear.cgColor
return cell
}
You can have an array which is the model of your table:
NSMutableArray *model; (model can have identifier).
You can change this model whenever you want.
With this you can make your table dynamic just calling tableView.reloadData() and make whatever in cellForRow & heightForRow
- (CGFloat) tableView: (UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height;
YourModel *model = self.model[indexPath.row];
if ([model isKindOfClass:[SomeClassTableViewCellModel class]]) {
height = 50;
} else if([model.identifier isEqualToString:#"Whatever"]){
height = 0.0f;
else{
height = kHeightForNormalCells;
}
return height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YourModel *model = self.model[indexPath.row];
cell = [tableView dequeueReusableCellWithIdentifier:model.identifier forIndexPath:indexPath];
if ([cell isKindOfClass:[SomeClass class]]) {
NSLog(#"Configure Cell");
[cell setModel:model];
}
return cell;
}
Moreover, your cell should have a method setModel:
- (void)setModel:(SomeTableViewCellModel *)model {
_model = model;
self.label1.text = [NSString stringWithFormat:#"%#",model.value1];
self.label2.text = [NSString stringWithFormat:#"%#",model.value2];
}
Hope this helps you.

How to create UIButton on UITableview and display the list for each button? [duplicate]

I want to make this type of expandable/collapsible table view.
there are categories and subcategories as in picture.
for example "health and beauty" is a category and when i click this cell than its open subcategories as in picture below.
So how can I make this type of table view?
please suggest me.
Finally i get two very useful helping link below which describes exact what the requirement is here
Expanding/Collapsing TableView Sections
Collapsable Table View for iOS
Really, good articles for such kind of expanding/collapsing tableview sections
Use Following code for expandable Cell into UITableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text=[[self.arForTable objectAtIndex:indexPath.row] valueForKey:#"name"];
[cell setIndentationLevel:[[[self.arForTable objectAtIndex:indexPath.row] valueForKey:#"level"] intValue]];
return cell;
}
code for expanding & collapsing rows – TableView DidSelectRow Method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *d=[self.arForTable objectAtIndex:indexPath.row];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
BOOL isAlreadyInserted=NO;
for(NSDictionary *dInner in ar ){
NSInteger index=[self.arForTable indexOfObjectIdenticalTo:dInner];
isAlreadyInserted=(index>0 && index!=NSIntegerMax);
if(isAlreadyInserted) break;
}
if(isAlreadyInserted) {
[self miniMizeThisRows:ar];
} else {
NSUInteger count=indexPath.row+1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar ) {
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[self.arForTable insertObject:dInner atIndex:count++];
}
[tableView insertRowsAtIndexPaths:arCells withRowAnimation:UITableViewRowAnimationLeft];
}
}
}
A Method which will help to minimize & maximize/expand-collapse rows.
-(void)miniMizeThisRows:(NSArray*)ar{
for(NSDictionary *dInner in ar ) {
NSUInteger indexToRemove=[self.arForTable indexOfObjectIdenticalTo:dInner];
NSArray *arInner=[dInner valueForKey:#"Objects"];
if(arInner && [arInner count]>0){
[self miniMizeThisRows:arInner];
}
if([self.arForTable indexOfObjectIdenticalTo:dInner]!=NSNotFound) {
[self.arForTable removeObjectIdenticalTo:dInner];
[self.tableView deleteRowsAtIndexPaths:
[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexToRemove inSection:0]]
withRowAnimation:UITableViewRowAnimationRight];
}
}
}
You can download the source code from my tutorial site.
If this helps: [Access uitableview's expandable and collapsable sections] https://github.com/OliverLetterer/UIExpandableTableView
I have a little bit of a different approach to expandable table views - one that aligns with how these kinds of table views are generally built.
There are headers and there are cells. Headers should be tappable, and then cells underneath the headers would show or hide. This can be achieved by adding a gesture recognizer to the header, and when tapped, you just remove all of the cells underneath that header (the section), and viceversa (add cells). Of course, you have to maintain state of which headers are "open" and which headers are "closed."
This is nice for a couple of reasons:
The job of headers and cells are separated which makes code cleaner.
This method flows nicely with how table views are built (headers and cells) and, therefore, there isn't much magic - the code is simply removing or adding cells, and should be compatible with later versions of iOS.
I made a very simple library to achieve this. As long as your table view is set up with UITableView section headers and cells, all you have to do is subclass the tableview and the header. Try it :)
Link: https://github.com/fuzz-productions/FZAccordionTableView
Try Using this code... May be this can help..
And Feel free to Edit the code according to your requirements...
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize myTable;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//myTable.backgroundColor=[UIColor clearColor];
// self.view.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
muArr= [[NSMutableArray alloc]initWithObjects:#"Vinay",#"Anmol",#"Jagriti", nil];
ExpArr=[[NSMutableArray alloc]initWithObjects:#"Useeee",#"Thissss",#"Codeee", nil];
otherExpand=100;
checker=100;
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return muArr.count;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(otherExpand==section)
return ExpArr.count;
return 0;
}
-(BOOL)tableView:(UITableView *)table canCollapse:(NSIndexPath *)indexPath
{
return NO;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *Identifier=#"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:Identifier];
if (cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Identifier];
}
cell.textLabel.text=[ExpArr objectAtIndex:indexPath.row];
cell.textLabel.backgroundColor=[UIColor clearColor];
UIView *viewww=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
viewww.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
cell.backgroundView=viewww;
// cell.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
[tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLineEtched];
[tableView setSeparatorColor:[UIColor purpleColor]];
return cell;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view1=[[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 44)];
[view1.layer setCornerRadius:20];
view1.layer.borderWidth=2;
view1.layer.borderColor=[UIColor brownColor].CGColor;
UILabel *label=[[UILabel alloc]initWithFrame:CGRectMake(10, 0, 295, 44)];
label.backgroundColor=[UIColor clearColor];
label.text=[muArr objectAtIndex:section];
UIButton *btn=[UIButton buttonWithType:UIButtonTypeDetailDisclosure];
btn.frame=CGRectMake(280, -5, 50, 50);
btn.backgroundColor=[UIColor clearColor];
btn.tag=section;
view1.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
label.textColor=[UIColor blackColor];
label.font=[UIFont fontWithName:#"American TypeWriter" size:18];
//btn.backgroundColor=[UIColor blackColor];
[view1 addSubview:btn];
[view1 addSubview:label];
[btn addTarget:self action:#selector(Btntap:) forControlEvents:UIControlEventTouchUpInside];
return view1;
}
-(void)Btntap : (UIButton *)btn
{
if(otherExpand!=100)
{
if (otherExpand==btn.tag)
{
NSMutableArray *tempArr2=[[NSMutableArray alloc]init];
for(int j=0;j<ExpArr.count;j++)
{
NSIndexPath *indexx1=[NSIndexPath indexPathForRow:j inSection:otherExpand];
[tempArr2 addObject:indexx1];
}
checker=0;
otherExpand=100;
[myTable deleteRowsAtIndexPaths:tempArr2 withRowAnimation:UITableViewRowAnimationAutomatic];
}
else
{
NSMutableArray *tempArr2=[[NSMutableArray alloc]init];
for(int j=0;j<ExpArr.count;j++)
{
NSIndexPath *indexx1=[NSIndexPath indexPathForRow:j inSection:otherExpand];
[tempArr2 addObject:indexx1];
}
checker=1;
otherExpand=100;
[myTable deleteRowsAtIndexPaths:tempArr2 withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
if(checker!=0)
{
otherExpand=btn.tag;
//checker=
NSMutableArray *tempArr=[[NSMutableArray alloc]init];
for(int i=0;i<ExpArr.count;i++)
{
NSIndexPath *indexx=[NSIndexPath indexPathForRow:i inSection:btn.tag];
[tempArr addObject:indexx];
}
[myTable insertRowsAtIndexPaths:tempArr withRowAnimation:UITableViewRowAnimationAutomatic];
checker=1;
}
checker=100;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 44;
}
#end
There is a great video in WWDC 2011 called UITableView Changes, Tips and Tricks - session 125 that shows how to do things like this.
Also check out the example code TVAnimationsGestures
You may take a look at this accordion example in Swift: https://github.com/tadija/AEAccordion
It's got very little code to create accordion effect (not by using sections but cells), and as a bonus there is also a solution to use XIB files inside other XIB files (useful for custom cells which use custom views).
Please try this example :
best example for Expandable TableView
https://github.com/OliverLetterer/UIExpandableTableView
TLIndexPathTools can do this sort of thing naturally. In fact, there is are extensions for both expandable sections and expandable tree structures. Try running the Collapse sample project for expandable sections and the Outline sample project for expandable trees.
One advantage of using TLIndexPathTools is that, as a simple, low-level API, it can solve all kinds of dynamic table view and collection view problems using a common approach. And it works interchangeably with Core Data and plain arrays.
it is so easy to create an expandable tableview
here is an example how I did this,
data I m using for this one
struct ItemList {
var name: String
var items: [String]
var collapsed: Bool
init(name: String, items: [String], collapsed: Bool = false) {
self.name = name
self.items = items
self.collapsed = collapsed
}
}
var sections = [ItemList]()
var items: [ItemList] = [
ItemList(name: "Mac", items: ["MacBook", "MacBook Air"]),
ItemList(name: "iPad", items: ["iPad Pro", "iPad Air 2"]),
ItemList(name: "iPhone", items: ["iPhone 7", "iPhone 6"])
]
now just add this piece of code and use accordingly
extension ViewController:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerHeading = UILabel(frame: CGRect(x: 5, y: 10, width: self.view.frame.width, height: 40))
let imageView = UIImageView(frame: CGRect(x: self.view.frame.width - 30, y: 20, width: 20, height: 20))
if items[section].collapsed{
imageView.image = UIImage(named: "collapsed")
}else{
imageView.image = UIImage(named: "expand")
}
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 60))
let tapGuesture = UITapGestureRecognizer(target: self, action: #selector(headerViewTapped))
tapGuesture.numberOfTapsRequired = 1
headerView.addGestureRecognizer(tapGuesture)
headerView.backgroundColor = UIColor.red
headerView.tag = section
headerHeading.text = items[section].name
headerHeading.textColor = .white
headerView.addSubview(headerHeading)
headerView.addSubview(imageView)
return headerView
}
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let itms = items[section]
return !itms.collapsed ? 0 : itms.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
cell.textLabel?.text = items[indexPath.section].items[indexPath.row]
return cell
}
#objc func headerViewTapped(tapped:UITapGestureRecognizer){
print(tapped.view?.tag)
if items[tapped.view!.tag].collapsed == true{
items[tapped.view!.tag].collapsed = false
}else{
items[tapped.view!.tag].collapsed = true
}
if let imView = tapped.view?.subviews[1] as? UIImageView{
if imView.isKind(of: UIImageView.self){
if items[tapped.view!.tag].collapsed{
imView.image = UIImage(named: "collapsed")
}else{
imView.image = UIImage(named: "expand")
}
}
}
tableView.reloadData()
}
}
and the result is Bingo :)
I had the requirement of expanding a single cell to a fuller view and collapsing it back to a summarised view.
So what I did was to design my cell using UIStackView. And I kept the view I didn't want to show in the collapsed state hidden and then showing it when the cell was tapped.
The trick here is to show and hide the view within tableView.beginUpdates() and tableView.endUpdates() statements. In this way table view automatically adjusts the cell height and does it animatedly.
Here is how a basic cell would look in the IB:
Cells Custom Class:
class AccordionCell: UITableViewCell {
#IBOutlet weak var stackView: UIStackView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var extendedDescriptionLabel: UILabel!
var expanded: Bool = false {
didSet {
if let extended = self.extendedDescriptionLabel {
extended.isHidden = !expanded
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.expanded = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
UITableView Delegate Implementation:
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! AccordionCell
cell.titleLabel.text = "Row: \(indexPath.row)"
cell.expanded = indexPath.row == expanded
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? AccordionCell {
tableView.beginUpdates()
if expanded == indexPath.row {
cell.expanded = false
expanded = -1
}
else {
cell.expanded = true
expanded = indexPath.row
}
tableView.endUpdates()
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? AccordionCell {
tableView.beginUpdates()
cell.expanded = false
tableView.endUpdates()
}
}
}
In order to keep track which cell is expanded, I introduced a variable saving indexpath of currently expanded cell so that the right cell is expanded when tableview is scrolled.
Check this Link :
http://iostechnotips.blogspot.in/2014/05/expandable-uitableview.html
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
*Use UITableView delegate method viewForHeaderInSection and return a custom UIView.
*Add a UIButton as subview with action "expandable:(id)sender" check the sender id as section number and reload the table view.
In your .h file
LoadCustomCell *cell1;
NSMutableArray *arrayForBool;
NSMutableArray *questionArray;
NSMutableArray *answerArray;
In your .m file
viewDidLoadMethod {
_faqTblView.estimatedRowHeight = 30;
_faqTblView.rowHeight = UITableViewAutomaticDimension;
arrayForBool = [[NSMutableArray alloc]init];
_questionArray = [[NSMutableArray alloc]init];
_answerArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _questionArray.count; i++) {
[arrayForBool addObject:#"0"];
}
self.faqTblView.dataSource = self;
self.faqTblView .delegate = self;
[self.faqTblView reloadData];
}
after that
#pragma mark - TableView Datasource & Delegate Method.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [_questionArray count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
UILabel *lblText = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 260, 100)];
lblText.text = [_questionArray objectAtIndex:section];
return [lblText getLabelHeight] + 20;(created custom class)
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UITapGestureRecognizer *headerTapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(sectionHeaderTapped:)];
cell1 = [[[NSBundle mainBundle] loadNibNamed:#"LoadCustomCell" owner:self options:nil] objectAtIndex:0];
[cell1 setFrame:CGRectMake(0, 0, cell1.frame.size.width, cell1.frame.size.height)];
NSString *numStr = [NSString stringWithFormat:#"%ld. ",section + 1];
[cell1.sideMenuUserNameLabel setText:[numStr stringByAppendingString:[_questionArray objectAtIndex:section]]];
[cell1 setBackgroundColor:[UIColor lightGrayColor]];
cell1.tag = section;
[cell1 addGestureRecognizer:headerTapped];
return cell1;
}
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:gestureRecognizer.view.tag];
if (indexPath.row == 0) {
BOOL collapsed = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
for (int i = 0; i < [_questionArray count]; i++) {
if (indexPath.section==i) {
[arrayForBool removeObjectAtIndex:i];
[arrayForBool insertObject:[NSString stringWithFormat:#"%d", !collapsed] atIndex:i];
}
}
NSLog(#"%#", arrayForBool);
[self.faqTblView reloadSections:[NSIndexSet indexSetWithIndex:gestureRecognizer.view.tag] withRowAnimation:UITableViewRowAnimationAutomatic];
for (NSIndexPath *indexPath in self.faqTblView.indexPathsForSelectedRows) {
[self.faqTblView deselectRowAtIndexPath:indexPath animated:NO];
}
cell1.imageView.transform = CGAffineTransformMakeRotation(M_PI);
}
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *questionCellIdentifier = #"questionCellIdentifier";
QuestionCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:questionCellIdentifier];
if (cell == nil) {
NSArray * myNib;
myNib =[[NSBundle mainBundle]loadNibNamed:#"QuestionCustomCell" owner:self options:nil];
cell = (QuestionCustomCell *)[myNib lastObject];
}
BOOL manyCells = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
if(manyCells){
cell.questionNameLbl.text = [_answerArray objectAtIndex:indexPath.section];
}
return cell;
}
You can use ExpyTableView
Which makes an expandable section from your given cell. Compatible down to iOS 8.0. You will have flexibility by generating an expandable table view with multiple table view cells. Just manipulate the separators for states and then no one will know you are using multiple cells for expanding.
Other solutions: You manipulate the height to expand a cell, when an update needed in design of the cell, you have to re-construct all the auto-layout constraints or logic in code.
ExpyTableView: You make an expandable table view by using multiple cells and inserting and deleting them(which can mean expanding and collapsing), you will have a great chance in future design requests. All you will have to do is adding a new UITableViewCell and writing the code for it. You will easily have the new design.
All you have to do is to import ExpyTableView and then:
class ViewController: ExpyTableViewDataSource, ExpyTableViewDelegate {
#IBOutlet weak var expandableTableView: ExpyTableView!
// First, set data source and delegate for your table view.
override func viewDidLoad() {
super.viewDidLoad()
expandableTableView.dataSource = self
expandableTableView.delegate = self
}
// Then return your expandable cell instance from expandingCell data source method.
func expandableCell(forSection section: Int, inTableView tableView: ExpyTableView) -> UITableViewCell {
// this cell will be displayed at IndexPath with section: section and row 0
}
}
You can see your former table view section is now an expandable table view section. You can also download the example project and see more detailed examples.
UITableView with Collapsible (expand and collapse) Cells swift 5
Very Easy to Use with Custom Cells
Expendable
Dynamic Content
Check Github Link : https://github.com/Murteza12/ExpandableTablew/wiki/UITableView-with-Collapsible-(expand-and-collapse)-Cells

UICollectionView & custom UICollectionReusableView not works

I'm trying to use custom UICollectionReusableView (which has own class and XIB) in my UICollectionView header. But after fetching data in the place of header I have nothing.
My steps:
Registering class in viewDidLoad:
[self.collectionView registerClass:[CollectionViewHeader class]
forSupplementaryViewOfKind: UICollectionElementKindSectionHeader
withReuseIdentifier:#"HeaderView"];
Trying to show:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableView = nil;
if (kind == UICollectionElementKindSectionHeader) {
CollectionViewHeader *collectionHeader = [self.collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
NSInteger section = [indexPath section];
id <NSFetchedResultsSectionInfo> sectionInfo = [fetchRecipes sections][section];
collectionHeader.headerLabel.text = #"bla-bla-bla";
reusableView = collectionHeader;
}
return reusableView;
}
Can anybody tell me what's wrong? )
Thanks for any advice
I think you are adding label to the xib. So you need to registerNib: for the header view instead of registerClass:
Register Your header nib/xib in the viewDidLoad section.
[self.collectionView registerNib: [UINib nibWithNibName:#"headerCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:#"headerCell"];
Create the custom supplementary view cell.
- (headerCollectionViewCell *)collectionView:(UICollectionView *)collectionViews viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableView = nil;
if (kind == UICollectionElementKindSectionHeader) {
UINib *nib = [UINib nibWithNibName:#"headerCollectionViewCell" bundle:nil];
[collectionViews registerNib:nib forCellWithReuseIdentifier:#"headerCell"];
headerCollectionViewCell *collectionHeader = [collectionViews dequeueReusableCellWithReuseIdentifier:#"headerCell" forIndexPath:indexPath];
collectionHeader.titleLabel.text = #"What";
reusableView = collectionHeader;
}
return reusableView;
}
Just in case if someone needs solution.
You don't have to register the header class using
[self.collectionView registerClass:...]
Just use the layout's delegate method to return the header class.
Actually there is can be few reasons:
(most common)
If u add u'r suplementaryView in storyboard, and then try to register class
[self.stickyCollectionView registerClass:[<CLASSFORSUPPLEMENTARYVIEWW> class]
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:NSStringFromClass([<CLASSFORSUPPLEMENTARYVIEWW> class])];
u will get suplementaryView, but not that u create (standard one) - u will see obj, but actually this is will be like placeholder.
Example:
Here u can see that obj was created, but outlets are nil (in this simple case only one outlet - _monthNameLabel).
I see few times this problem also
If u create separate Obj for handling dataSourse/delegate for collectionView and add reference outlet to it, and in init methods try to register class to u'r outlet (assumed that view created in separate nib file), u will also receive same result like previous, but reason another - u'r outlet is nil here:
As solution u for example can use custom setter for this like:
#pragma mark - CustomAccessors
- (void)setcCollectionView:(UICollectionView *)collectionView
{
_collectionView = collectionView;
[self.stickyCollectionView registerNib:...];
}
as suggested by #Anil Varghese
u can use registerClass instead of registerNib
Xcode 9, Swift 4:
Register UICollectionReusableView in viewDidLoad
self.collectionView.register(UINib(nibName: "DashBoardCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "ReuseIdentifier")
class CustomFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributesForElementsInRect = super.layoutAttributesForElements(in: rect)
var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()
for attributes in attributesForElementsInRect! {
if !(attributes.representedElementKind == UICollectionElementKindSectionHeader
|| attributes.representedElementKind == UICollectionElementKindSectionFooter) {
// cells will be customise here, but Header and Footer will have layout without changes.
}
newAttributesForElementsInRect.append(attributes)
}
return newAttributesForElementsInRect
}
}
class YourViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let headerNib = UINib.init(nibName: "HeaderCell", bundle: nil)
collectionView.register(headerNib, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderCell")
let footerNib = UINib.init(nibName: "FooterCell", bundle: nil)
collectionView.register(footerNib, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: "FooterCell")
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderCell", for: indexPath) as! HeaderCell
return headerView
case UICollectionElementKindSectionFooter:
let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FooterCell", for: indexPath) as! FooterCell
return footerView
default:
return UICollectionReusableView()
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 45)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 25)
}
}
You was registering correctly the class and the delegate is implemented correctly.
The problem can be that the flow layout (by default) is not configured for header view, do you have this line in the code or interface file?
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)_collectionView.collectionViewLayout;
flowLayout.headerReferenceSize = CGSizeMake(CGRectGetWidth(_collectionView.bounds), 100);

UICollectionView cell selection and cell reuse

Upon cell selection, I want to handle changing the cell appearance. I figured the delegate method collectionView:didSelectItemAtIndexPath: & collectionView:didDeselectItemAtIndexPath: is where I should edit the cell.
-(void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
DatasetCell *datasetCell =
(DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath];
[datasetCell replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]];
datasetCell.backgroundColor = [UIColor skyBlueColor];
}
and
-(void)collectionView:(UICollectionView *)collectionView
didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
DatasetCell *datasetCell =
(DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath];
[datasetCell replaceHeaderGradientWith:[UIColor grayGradient]];
datasetCell.backgroundColor = [UIColor myDarkGrayColor];
}
This works fine, except when the cell gets reused. If I select cell at index (0, 0), it changes the appearance but when I scroll down, there is another cell in the selected state.
I believe I should use the UICollectionViewCell method -(void)prepareForReuse to prep the cell for resuse (ie, set the cell appearance to non selected state) but its giving me difficulties.
-(void)prepareForReuse {
if ( self.selected ) {
[self replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]];
self.backgroundColor = [UIColor skyBlueColor];
} else {
[self replaceHeaderGradientWith:[UIColor grayGradient]];
self.backgroundColor = [UIColor myDarkGrayColor];
}
}
When I scroll back to the top, the cell at index (0, 0) is in the deselected state.
When I just used the cell.backgroundView property, to prevent this from happening was to:
-(void)prepareForReuse {
self.selected = FALSE;
}
and the selection state worked as intended.
Any ideas?
Your observation is correct. This behavior is happening due to the reuse of cells. But you dont have to do any thing with the prepareForReuse. Instead do your check in cellForItem and set the properties accordingly. Some thing like..
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cvCell" forIndexPath:indexPath];
if (cell.selected) {
cell.backgroundColor = [UIColor blueColor]; // highlight selection
}
else
{
cell.backgroundColor = [UIColor redColor]; // Default color
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor redColor]; // Default color
}
Framework will handle switching the views for you once you setup your cell's backgroundView and selectedBackgroundView, see example from Managing the Visual State for Selections and Highlights:
UIView* backgroundView = [[UIView alloc] initWithFrame:self.bounds];
backgroundView.backgroundColor = [UIColor redColor];
self.backgroundView = backgroundView;
UIView* selectedBGView = [[UIView alloc] initWithFrame:self.bounds];
selectedBGView.backgroundColor = [UIColor whiteColor];
self.selectedBackgroundView = selectedBGView;
you only need in your class that implements UICollectionViewDelegate enable cells to be highlighted and selected like this:
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{
return YES;
}
This works me.
UICollectionView has changed in iOS 10 introducing some problems to solutions above.
Here is a good guide:
https://littlebitesofcocoa.com/241-uicollectionview-cell-pre-fetching
Cells now stay around for a bit after going off-screen. Which means that sometimes we might not be able to get hold of a cell in didDeselectItemAt indexPath in order to adjust it. It can then show up on screen un-updated and un-recycled. prepareForReuse does not help this corner case.
The easiest solution is disabling the new scrolling by setting isPrefetchingEnabled to false. With this, managing the cell's display with
cellForItemAt didSelect didDeselect works as it used to.
However, if you'd rather keep the new smooth scrolling behaviour it's better to use willDisplay :
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let customCell = cell as! CustomCell
if customCell.isSelected {
customCell.select()
} else {
customCell.unselect()
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
//Don't even need to set selection-specific things here as recycled cells will also go through willDisplay
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
cell?.select()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
cell?.unselect() // <----- this can be null here, and the cell can still come back on screen!
}
With the above you control the cell when it's selected, unselected on-screen, recycled, and just re-displayed.
Anil was on the right track (his solution looks like it should work, I developed this solution independently of his). I still used the prepareForReuse: method to set the cell's selected to FALSE, then in the cellForItemAtIndexPath I check to see if the cell's index is in `collectionView.indexPathsForSelectedItems', if so, highlight it.
In the custom cell:
-(void)prepareForReuse {
self.selected = FALSE;
}
In cellForItemAtIndexPath: to handle highlighting and dehighlighting reuse cells:
if ([collectionView.indexPathsForSelectedItems containsObject:indexPath]) {
[collectionView selectItemAtIndexPath:indexPath animated:FALSE scrollPosition:UICollectionViewScrollPositionNone];
// Select Cell
}
else {
// Set cell to non-highlight
}
And then handle cell highlighting and dehighlighting in the didDeselectItemAtIndexPath: and didSelectItemAtIndexPath:
This works like a charm for me.
I had a horizontal scrolling collection view (I use collection view in Tableview) and I too faced problems withcell reuse, whenever I select one item and scroll towards right, some other cells in the next visible set gets select automatically. Trying to solve this using any custom cell properties like "selected", highlighted etc didnt help me so I came up with the below solution and this worked for me.
Step1:
Create a variable in the collectionView to store the selected index, here I have used a class level variable called selectedIndex
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCVCell *cell = (MyCVCell*)[collectionView dequeueReusableCellWithReuseIdentifier:#"MyCVCell" forIndexPath:indexPath];
// When scrolling happens, set the selection status only if the index matches the selected Index
if (selectedIndex == indexPath.row) {
cell.layer.borderWidth = 1.0;
cell.layer.borderColor = [[UIColor redColor] CGColor];
}
else
{
// Turn off the selection
cell.layer.borderWidth = 0.0;
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];
// Set the index once user taps on a cell
selectedIndex = indexPath.row;
// Set the selection here so that selection of cell is shown to ur user immediately
cell.layer.borderWidth = 1.0;
cell.layer.borderColor = [[UIColor redColor] CGColor];
[cell setNeedsDisplay];
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];
// Set the index to an invalid value so that the cells get deselected
selectedIndex = -1;
cell.layer.borderWidth = 0.0;
[cell setNeedsDisplay];
}
-anoop
What I did to solve this was to make the changes in the customized cell. You have a custom cell called DataSetCell in its class you could do the following (the code is in swift)
override var isSelected: Bool {
didSet {
if isSelected {
changeStuff
} else {
changeOtherStuff
}
}
}
What this does is that every time the cell is selected, deselected, initialized or get called from the reusable queue, that code will run and the changes will be made. Hope this helps you.
In your custom cell create public method:
- (void)showSelection:(BOOL)selection
{
self.contentView.backgroundColor = selection ? [UIColor blueColor] : [UIColor white];
}
Also write redefenition of -prepareForReuse cell method:
- (void)prepareForReuse
{
[self showSelection:NO];
[super prepareForReuse];
}
And in your ViewController you should have _selectedIndexPath variable, which defined in -didSelectItemAtIndexPath and nullified in -didDeselectItemAtIndexPath
NSIndexPath *_selectedIndexPath;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if (_selectedIndexPath) {
[cell showSelection:[indexPath isEqual:_selectedIndexPath]];
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
[cell showSelection:![indexPath isEqual:_selectedIndexPath]];// on/off selection
_selectedIndexPath = [indexPath isEqual:_selectedIndexPath] ? nil : indexPath;
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
[cell showSelection:NO];
_selectedIndexPath = nil;
}
Only #stefanB solution worked for me on iOS 9.3
Here what I have to change for Swift 2
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
//prepare your cell here..
//Add background view for normal cell
let backgroundView: UIView = UIView(frame: cell!.bounds)
backgroundView.backgroundColor = UIColor.lightGrayColor()
cell!.backgroundView = backgroundView
//Add background view for selected cell
let selectedBGView: UIView = UIView(frame: cell!.bounds)
selectedBGView.backgroundColor = UIColor.redColor()
cell!.selectedBackgroundView = selectedBGView
return cell!
}
func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
The problem you encounter comes from the lack of call to super.prepareForReuse().
Some other solutions above, suggesting to update the UI of the cell from the delegate's functions, are leading to a flawed design where the logic of the cell's behaviour is outside of its class. Furthermore, it's extra code that can be simply fixed by calling super.prepareForReuse(). For example :
class myCell: UICollectionViewCell {
// defined in interface builder
#IBOutlet weak var viewSelection : UIView!
override var isSelected: Bool {
didSet {
self.viewSelection.alpha = isSelected ? 1 : 0
}
}
override func prepareForReuse() {
// Do whatever you want here, but don't forget this :
super.prepareForReuse()
// You don't need to do `self.viewSelection.alpha = 0` here
// because `super.prepareForReuse()` will update the property `isSelected`
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.viewSelection.alpha = 0
}
}
With such design, you can even leave the delegate's functions collectionView:didSelectItemAt:/collectionView:didDeselectItemAt: all empty, and the selection process will be totally handled, and behave properly with the cells recycling.
you can just set the selectedBackgroundView of the cell to be backgroundColor=x.
Now any time you tap on cell his selected mode will change automatically and will couse to the background color to change to x.
Thanks to your answer #RDC.
The following codes works with Swift 3
// MARK: - UICollectionViewDataSource protocol
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//prepare your cell here..
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCell
cell.myLabel.text = "my text"
//Add background view for normal cell
let backgroundView: UIView = UIView(frame: cell.bounds)
backgroundView.backgroundColor = UIColor.lightGray
cell.backgroundView = backgroundView
//Add background view for selected cell
let selectedBGView: UIView = UIView(frame: cell.bounds)
selectedBGView.backgroundColor = UIColor.green
cell.selectedBackgroundView = selectedBGView
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return true
}
Changing the cell property such as the cell's background colors shouldn't be done on the UICollectionViewController itself, it should be done inside you CollectionViewCell class. Don't use didSelect and didDeselect, just use this:
class MyCollectionViewCell: UICollectionViewCell
{
override var isSelected: Bool
{
didSet
{
// Your code
}
}
}

Expandable tableView in iphone

I want to make this type of expandable/collapsible table view.
there are categories and subcategories as in picture.
for example "health and beauty" is a category and when i click this cell than its open subcategories as in picture below.
So how can I make this type of table view?
please suggest me.
Finally i get two very useful helping link below which describes exact what the requirement is here
Expanding/Collapsing TableView Sections
Collapsable Table View for iOS
Really, good articles for such kind of expanding/collapsing tableview sections
Use Following code for expandable Cell into UITableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text=[[self.arForTable objectAtIndex:indexPath.row] valueForKey:#"name"];
[cell setIndentationLevel:[[[self.arForTable objectAtIndex:indexPath.row] valueForKey:#"level"] intValue]];
return cell;
}
code for expanding & collapsing rows – TableView DidSelectRow Method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *d=[self.arForTable objectAtIndex:indexPath.row];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
BOOL isAlreadyInserted=NO;
for(NSDictionary *dInner in ar ){
NSInteger index=[self.arForTable indexOfObjectIdenticalTo:dInner];
isAlreadyInserted=(index>0 && index!=NSIntegerMax);
if(isAlreadyInserted) break;
}
if(isAlreadyInserted) {
[self miniMizeThisRows:ar];
} else {
NSUInteger count=indexPath.row+1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar ) {
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[self.arForTable insertObject:dInner atIndex:count++];
}
[tableView insertRowsAtIndexPaths:arCells withRowAnimation:UITableViewRowAnimationLeft];
}
}
}
A Method which will help to minimize & maximize/expand-collapse rows.
-(void)miniMizeThisRows:(NSArray*)ar{
for(NSDictionary *dInner in ar ) {
NSUInteger indexToRemove=[self.arForTable indexOfObjectIdenticalTo:dInner];
NSArray *arInner=[dInner valueForKey:#"Objects"];
if(arInner && [arInner count]>0){
[self miniMizeThisRows:arInner];
}
if([self.arForTable indexOfObjectIdenticalTo:dInner]!=NSNotFound) {
[self.arForTable removeObjectIdenticalTo:dInner];
[self.tableView deleteRowsAtIndexPaths:
[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexToRemove inSection:0]]
withRowAnimation:UITableViewRowAnimationRight];
}
}
}
You can download the source code from my tutorial site.
If this helps: [Access uitableview's expandable and collapsable sections] https://github.com/OliverLetterer/UIExpandableTableView
I have a little bit of a different approach to expandable table views - one that aligns with how these kinds of table views are generally built.
There are headers and there are cells. Headers should be tappable, and then cells underneath the headers would show or hide. This can be achieved by adding a gesture recognizer to the header, and when tapped, you just remove all of the cells underneath that header (the section), and viceversa (add cells). Of course, you have to maintain state of which headers are "open" and which headers are "closed."
This is nice for a couple of reasons:
The job of headers and cells are separated which makes code cleaner.
This method flows nicely with how table views are built (headers and cells) and, therefore, there isn't much magic - the code is simply removing or adding cells, and should be compatible with later versions of iOS.
I made a very simple library to achieve this. As long as your table view is set up with UITableView section headers and cells, all you have to do is subclass the tableview and the header. Try it :)
Link: https://github.com/fuzz-productions/FZAccordionTableView
Try Using this code... May be this can help..
And Feel free to Edit the code according to your requirements...
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize myTable;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//myTable.backgroundColor=[UIColor clearColor];
// self.view.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
muArr= [[NSMutableArray alloc]initWithObjects:#"Vinay",#"Anmol",#"Jagriti", nil];
ExpArr=[[NSMutableArray alloc]initWithObjects:#"Useeee",#"Thissss",#"Codeee", nil];
otherExpand=100;
checker=100;
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return muArr.count;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(otherExpand==section)
return ExpArr.count;
return 0;
}
-(BOOL)tableView:(UITableView *)table canCollapse:(NSIndexPath *)indexPath
{
return NO;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *Identifier=#"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:Identifier];
if (cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Identifier];
}
cell.textLabel.text=[ExpArr objectAtIndex:indexPath.row];
cell.textLabel.backgroundColor=[UIColor clearColor];
UIView *viewww=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
viewww.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
cell.backgroundView=viewww;
// cell.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
[tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLineEtched];
[tableView setSeparatorColor:[UIColor purpleColor]];
return cell;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view1=[[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 44)];
[view1.layer setCornerRadius:20];
view1.layer.borderWidth=2;
view1.layer.borderColor=[UIColor brownColor].CGColor;
UILabel *label=[[UILabel alloc]initWithFrame:CGRectMake(10, 0, 295, 44)];
label.backgroundColor=[UIColor clearColor];
label.text=[muArr objectAtIndex:section];
UIButton *btn=[UIButton buttonWithType:UIButtonTypeDetailDisclosure];
btn.frame=CGRectMake(280, -5, 50, 50);
btn.backgroundColor=[UIColor clearColor];
btn.tag=section;
view1.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"wood.png"]];
label.textColor=[UIColor blackColor];
label.font=[UIFont fontWithName:#"American TypeWriter" size:18];
//btn.backgroundColor=[UIColor blackColor];
[view1 addSubview:btn];
[view1 addSubview:label];
[btn addTarget:self action:#selector(Btntap:) forControlEvents:UIControlEventTouchUpInside];
return view1;
}
-(void)Btntap : (UIButton *)btn
{
if(otherExpand!=100)
{
if (otherExpand==btn.tag)
{
NSMutableArray *tempArr2=[[NSMutableArray alloc]init];
for(int j=0;j<ExpArr.count;j++)
{
NSIndexPath *indexx1=[NSIndexPath indexPathForRow:j inSection:otherExpand];
[tempArr2 addObject:indexx1];
}
checker=0;
otherExpand=100;
[myTable deleteRowsAtIndexPaths:tempArr2 withRowAnimation:UITableViewRowAnimationAutomatic];
}
else
{
NSMutableArray *tempArr2=[[NSMutableArray alloc]init];
for(int j=0;j<ExpArr.count;j++)
{
NSIndexPath *indexx1=[NSIndexPath indexPathForRow:j inSection:otherExpand];
[tempArr2 addObject:indexx1];
}
checker=1;
otherExpand=100;
[myTable deleteRowsAtIndexPaths:tempArr2 withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
if(checker!=0)
{
otherExpand=btn.tag;
//checker=
NSMutableArray *tempArr=[[NSMutableArray alloc]init];
for(int i=0;i<ExpArr.count;i++)
{
NSIndexPath *indexx=[NSIndexPath indexPathForRow:i inSection:btn.tag];
[tempArr addObject:indexx];
}
[myTable insertRowsAtIndexPaths:tempArr withRowAnimation:UITableViewRowAnimationAutomatic];
checker=1;
}
checker=100;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 44;
}
#end
There is a great video in WWDC 2011 called UITableView Changes, Tips and Tricks - session 125 that shows how to do things like this.
Also check out the example code TVAnimationsGestures
You may take a look at this accordion example in Swift: https://github.com/tadija/AEAccordion
It's got very little code to create accordion effect (not by using sections but cells), and as a bonus there is also a solution to use XIB files inside other XIB files (useful for custom cells which use custom views).
Please try this example :
best example for Expandable TableView
https://github.com/OliverLetterer/UIExpandableTableView
TLIndexPathTools can do this sort of thing naturally. In fact, there is are extensions for both expandable sections and expandable tree structures. Try running the Collapse sample project for expandable sections and the Outline sample project for expandable trees.
One advantage of using TLIndexPathTools is that, as a simple, low-level API, it can solve all kinds of dynamic table view and collection view problems using a common approach. And it works interchangeably with Core Data and plain arrays.
it is so easy to create an expandable tableview
here is an example how I did this,
data I m using for this one
struct ItemList {
var name: String
var items: [String]
var collapsed: Bool
init(name: String, items: [String], collapsed: Bool = false) {
self.name = name
self.items = items
self.collapsed = collapsed
}
}
var sections = [ItemList]()
var items: [ItemList] = [
ItemList(name: "Mac", items: ["MacBook", "MacBook Air"]),
ItemList(name: "iPad", items: ["iPad Pro", "iPad Air 2"]),
ItemList(name: "iPhone", items: ["iPhone 7", "iPhone 6"])
]
now just add this piece of code and use accordingly
extension ViewController:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerHeading = UILabel(frame: CGRect(x: 5, y: 10, width: self.view.frame.width, height: 40))
let imageView = UIImageView(frame: CGRect(x: self.view.frame.width - 30, y: 20, width: 20, height: 20))
if items[section].collapsed{
imageView.image = UIImage(named: "collapsed")
}else{
imageView.image = UIImage(named: "expand")
}
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 60))
let tapGuesture = UITapGestureRecognizer(target: self, action: #selector(headerViewTapped))
tapGuesture.numberOfTapsRequired = 1
headerView.addGestureRecognizer(tapGuesture)
headerView.backgroundColor = UIColor.red
headerView.tag = section
headerHeading.text = items[section].name
headerHeading.textColor = .white
headerView.addSubview(headerHeading)
headerView.addSubview(imageView)
return headerView
}
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let itms = items[section]
return !itms.collapsed ? 0 : itms.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
cell.textLabel?.text = items[indexPath.section].items[indexPath.row]
return cell
}
#objc func headerViewTapped(tapped:UITapGestureRecognizer){
print(tapped.view?.tag)
if items[tapped.view!.tag].collapsed == true{
items[tapped.view!.tag].collapsed = false
}else{
items[tapped.view!.tag].collapsed = true
}
if let imView = tapped.view?.subviews[1] as? UIImageView{
if imView.isKind(of: UIImageView.self){
if items[tapped.view!.tag].collapsed{
imView.image = UIImage(named: "collapsed")
}else{
imView.image = UIImage(named: "expand")
}
}
}
tableView.reloadData()
}
}
and the result is Bingo :)
I had the requirement of expanding a single cell to a fuller view and collapsing it back to a summarised view.
So what I did was to design my cell using UIStackView. And I kept the view I didn't want to show in the collapsed state hidden and then showing it when the cell was tapped.
The trick here is to show and hide the view within tableView.beginUpdates() and tableView.endUpdates() statements. In this way table view automatically adjusts the cell height and does it animatedly.
Here is how a basic cell would look in the IB:
Cells Custom Class:
class AccordionCell: UITableViewCell {
#IBOutlet weak var stackView: UIStackView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var extendedDescriptionLabel: UILabel!
var expanded: Bool = false {
didSet {
if let extended = self.extendedDescriptionLabel {
extended.isHidden = !expanded
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.expanded = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
UITableView Delegate Implementation:
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! AccordionCell
cell.titleLabel.text = "Row: \(indexPath.row)"
cell.expanded = indexPath.row == expanded
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? AccordionCell {
tableView.beginUpdates()
if expanded == indexPath.row {
cell.expanded = false
expanded = -1
}
else {
cell.expanded = true
expanded = indexPath.row
}
tableView.endUpdates()
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? AccordionCell {
tableView.beginUpdates()
cell.expanded = false
tableView.endUpdates()
}
}
}
In order to keep track which cell is expanded, I introduced a variable saving indexpath of currently expanded cell so that the right cell is expanded when tableview is scrolled.
Check this Link :
http://iostechnotips.blogspot.in/2014/05/expandable-uitableview.html
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
*Use UITableView delegate method viewForHeaderInSection and return a custom UIView.
*Add a UIButton as subview with action "expandable:(id)sender" check the sender id as section number and reload the table view.
In your .h file
LoadCustomCell *cell1;
NSMutableArray *arrayForBool;
NSMutableArray *questionArray;
NSMutableArray *answerArray;
In your .m file
viewDidLoadMethod {
_faqTblView.estimatedRowHeight = 30;
_faqTblView.rowHeight = UITableViewAutomaticDimension;
arrayForBool = [[NSMutableArray alloc]init];
_questionArray = [[NSMutableArray alloc]init];
_answerArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _questionArray.count; i++) {
[arrayForBool addObject:#"0"];
}
self.faqTblView.dataSource = self;
self.faqTblView .delegate = self;
[self.faqTblView reloadData];
}
after that
#pragma mark - TableView Datasource & Delegate Method.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [_questionArray count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
UILabel *lblText = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 260, 100)];
lblText.text = [_questionArray objectAtIndex:section];
return [lblText getLabelHeight] + 20;(created custom class)
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UITapGestureRecognizer *headerTapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(sectionHeaderTapped:)];
cell1 = [[[NSBundle mainBundle] loadNibNamed:#"LoadCustomCell" owner:self options:nil] objectAtIndex:0];
[cell1 setFrame:CGRectMake(0, 0, cell1.frame.size.width, cell1.frame.size.height)];
NSString *numStr = [NSString stringWithFormat:#"%ld. ",section + 1];
[cell1.sideMenuUserNameLabel setText:[numStr stringByAppendingString:[_questionArray objectAtIndex:section]]];
[cell1 setBackgroundColor:[UIColor lightGrayColor]];
cell1.tag = section;
[cell1 addGestureRecognizer:headerTapped];
return cell1;
}
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:gestureRecognizer.view.tag];
if (indexPath.row == 0) {
BOOL collapsed = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
for (int i = 0; i < [_questionArray count]; i++) {
if (indexPath.section==i) {
[arrayForBool removeObjectAtIndex:i];
[arrayForBool insertObject:[NSString stringWithFormat:#"%d", !collapsed] atIndex:i];
}
}
NSLog(#"%#", arrayForBool);
[self.faqTblView reloadSections:[NSIndexSet indexSetWithIndex:gestureRecognizer.view.tag] withRowAnimation:UITableViewRowAnimationAutomatic];
for (NSIndexPath *indexPath in self.faqTblView.indexPathsForSelectedRows) {
[self.faqTblView deselectRowAtIndexPath:indexPath animated:NO];
}
cell1.imageView.transform = CGAffineTransformMakeRotation(M_PI);
}
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *questionCellIdentifier = #"questionCellIdentifier";
QuestionCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:questionCellIdentifier];
if (cell == nil) {
NSArray * myNib;
myNib =[[NSBundle mainBundle]loadNibNamed:#"QuestionCustomCell" owner:self options:nil];
cell = (QuestionCustomCell *)[myNib lastObject];
}
BOOL manyCells = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
if(manyCells){
cell.questionNameLbl.text = [_answerArray objectAtIndex:indexPath.section];
}
return cell;
}
You can use ExpyTableView
Which makes an expandable section from your given cell. Compatible down to iOS 8.0. You will have flexibility by generating an expandable table view with multiple table view cells. Just manipulate the separators for states and then no one will know you are using multiple cells for expanding.
Other solutions: You manipulate the height to expand a cell, when an update needed in design of the cell, you have to re-construct all the auto-layout constraints or logic in code.
ExpyTableView: You make an expandable table view by using multiple cells and inserting and deleting them(which can mean expanding and collapsing), you will have a great chance in future design requests. All you will have to do is adding a new UITableViewCell and writing the code for it. You will easily have the new design.
All you have to do is to import ExpyTableView and then:
class ViewController: ExpyTableViewDataSource, ExpyTableViewDelegate {
#IBOutlet weak var expandableTableView: ExpyTableView!
// First, set data source and delegate for your table view.
override func viewDidLoad() {
super.viewDidLoad()
expandableTableView.dataSource = self
expandableTableView.delegate = self
}
// Then return your expandable cell instance from expandingCell data source method.
func expandableCell(forSection section: Int, inTableView tableView: ExpyTableView) -> UITableViewCell {
// this cell will be displayed at IndexPath with section: section and row 0
}
}
You can see your former table view section is now an expandable table view section. You can also download the example project and see more detailed examples.
UITableView with Collapsible (expand and collapse) Cells swift 5
Very Easy to Use with Custom Cells
Expendable
Dynamic Content
Check Github Link : https://github.com/Murteza12/ExpandableTablew/wiki/UITableView-with-Collapsible-(expand-and-collapse)-Cells

Resources