|
29 | 29 | #import "FZAccordionTableView.h"
|
30 | 30 | #import <objc/runtime.h>
|
31 | 31 |
|
32 |
| -#pragma mark - |
33 |
| -#pragma mark - FZAccordionTableViewSectionInfo |
34 |
| -#pragma mark - |
35 |
| - |
36 |
| -@interface FZAccordionTableViewSectionInfo : NSObject |
37 |
| -@property (nonatomic, getter=isOpen) BOOL open; |
38 |
| -@property (nonatomic) NSInteger numberOfRows; |
39 |
| -@end |
40 |
| - |
41 |
| -@implementation FZAccordionTableViewSectionInfo |
42 |
| -- (instancetype)initWithNumberOfRows:(NSInteger)numberOfRows { |
43 |
| - if (self = [super init]) { |
44 |
| - _numberOfRows = numberOfRows; |
45 |
| - } |
46 |
| - return self; |
47 |
| -} |
48 |
| -@end |
49 |
| - |
50 | 32 | #pragma mark -
|
51 | 33 | #pragma mark - FZAccordionTableViewHeaderView
|
52 | 34 | #pragma mark -
|
@@ -89,6 +71,24 @@ - (void)touchedHeaderView:(UITapGestureRecognizer *)recognizer {
|
89 | 71 |
|
90 | 72 | @end
|
91 | 73 |
|
| 74 | +#pragma mark - |
| 75 | +#pragma mark - FZAccordionTableViewSectionInfo |
| 76 | +#pragma mark - |
| 77 | + |
| 78 | +@interface FZAccordionTableViewSectionInfo : NSObject |
| 79 | +@property (nonatomic, getter=isOpen) BOOL open; |
| 80 | +@property (nonatomic) NSInteger numberOfRows; |
| 81 | +@end |
| 82 | + |
| 83 | +@implementation FZAccordionTableViewSectionInfo |
| 84 | +- (instancetype)initWithNumberOfRows:(NSInteger)numberOfRows { |
| 85 | + if (self = [super init]) { |
| 86 | + _numberOfRows = numberOfRows; |
| 87 | + } |
| 88 | + return self; |
| 89 | +} |
| 90 | +@end |
| 91 | + |
92 | 92 | #pragma mark -
|
93 | 93 | #pragma mark - FZAccordionTableView
|
94 | 94 | #pragma mark -
|
@@ -130,64 +130,12 @@ - (void)initializeVars {
|
130 | 130 | _keepOneSectionOpen = NO;
|
131 | 131 | }
|
132 | 132 |
|
133 |
| -#pragma mark - Helper methods - |
134 |
| - |
135 |
| -- (BOOL)isSectionOpen:(NSInteger)section { |
136 |
| - return [self.sectionInfos[section] isOpen]; |
137 |
| -} |
138 |
| - |
139 |
| -- (void)markSection:(NSInteger)section open:(BOOL)open { |
140 |
| - [self.sectionInfos[section] setOpen:open]; |
141 |
| -} |
142 |
| - |
143 |
| -- (NSArray *)getIndexPathsForSection:(NSInteger)section { |
144 |
| - NSInteger numOfRows = [self.sectionInfos[section] numberOfRows]; |
145 |
| - NSMutableArray *indexPaths = [NSMutableArray array]; |
146 |
| - for (int row = 0; row < numOfRows; row++) { |
147 |
| - [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:section]]; |
148 |
| - } |
149 |
| - return indexPaths; |
150 |
| -} |
151 |
| - |
152 |
| -- (void)toggleSection:(NSInteger)section { |
153 |
| - FZAccordionTableViewHeaderView *headerView = (FZAccordionTableViewHeaderView *)[self headerViewForSection:section]; |
154 |
| - [self tappedHeaderView:headerView]; |
155 |
| -} |
156 |
| - |
157 |
| -- (NSInteger)sectionForHeaderView:(UITableViewHeaderFooterView *)headerView { |
158 |
| - |
159 |
| - NSInteger section = NSNotFound; |
160 |
| - NSInteger minSection = 0; |
161 |
| - NSInteger maxSection = self.numberOfSections; |
162 |
| - |
163 |
| - CGRect headerViewFrame = headerView.frame; |
164 |
| - CGRect compareHeaderViewFrame; |
165 |
| - |
166 |
| - while (minSection <= maxSection) { |
167 |
| - NSInteger middleSection = (minSection+maxSection)/2; |
168 |
| - compareHeaderViewFrame = [self rectForHeaderInSection:middleSection]; |
169 |
| - if (CGRectEqualToRect(headerViewFrame, compareHeaderViewFrame)) { |
170 |
| - section = middleSection; |
171 |
| - break; |
172 |
| - } |
173 |
| - else if (headerViewFrame.origin.y > compareHeaderViewFrame.origin.y) { |
174 |
| - minSection = middleSection+1; |
175 |
| - } |
176 |
| - else { |
177 |
| - maxSection = middleSection-1; |
178 |
| - section = maxSection; // Occurs when headerView sticks to the top |
179 |
| - } |
180 |
| - } |
181 |
| - |
182 |
| - return section; |
183 |
| -} |
| 133 | +#pragma mark - Override Setters - |
184 | 134 |
|
185 |
| -- (BOOL)canInteractWithHeaderAtSection:(NSInteger)section { |
186 |
| - BOOL canInteractWithHeader = YES; |
187 |
| - if ([self.delegate respondsToSelector:@selector(tableView:canInteractWithHeaderAtSection:)]) { |
188 |
| - canInteractWithHeader = [self.subclassDelegate tableView:self canInteractWithHeaderAtSection:section]; |
189 |
| - } |
190 |
| - return canInteractWithHeader; |
| 135 | +- (void)setInitialOpenSections:(NSSet *)initialOpenedSections { |
| 136 | + NSAssert(self.sectionInfos.count == 0, @"'initialOpenedSections' MUST be set before the tableView has started loading data."); |
| 137 | + _initialOpenSections = initialOpenedSections; |
| 138 | + _mutableInitialOpenSections = [initialOpenedSections mutableCopy]; |
191 | 139 | }
|
192 | 140 |
|
193 | 141 | #pragma mark - UITableView Overrides -
|
@@ -237,15 +185,69 @@ - (BOOL)respondsToSelector:(SEL)aSelector {
|
237 | 185 | return [super respondsToSelector:aSelector] || [self.subclassDelegate respondsToSelector:aSelector] || [self.subclassDataSource respondsToSelector:aSelector];
|
238 | 186 | }
|
239 | 187 |
|
240 |
| -#pragma mark - Override Setters - |
| 188 | +#pragma mark - Public Helper Methods - |
241 | 189 |
|
242 |
| -- (void)setInitialOpenSections:(NSSet *)initialOpenedSections { |
243 |
| - NSAssert(self.sectionInfos.count == 0, @"'initialOpenedSections' MUST be set before the tableView has started loading data."); |
244 |
| - _initialOpenSections = initialOpenedSections; |
245 |
| - _mutableInitialOpenSections = [initialOpenedSections mutableCopy]; |
| 190 | +- (BOOL)isSectionOpen:(NSInteger)section { |
| 191 | + return [self.sectionInfos[section] isOpen]; |
246 | 192 | }
|
247 | 193 |
|
248 |
| -#pragma mark - FZAccordionTableViewHeaderViewDelegate - |
| 194 | +- (void)toggleSection:(NSInteger)section { |
| 195 | + FZAccordionTableViewHeaderView *headerView = (FZAccordionTableViewHeaderView *)[self headerViewForSection:section]; |
| 196 | + [self tappedHeaderView:headerView]; |
| 197 | +} |
| 198 | + |
| 199 | +- (NSInteger)sectionForHeaderView:(UITableViewHeaderFooterView *)headerView { |
| 200 | + |
| 201 | + NSInteger section = NSNotFound; |
| 202 | + NSInteger minSection = 0; |
| 203 | + NSInteger maxSection = self.numberOfSections; |
| 204 | + |
| 205 | + CGRect headerViewFrame = headerView.frame; |
| 206 | + CGRect compareHeaderViewFrame; |
| 207 | + |
| 208 | + while (minSection <= maxSection) { |
| 209 | + NSInteger middleSection = (minSection+maxSection)/2; |
| 210 | + compareHeaderViewFrame = [self rectForHeaderInSection:middleSection]; |
| 211 | + if (CGRectEqualToRect(headerViewFrame, compareHeaderViewFrame)) { |
| 212 | + section = middleSection; |
| 213 | + break; |
| 214 | + } |
| 215 | + else if (headerViewFrame.origin.y > compareHeaderViewFrame.origin.y) { |
| 216 | + minSection = middleSection+1; |
| 217 | + } |
| 218 | + else { |
| 219 | + maxSection = middleSection-1; |
| 220 | + section = maxSection; // Occurs when headerView sticks to the top |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + return section; |
| 225 | +} |
| 226 | + |
| 227 | +#pragma mark - Private Utility Helpers - |
| 228 | + |
| 229 | +- (void)markSection:(NSInteger)section open:(BOOL)open { |
| 230 | + [self.sectionInfos[section] setOpen:open]; |
| 231 | +} |
| 232 | + |
| 233 | +- (NSArray *)getIndexPathsForSection:(NSInteger)section { |
| 234 | + NSInteger numOfRows = [self.sectionInfos[section] numberOfRows]; |
| 235 | + NSMutableArray *indexPaths = [NSMutableArray array]; |
| 236 | + for (int row = 0; row < numOfRows; row++) { |
| 237 | + [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:section]]; |
| 238 | + } |
| 239 | + return indexPaths; |
| 240 | +} |
| 241 | + |
| 242 | +- (BOOL)canInteractWithHeaderAtSection:(NSInteger)section { |
| 243 | + BOOL canInteractWithHeader = YES; |
| 244 | + if ([self.delegate respondsToSelector:@selector(tableView:canInteractWithHeaderAtSection:)]) { |
| 245 | + canInteractWithHeader = [self.subclassDelegate tableView:self canInteractWithHeaderAtSection:section]; |
| 246 | + } |
| 247 | + return canInteractWithHeader; |
| 248 | +} |
| 249 | + |
| 250 | +#pragma mark - <FZAccordionTableViewHeaderViewDelegate> - |
249 | 251 |
|
250 | 252 | - (void)tappedHeaderView:(FZAccordionTableViewHeaderView *)sectionHeaderView {
|
251 | 253 | NSParameterAssert(sectionHeaderView);
|
@@ -285,12 +287,44 @@ - (void)tappedHeaderView:(FZAccordionTableViewHeaderView *)sectionHeaderView {
|
285 | 287 |
|
286 | 288 | // Auto-collapse the rest of the opened sections
|
287 | 289 | if (!self.allowMultipleSectionsOpen && !openSection) {
|
288 |
| - [self autoCollapseAllSectionsExceptSection:section]; |
| 290 | + [self closeAllSectionsExcept:section]; |
289 | 291 | }
|
290 | 292 |
|
291 | 293 | [self endUpdates];
|
292 | 294 | }
|
293 | 295 |
|
| 296 | +- (void)closeAllSectionsExcept:(NSInteger)section { |
| 297 | + // Get all of the sections that we need to close |
| 298 | + NSMutableSet *sectionsToClose = [[NSMutableSet alloc] init]; |
| 299 | + for (NSInteger i = 0; i < self.numberOfSections; i++) { |
| 300 | + FZAccordionTableViewSectionInfo *sectionInfo = self.sectionInfos[i]; |
| 301 | + if (section != i && sectionInfo.isOpen) { |
| 302 | + [sectionsToClose addObject:@(i)]; |
| 303 | + } |
| 304 | + } |
| 305 | + |
| 306 | + // Close the found sections |
| 307 | + for (NSNumber *sectionToClose in sectionsToClose) { |
| 308 | + |
| 309 | + // Change animations based off which sections are closed |
| 310 | + UITableViewRowAnimation closeAnimation = UITableViewRowAnimationTop; |
| 311 | + if (section < sectionToClose.integerValue) { |
| 312 | + closeAnimation = UITableViewRowAnimationBottom; |
| 313 | + } |
| 314 | + if (self.enableAnimationFix) { |
| 315 | + if (!self.allowsMultipleSelection && |
| 316 | + (sectionToClose.integerValue == self.sectionInfos.count - 1 || |
| 317 | + sectionToClose.integerValue == self.sectionInfos.count - 2)) { |
| 318 | + closeAnimation = UITableViewRowAnimationFade; |
| 319 | + } |
| 320 | + } |
| 321 | + |
| 322 | + [self closeSection:sectionToClose.integerValue withHeaderView:(FZAccordionTableViewHeaderView *)[self headerViewForSection:sectionToClose.integerValue] rowAnimation:closeAnimation]; |
| 323 | + } |
| 324 | +} |
| 325 | + |
| 326 | +#pragma mark - Open / Closing |
| 327 | + |
294 | 328 | - (void)openSection:(NSInteger)section withHeaderView:(FZAccordionTableViewHeaderView *)sectionHeaderView {
|
295 | 329 | if (![self canInteractWithHeaderAtSection:section]) {
|
296 | 330 | return;
|
@@ -354,36 +388,6 @@ - (void)closeSection:(NSInteger)section withHeaderView:(FZAccordionTableViewHead
|
354 | 388 | [self endUpdates];
|
355 | 389 | }
|
356 | 390 |
|
357 |
| -- (void)autoCollapseAllSectionsExceptSection:(NSInteger)section { |
358 |
| - // Get all of the sections that we need to close |
359 |
| - NSMutableSet *sectionsToClose = [[NSMutableSet alloc] init]; |
360 |
| - for (NSInteger i = 0; i < self.numberOfSections; i++) { |
361 |
| - FZAccordionTableViewSectionInfo *sectionInfo = self.sectionInfos[i]; |
362 |
| - if (section != i && sectionInfo.isOpen) { |
363 |
| - [sectionsToClose addObject:@(i)]; |
364 |
| - } |
365 |
| - } |
366 |
| - |
367 |
| - // Close the found sections |
368 |
| - for (NSNumber *sectionToClose in sectionsToClose) { |
369 |
| - |
370 |
| - // Change animations based off which sections are closed |
371 |
| - UITableViewRowAnimation closeAnimation = UITableViewRowAnimationTop; |
372 |
| - if (section < sectionToClose.integerValue) { |
373 |
| - closeAnimation = UITableViewRowAnimationBottom; |
374 |
| - } |
375 |
| - if (self.enableAnimationFix) { |
376 |
| - if (!self.allowsMultipleSelection && |
377 |
| - (sectionToClose.integerValue == self.sectionInfos.count - 1 || |
378 |
| - sectionToClose.integerValue == self.sectionInfos.count - 2)) { |
379 |
| - closeAnimation = UITableViewRowAnimationFade; |
380 |
| - } |
381 |
| - } |
382 |
| - |
383 |
| - [self closeSection:sectionToClose.integerValue withHeaderView:(FZAccordionTableViewHeaderView *)[self headerViewForSection:sectionToClose.integerValue] rowAnimation:closeAnimation]; |
384 |
| - } |
385 |
| -} |
386 |
| - |
387 | 391 | #pragma mark - <UITableViewDataSource> -
|
388 | 392 |
|
389 | 393 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
|
0 commit comments