diff --git a/Demo/STCollapseTableViewDemo/STCollapseTableViewDemo/ViewController.m b/Demo/STCollapseTableViewDemo/STCollapseTableViewDemo/ViewController.m index 6a7d73c..8fe5827 100644 --- a/Demo/STCollapseTableViewDemo/STCollapseTableViewDemo/ViewController.m +++ b/Demo/STCollapseTableViewDemo/STCollapseTableViewDemo/ViewController.m @@ -10,7 +10,7 @@ #import "STCollapseTableView.h" -@interface ViewController () +@interface ViewController () @property (weak, nonatomic) IBOutlet STCollapseTableView *tableView; @@ -49,7 +49,7 @@ - (void)setupViewController [UIColor greenColor], [UIColor blueColor], [UIColor purpleColor]]; - + self.data = [[NSMutableArray alloc] init]; for (int i = 0 ; i < [colors count] ; i++) { @@ -60,7 +60,7 @@ - (void)setupViewController } [self.data addObject:section]; } - + self.headers = [[NSMutableArray alloc] init]; for (int i = 0 ; i < [colors count] ; i++) { @@ -73,7 +73,8 @@ - (void)setupViewController - (void)viewDidLoad { [super viewDidLoad]; - + + self.tableView.headerViewTapDelegate = self; [self.tableView reloadData]; [self.tableView openSection:0 animated:NO]; } @@ -81,40 +82,40 @@ - (void)viewDidLoad - (IBAction)handleExclusiveButtonTap:(UIButton*)button { [self.tableView setExclusiveSections:!self.tableView.exclusiveSections]; - + [button setTitle:self.tableView.exclusiveSections?@"exclusive":@"!exclusive" forState:UIControlStateNormal]; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return [self.data count]; + return [self.data count]; } - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString* cellIdentifier = @"cell"; - - UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; - - if (!cell) + static NSString* cellIdentifier = @"cell"; + + UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + + if (!cell) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; - } - - NSString* text = [[self.data objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; - cell.textLabel.text = text; - - return cell; + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; + } + + NSString* text = [[self.data objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; + cell.textLabel.text = text; + + return cell; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [[self.data objectAtIndex:section] count]; + return [[self.data objectAtIndex:section] count]; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { - return 40; + return 40; } - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section @@ -122,4 +123,14 @@ - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger return [self.headers objectAtIndex:section]; } -@end +- (void)STCollapseTableView:(STCollapseTableView *)STCollapseTableView didSelectHeaderViewAtSection:(NSInteger)section +{ + [[[UIAlertView alloc] initWithTitle:@"" + message:[NSString stringWithFormat:@"headerView %ld tapped",section] + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil, nil] + show]; +} + +@end \ No newline at end of file diff --git a/README.md b/README.md index 12809a7..5a48520 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ so if you want the first section to be open after your view is loaded, you could - (void)viewDidLoad { [super viewDidLoad]; - + [self.tableView reloadData]; [self.tableView openSection:0 animated:NO]; } @@ -43,6 +43,15 @@ This is automatically done for you in three conditions: * The returned views haven't any UITapGestureRecognizer. * the STCollapseTableView property `shouldHandleHeadersTap` is YES (which is the default value). +---- +``` +@property (nonatomic, weak) id headerViewTapDelegate; +``` +Just as the name, the herderViewTapDelegate is designed to handle hederViews tapping events, you can do things in the delegate method when a specified headerView tapped: + +``` +- (void)STCollapseTableView:(STCollapseTableView *)STCollapseTableView didSelectHeaderViewAtSection:(NSInteger)section; +``` ## Installation To include this component in your project, I recommend you to use [Cocoapods](http://cocoapods.org): diff --git a/STCollapseTableView.podspec b/STCollapseTableView.podspec new file mode 100644 index 0000000..71d4c96 --- /dev/null +++ b/STCollapseTableView.podspec @@ -0,0 +1,19 @@ +Pod::Spec.new do |s| + s.name = "STCollapseTableView" + s.version = "0.1.0" + s.summary = "A UITableView subclass that automatically collapse and/or expand your sections." + s.description = <<-DESC + A UITableView subclass that automatically collapse and/or expand your sections. + You just have to fill your datasource like for a classic UITableView and the magic will happen., + DESC + + s.homepage = "https://github.com/iSofTom/STCollapseTableView" + s.license = "MIT" + s.authors = { "iSofTom" => "thomas@isoftom.com" } + s.source = { :git => "https://github.com/iSofTom/STCollapseTableView.git", :tag => "0.1.2" } + s.platform = :ios, '5.0' + s.source_files = 'STCollapseTableView/*.{h,m}' + s.ios.frameworks = ['Foundation', 'UIKit'] + s.requires_arc = true + +end diff --git a/STCollapseTableView/STCollapseTableView.h b/STCollapseTableView/STCollapseTableView.h index 6b7e99e..1379155 100644 --- a/STCollapseTableView/STCollapseTableView.h +++ b/STCollapseTableView/STCollapseTableView.h @@ -29,6 +29,16 @@ ***********************************************************************************/ #import +@class STCollapseTableView; + +@protocol STCollapseTableViewDelegate + +@optional + +- (void)collapseTableView:(STCollapseTableView *)collapseTableView didOpenSection:(NSInteger)section; +- (void)collapseTableView:(STCollapseTableView *)collapseTableView didCloseSection:(NSInteger)section; + +@end /** * STCollapseTableView is a UITableView subclass that automatically collapse and/or expand your sections. @@ -86,4 +96,6 @@ */ - (BOOL)isOpenSection:(NSUInteger)sectionIndex; -@end +@property (nonatomic, weak) id headerViewTapDelegate; + +@end \ No newline at end of file diff --git a/STCollapseTableView/STCollapseTableView.m b/STCollapseTableView/STCollapseTableView.m index 9a3b893..c217e34 100644 --- a/STCollapseTableView/STCollapseTableView.m +++ b/STCollapseTableView/STCollapseTableView.m @@ -34,9 +34,9 @@ @interface STCollapseTableView () -@property (nonatomic, assign) id collapseDataSource; -@property (nonatomic, assign) id collapseDelegate; -@property (nonatomic, strong) NSMutableArray* sectionsStates; +@property (nonatomic, weak) id collapseDataSource; +@property (nonatomic, weak) id collapseDelegate; +@property (nonatomic, strong) NSMutableArray *sectionsStates; @end @@ -44,54 +44,54 @@ @implementation STCollapseTableView - (id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) + self = [super initWithCoder:aDecoder]; + if (self) { - [self setupCollapseTableView]; - } - return self; + [self setupCollapseTableView]; + } + return self; } - (id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) + self = [super initWithFrame:frame]; + if (self) { - [self setupCollapseTableView]; - } - return self; + [self setupCollapseTableView]; + } + return self; } - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { - self = [super initWithFrame:frame style:style]; - if (self) + self = [super initWithFrame:frame style:style]; + if (self) { - [self setupCollapseTableView]; - } - return self; + [self setupCollapseTableView]; + } + return self; } - (void)setupCollapseTableView { - self.exclusiveSections = YES; + self.exclusiveSections = YES; self.shouldHandleHeadersTap = YES; - self.sectionsStates = [[NSMutableArray alloc] init]; + self.sectionsStates = [[NSMutableArray alloc] init]; } - (void)setDataSource:(id )newDataSource { - if (newDataSource != self.collapseDataSource) + if (newDataSource && newDataSource != self.collapseDataSource) { - self.collapseDataSource = newDataSource; - [self.sectionsStates removeAllObjects]; - [super setDataSource:self.collapseDataSource?self:nil]; - } + self.collapseDataSource = newDataSource; + [self.sectionsStates removeAllObjects]; + [super setDataSource:self.collapseDataSource?self:nil]; + } } - (void)setDelegate:(id)newDelegate { - if (newDelegate != self.collapseDelegate) + if (newDelegate && newDelegate != self.collapseDelegate) { self.collapseDelegate = newDelegate; [super setDelegate:self.collapseDelegate?self:nil]; @@ -100,15 +100,15 @@ - (void)setDelegate:(id)newDelegate - (id)forwardingTargetForSelector:(SEL)aSelector { - if ([self.collapseDataSource respondsToSelector:aSelector]) + if ([self.collapseDataSource respondsToSelector:aSelector]) { - return self.collapseDataSource; - } + return self.collapseDataSource; + } if ([self.collapseDelegate respondsToSelector:aSelector]) { return self.collapseDelegate; } - return nil; + return nil; } - (BOOL)respondsToSelector:(SEL)aSelector @@ -117,8 +117,8 @@ - (BOOL)respondsToSelector:(SEL)aSelector { return [self.collapseDelegate respondsToSelector:aSelector]; } - - return [super respondsToSelector:aSelector] || [self.collapseDataSource respondsToSelector:aSelector] || [self.collapseDelegate respondsToSelector:aSelector]; + + return [super respondsToSelector:aSelector] || [self.collapseDataSource respondsToSelector:aSelector] || [self.collapseDelegate respondsToSelector:aSelector]; } - (void)openSection:(NSUInteger)sectionIndex animated:(BOOL)animated @@ -127,27 +127,31 @@ - (void)openSection:(NSUInteger)sectionIndex animated:(BOOL)animated { return; } - + if ([[self.sectionsStates objectAtIndex:sectionIndex] boolValue]) { return; } - - if (self.exclusiveSections) + + if ([self.headerViewTapDelegate respondsToSelector:@selector(collapseTableView:didOpenSection:)]) { + [self.headerViewTapDelegate collapseTableView:self didOpenSection:sectionIndex]; + } + + if (self.exclusiveSections) { NSUInteger openedSection = [self openedSection]; - - [self setSectionAtIndex:sectionIndex open:YES]; - [self setSectionAtIndex:openedSection open:NO]; - + + [self setSectionAtIndex:sectionIndex open:YES]; + [self setSectionAtIndex:openedSection open:NO]; + if(animated) - { + { NSArray* indexPathsToInsert = [self indexPathsForRowsInSectionAtIndex:sectionIndex]; NSArray* indexPathsToDelete = [self indexPathsForRowsInSectionAtIndex:openedSection]; - + UITableViewRowAnimation insertAnimation; UITableViewRowAnimation deleteAnimation; - + if (openedSection == NSNotFound || sectionIndex < openedSection) { insertAnimation = UITableViewRowAnimationTop; @@ -158,7 +162,7 @@ - (void)openSection:(NSUInteger)sectionIndex animated:(BOOL)animated insertAnimation = UITableViewRowAnimationBottom; deleteAnimation = UITableViewRowAnimationTop; } - + [self beginUpdates]; [self insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation]; [self deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation]; @@ -168,31 +172,34 @@ - (void)openSection:(NSUInteger)sectionIndex animated:(BOOL)animated { [self reloadData]; } - } + } else { - [self setSectionAtIndex:sectionIndex open:YES]; - - if (animated) + [self setSectionAtIndex:sectionIndex open:YES]; + + if (animated) { NSArray* indexPathsToInsert = [self indexPathsForRowsInSectionAtIndex:sectionIndex]; - [self insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationTop]; + [self insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationFade]; } else { [self reloadData]; } - } + } } - (void)closeSection:(NSUInteger)sectionIndex animated:(BOOL)animated { [self setSectionAtIndex:sectionIndex open:NO]; - - if (animated) + if ([self.headerViewTapDelegate respondsToSelector:@selector(collapseTableView:didCloseSection:)]) { + [self.headerViewTapDelegate collapseTableView:self didCloseSection:sectionIndex]; + } + + if (animated) { NSArray* indexPathsToDelete = [self indexPathsForRowsInSectionAtIndex:sectionIndex]; - [self deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop]; + [self deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:NO]; } else { @@ -204,38 +211,38 @@ - (void)toggleSection:(NSUInteger)sectionIndex animated:(BOOL)animated { if (sectionIndex >= [self.sectionsStates count]) { - return; - } - - BOOL sectionIsOpen = [[self.sectionsStates objectAtIndex:sectionIndex] boolValue]; - - if (sectionIsOpen) + return; + } + + BOOL sectionIsOpen = [[self.sectionsStates objectAtIndex:sectionIndex] boolValue]; + + if (sectionIsOpen) { - [self closeSection:sectionIndex animated:animated]; - } + [self closeSection:sectionIndex animated:animated]; + } else { - [self openSection:sectionIndex animated:animated]; - } + [self openSection:sectionIndex animated:animated]; + } } - (BOOL)isOpenSection:(NSUInteger)sectionIndex { if (sectionIndex >= [self.sectionsStates count]) { - return NO; - } - return [[self.sectionsStates objectAtIndex:sectionIndex] boolValue]; + return NO; + } + return [[self.sectionsStates objectAtIndex:sectionIndex] boolValue]; } - (void)setExclusiveSections:(BOOL)exclusiveSections { _exclusiveSections = exclusiveSections; - + if (self.exclusiveSections) { NSInteger firstSection = NSNotFound; - + for (NSUInteger index = 0 ; index < [self.sectionsStates count] ; index++) { if ([[self.sectionsStates objectAtIndex:index] boolValue]) @@ -253,50 +260,67 @@ - (void)setExclusiveSections:(BOOL)exclusiveSections } } +#pragma mark managing section updates (overload table view a bit...) + +- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + [self.sectionsStates removeObjectsAtIndexes: sections]; + [super deleteSections:sections withRowAnimation:animation]; +} + +- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + [self.sectionsStates insertObject:@NO atIndex:idx]; + }]; + + [super insertSections:sections withRowAnimation:animation]; +} + #pragma mark - DataSource - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - return [self.collapseDataSource tableView:tableView cellForRowAtIndexPath:indexPath]; + return [self.collapseDataSource tableView:tableView cellForRowAtIndexPath:indexPath]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - if ([[self.sectionsStates objectAtIndex:section] boolValue]) + if ([[self.sectionsStates objectAtIndex:section] boolValue]) { - return [self.collapseDataSource tableView:tableView numberOfRowsInSection:section]; - } - return 0; + return [self.collapseDataSource tableView:tableView numberOfRowsInSection:section]; + } + return 0; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - int nbSection = [self.collapseDataSource numberOfSectionsInTableView:tableView]; - - while (nbSection < [self.sectionsStates count]) + int nbSection = (int)[self.collapseDataSource numberOfSectionsInTableView:tableView]; + + while (nbSection < [self.sectionsStates count]) { - [self.sectionsStates removeLastObject]; - } - - while (nbSection > [self.sectionsStates count]) + [self.sectionsStates removeLastObject]; + } + + while (nbSection > [self.sectionsStates count]) { - [self.sectionsStates addObject:@NO]; - } - - return nbSection; + [self.sectionsStates addObject:@NO]; + } + + return nbSection; } #pragma mark - Delegate - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - UIView* view = [self.collapseDelegate tableView:tableView viewForHeaderInSection:section]; - + UIView *view = [self.collapseDelegate tableView:tableView viewForHeaderInSection:section]; + if (self.shouldHandleHeadersTap) { - NSArray* gestures = view.gestureRecognizers; + NSArray *gestures = view.gestureRecognizers; BOOL tapGestureFound = NO; - for (UIGestureRecognizer* gesture in gestures) + for (UIGestureRecognizer *gesture in gestures) { if ([gesture isKindOfClass:[UITapGestureRecognizer class]]) { @@ -304,14 +328,14 @@ - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger break; } } - + if (!tapGestureFound) { - [view setTag:section]; [view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]]; } + [view setTag:section]; } - + return view; } @@ -328,31 +352,31 @@ - (void)handleTapGesture:(UITapGestureRecognizer*)tap - (NSArray*)indexPathsForRowsInSectionAtIndex:(NSUInteger)sectionIndex { - if (sectionIndex >= [self.sectionsStates count]) + if (sectionIndex >= [self.sectionsStates count]) { - return nil; - } - - NSInteger numberOfRows = [self.collapseDataSource tableView:self numberOfRowsInSection:sectionIndex]; - - NSMutableArray* array = [[NSMutableArray alloc] init]; - - for (int i = 0 ; i < numberOfRows ; i++) + return nil; + } + + NSInteger numberOfRows = [self.collapseDataSource tableView:self numberOfRowsInSection:sectionIndex]; + + NSMutableArray* array = [[NSMutableArray alloc] init]; + + for (int i = 0 ; i < numberOfRows ; i++) { - [array addObject:[NSIndexPath indexPathForRow:i inSection:sectionIndex]]; - } - + [array addObject:[NSIndexPath indexPathForRow:i inSection:sectionIndex]]; + } + return array; } - (void)setSectionAtIndex:(NSUInteger)sectionIndex open:(BOOL)open { - if (sectionIndex >= [self.sectionsStates count]) + if (sectionIndex >= [self.sectionsStates count]) { - return; - } - - [self.sectionsStates replaceObjectAtIndex:sectionIndex withObject:@(open)]; + return; + } + + [self.sectionsStates replaceObjectAtIndex:sectionIndex withObject:@(open)]; } - (NSUInteger)openedSection @@ -361,7 +385,7 @@ - (NSUInteger)openedSection { return NSNotFound; } - + for (NSUInteger index = 0 ; index < [self.sectionsStates count] ; index++) { if ([[self.sectionsStates objectAtIndex:index] boolValue]) @@ -373,4 +397,4 @@ - (NSUInteger)openedSection return NSNotFound; } -@end +@end \ No newline at end of file