|
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