├── AccordionTableView ├── AccordionHeaderView.xib ├── AccordionTableView.h ├── AccordionTableView.m ├── AccordionTableView.xib ├── AccordionViewCell.xib └── Utility │ ├── UIView+NibLoading.h │ └── UIView+NibLoading.m ├── Demo Project ├── Accordion.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── Accordion.xccheckout │ │ └── xcuserdata │ │ │ ├── francescacorsini.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ │ └── ligeia9.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ ├── francescacorsini.xcuserdatad │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ │ ├── Accordion.xcscheme │ │ │ └── xcschememanagement.plist │ │ └── ligeia9.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── Accordion.xcscheme │ │ └── xcschememanagement.plist ├── Accordion │ ├── AccordionTableView │ │ ├── AccordionHeaderView.xib │ │ ├── AccordionTableView.h │ │ ├── AccordionTableView.m │ │ ├── AccordionTableView.xib │ │ ├── AccordionViewCell.xib │ │ └── Utility │ │ │ ├── UIView+NibLoading.h │ │ │ └── UIView+NibLoading.m │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ ├── ViewController_iPad.xib │ ├── ViewController_iPhone.xib │ └── main.m └── Default-568h@2x.png └── README.md /AccordionTableView/AccordionHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /AccordionTableView/AccordionTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AccordionTableView.h 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 14/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIView+NibLoading.h" 11 | 12 | @class AccordionTableView; 13 | 14 | #pragma mark - Defines 15 | 16 | // available types of animations 17 | typedef NS_ENUM(NSInteger, AccordionAnimation) { 18 | AccordionAnimationFade, 19 | AccordionAnimationRight, 20 | AccordionAnimationLeft, 21 | AccordionAnimationTop, 22 | AccordionAnimationBottom, 23 | AccordionAnimationNone, 24 | AccordionAnimationMiddle 25 | }; 26 | 27 | #pragma mark - AccordionContentBean 28 | 29 | // the content bean of the accordion tableview: AccordionContentBean 30 | @interface AccordionContentBean : NSObject 31 | 32 | @end 33 | 34 | #pragma mark - AccordionHeaderView 35 | 36 | @protocol AccordionHeaderViewDelegate; 37 | 38 | // the header of the accordion tableview: AccordionHeaderView 39 | @interface AccordionHeaderView : UITableViewHeaderFooterView 40 | 41 | @end 42 | 43 | #pragma mark - AccordionViewCell 44 | 45 | @protocol AccordionViewCellDelegate; 46 | 47 | // the cell of the accordion tableview: AccordionViewCell 48 | @interface AccordionViewCell : UITableViewCell 49 | 50 | @end 51 | 52 | #pragma mark - AccordionTableView 53 | 54 | // protocol delegate of AccordionTableView 55 | @protocol AccordionViewDelegate 56 | 57 | // returns the array of the headers 58 | // objects can be NSString or id 59 | - (NSArray *)accordionViewHeaders; 60 | 61 | // returns the array of the content for each section 62 | // objects can be NSString or id 63 | - (NSArray *)accordionView:(AccordionTableView *)accordionView dataForHeaderInSection:(NSInteger)section; 64 | 65 | @optional 66 | 67 | // returns the header being tapped for each section 68 | - (UIView *)accordionView:(AccordionTableView *)accordionView viewForHeaderInSection:(NSInteger)section; 69 | 70 | // returns the cell of a section 71 | - (AccordionViewCell *)accordionView:(AccordionTableView *)accordionView cellForRowAtIndexPath:(NSIndexPath *)indexPath; 72 | 73 | // tells the delegate that the specified row is now selected 74 | - (void)accordionView:(AccordionTableView *)accordionView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; 75 | 76 | @end 77 | 78 | // the view that contains the accordion tableview: AccordionTableView 79 | @interface AccordionTableView : NibLoadedView 80 | 81 | // the tableview 82 | @property (nonatomic, weak) IBOutlet UITableView *table; 83 | 84 | // the delegate object 85 | @property (nonatomic, weak) IBOutlet id delegate; 86 | 87 | // the animation type 88 | @property (nonatomic) AccordionAnimation animation; 89 | 90 | // YES: a tap gesture on an open section performs a close animation 91 | // NO: a tap gesture on an open section doesn't perform a close animation (default value) 92 | @property (nonatomic) BOOL handleOpenSection; 93 | 94 | // height of the header being tapped 95 | @property (nonatomic) CGFloat headerHeight; 96 | 97 | // load the accordion for the first time 98 | - (void)load; 99 | 100 | // reloads the rows and sections of the table view 101 | - (void)reloadData; 102 | 103 | // reloads the specified sections using a given animation effect 104 | - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; 105 | 106 | // selects a section in the accordion 107 | - (void)selectSection:(NSInteger)section; 108 | 109 | // registers a nib object containing a header with the accordion table view 110 | - (void)registerNibForHeaderView:(UINib *)nib; 111 | 112 | // registers a class for use in creating new accordion table header 113 | - (void)registerClassForHeaderView:(Class)aClass; 114 | 115 | // registers a nib object containing a cell with the accordion table view 116 | - (void)registerNibForCell:(UINib *)nib; 117 | 118 | // registers a class for use in creating new accordion table cells 119 | - (void)registerClassForCell:(Class)aClass; 120 | 121 | @end -------------------------------------------------------------------------------- /AccordionTableView/AccordionTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AccordionTableView.m 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 14/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import "AccordionTableView.h" 10 | 11 | #pragma mark - AccordionContentBean 12 | 13 | @interface AccordionContentBean() 14 | 15 | // YES: the content is open 16 | // NO: the content isn't open 17 | @property (nonatomic) BOOL open; 18 | 19 | // the data of the content 20 | @property (nonatomic, strong) id content; 21 | 22 | // init 23 | - (id)initWithContent:(id)content; 24 | 25 | @end 26 | 27 | @implementation AccordionContentBean 28 | 29 | #pragma mark Init 30 | 31 | - (id)initWithContent:(id)content 32 | { 33 | self = [super init]; 34 | if (self) { 35 | self.content = content; 36 | self.open = NO; 37 | } 38 | return self; 39 | } 40 | 41 | @end 42 | 43 | #pragma mark - AccordionHeaderView 44 | 45 | typedef void (^AccordionHeaderBlock)(NSInteger); 46 | 47 | @interface AccordionHeaderView() 48 | 49 | // utility block for notification of the section being tapped 50 | @property (copy) AccordionHeaderBlock accordionHeaderBlock; 51 | 52 | // the area tapped 53 | @property (nonatomic, weak) IBOutlet UIButton *button; 54 | 55 | // the index section of the accordion 56 | @property (nonatomic) NSInteger section; 57 | 58 | @end 59 | 60 | @implementation AccordionHeaderView 61 | 62 | #pragma mark Init 63 | 64 | 65 | - (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier 66 | { 67 | self = [super initWithReuseIdentifier:reuseIdentifier]; 68 | if (self) { 69 | } 70 | return self; 71 | } 72 | 73 | - (id)initWithCoder:(NSCoder *)aDecoder 74 | { 75 | self = [super initWithCoder:aDecoder]; 76 | if (self) { 77 | } 78 | return self; 79 | } 80 | 81 | - (id)initWithFrame:(CGRect)frame 82 | { 83 | self = [super initWithFrame:frame]; 84 | if (self) { 85 | } 86 | return self; 87 | } 88 | 89 | - (void)awakeFromNib 90 | { 91 | [super awakeFromNib]; 92 | } 93 | 94 | #pragma mark Identifier 95 | 96 | + (NSString *)reuseIdentifier 97 | { 98 | return NSStringFromClass([self class]); 99 | } 100 | 101 | #pragma mark UI 102 | 103 | - (void)setContent:(id)obj block:(AccordionHeaderBlock)block 104 | { 105 | self.accordionHeaderBlock = [block copy]; 106 | [self.button setTitle:(NSString*)obj forState:UIControlStateNormal]; 107 | } 108 | 109 | - (IBAction)selectSection:(id)sender 110 | { 111 | if (self.accordionHeaderBlock != nil) 112 | self.accordionHeaderBlock(self.section); 113 | } 114 | 115 | @end 116 | 117 | #pragma mark - AccordionViewCell 118 | 119 | typedef void (^AccordionViewCellBlock)(NSString*, NSInteger, id); 120 | 121 | @interface AccordionViewCell() 122 | 123 | // utility block for notification of some actions 124 | @property (copy) AccordionViewCellBlock accordionViewCellBlock; 125 | 126 | // the main label of the cell 127 | @property (nonatomic, weak) IBOutlet UILabel *label; 128 | 129 | // the index row of the cell 130 | @property (nonatomic) NSInteger row; 131 | 132 | @end 133 | 134 | @implementation AccordionViewCell 135 | 136 | #pragma mark Init 137 | 138 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 139 | { 140 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 141 | if (self) { 142 | } 143 | return self; 144 | } 145 | 146 | - (id)initWithCoder:(NSCoder *)aDecoder 147 | { 148 | self = [super initWithCoder:aDecoder]; 149 | if (self) { 150 | } 151 | return self; 152 | } 153 | 154 | - (id)initWithFrame:(CGRect)frame 155 | { 156 | self = [super initWithFrame:frame]; 157 | if (self) { 158 | } 159 | return self; 160 | } 161 | 162 | - (void)awakeFromNib 163 | { 164 | [super awakeFromNib]; 165 | } 166 | 167 | #pragma mark Identifier 168 | 169 | + (NSString *)reuseIdentifier 170 | { 171 | return NSStringFromClass([self class]); 172 | } 173 | 174 | #pragma mark UI 175 | 176 | - (void)setContent:(id)obj block:(AccordionViewCellBlock)block 177 | { 178 | self.accordionViewCellBlock = [block copy]; 179 | [self.label setText:(NSString*)obj]; 180 | } 181 | 182 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 183 | { 184 | [super setSelected:selected animated:animated]; 185 | } 186 | 187 | - (IBAction)someAction:(id)sender 188 | { 189 | if (self.accordionViewCellBlock != nil) 190 | self.accordionViewCellBlock(@"action_name", self.row, @[@"1"]); 191 | } 192 | 193 | @end 194 | 195 | 196 | #pragma mark - AccordionTableView 197 | 198 | @interface AccordionTableView() 199 | 200 | // the data of contents of the tableview 201 | @property (nonatomic, strong) NSMutableArray *data; 202 | 203 | // the data of headers of the tableview 204 | @property (nonatomic, strong) NSArray *headers; 205 | 206 | @end 207 | 208 | @implementation AccordionTableView 209 | 210 | #pragma mark Init 211 | 212 | - (id)initWithCoder:(NSCoder *)aDecoder 213 | { 214 | self = [super initWithCoder:aDecoder]; 215 | if (self) { 216 | [self commonInit]; 217 | } 218 | return self; 219 | } 220 | 221 | - (id)initWithFrame:(CGRect)frame 222 | { 223 | self = [super initWithFrame:frame]; 224 | if (self) { 225 | [self commonInit]; 226 | } 227 | return self; 228 | } 229 | 230 | - (void)commonInit 231 | { 232 | // init properties 233 | self.animation = AccordionAnimationNone; 234 | self.handleOpenSection = NO; 235 | self.headerHeight = 60; 236 | } 237 | 238 | - (void)load 239 | { 240 | // init headers 241 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionViewHeaders)]) 242 | self.headers = [self.delegate accordionViewHeaders]; 243 | 244 | // init data 245 | self.data = @[].mutableCopy; 246 | [self.headers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 247 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:dataForHeaderInSection:)]) 248 | { 249 | NSArray *content = [self.delegate accordionView:self dataForHeaderInSection:idx]; 250 | if (content == nil) 251 | NSAssert(content != nil, @"Warning: You didn't passed any array for section %lu", (unsigned long)idx); 252 | 253 | AccordionContentBean *bean = [[AccordionContentBean alloc] initWithContent:content]; 254 | [self.data addObject:bean]; 255 | } 256 | }]; 257 | 258 | [self.table reloadData]; 259 | } 260 | 261 | #pragma mark UITableViewDataSource/UITableViewDelegate Methods 262 | 263 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 264 | { 265 | return [self.headers count]; 266 | } 267 | 268 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 269 | { 270 | // the content bean of the data 271 | AccordionContentBean *bean = self.data[section]; 272 | if (bean.open) 273 | return [bean.content count]; 274 | else 275 | return 0; 276 | } 277 | 278 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 279 | { 280 | // overrided 281 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:viewForHeaderInSection:)]) 282 | return [self.delegate accordionView:self viewForHeaderInSection:section]; 283 | 284 | AccordionHeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:[AccordionHeaderView reuseIdentifier]]; 285 | headerView.section = section; 286 | id content = self.headers[section]; 287 | if ([content isKindOfClass:[NSString class]]) 288 | [headerView setContent:content block:^(NSInteger section) { 289 | [self didSelectSection:section]; 290 | }]; 291 | 292 | return headerView; 293 | } 294 | 295 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 296 | { 297 | return self.headerHeight; 298 | } 299 | 300 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 301 | { 302 | // overrided 303 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:cellForRowAtIndexPath:)]) 304 | return [self.delegate accordionView:self cellForRowAtIndexPath:indexPath]; 305 | 306 | AccordionViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[AccordionViewCell reuseIdentifier]]; 307 | AccordionContentBean *bean = self.data[indexPath.section]; 308 | if (bean.open) 309 | { 310 | id content = bean.content[indexPath.row]; 311 | cell.row = indexPath.row; 312 | if ([content isKindOfClass:[NSString class]]) 313 | [cell setContent:content block:^(NSString * actionName, NSInteger index, id object) { 314 | NSLog(@"Perform some action %@ with index %li and object %@", actionName, (long)index, object); 315 | }]; 316 | } 317 | 318 | return cell; 319 | } 320 | 321 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 322 | { 323 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:didSelectRowAtIndexPath:)]) 324 | [self.delegate accordionView:self didSelectRowAtIndexPath:indexPath]; 325 | } 326 | 327 | - (void)didSelectSection:(NSInteger)section 328 | { 329 | AccordionContentBean *beanSelected = self.data[section]; 330 | 331 | if (!beanSelected.open || self.handleOpenSection) 332 | { 333 | // change data 334 | if (self.handleOpenSection && beanSelected.open) 335 | beanSelected.open = NO; 336 | else 337 | [self.data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 338 | AccordionContentBean *bean = (AccordionContentBean*)obj; 339 | bean.open = (section == idx); 340 | }]; 341 | 342 | 343 | // perform the open/close animation 344 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self.data count])]; 345 | switch (self.animation) 346 | { 347 | case AccordionAnimationFade: 348 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationFade]; 349 | break; 350 | case AccordionAnimationRight: 351 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationRight]; 352 | break; 353 | case AccordionAnimationLeft: 354 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationLeft]; 355 | break; 356 | case AccordionAnimationTop: 357 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationTop]; 358 | break; 359 | case AccordionAnimationBottom: 360 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationBottom]; 361 | break; 362 | case AccordionAnimationMiddle: 363 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationMiddle]; 364 | break; 365 | case AccordionAnimationNone: 366 | [self.table reloadData]; 367 | break; 368 | default: 369 | break; 370 | } 371 | } 372 | } 373 | 374 | #pragma mark Public 375 | 376 | - (void)reloadData 377 | { 378 | [self.data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 379 | AccordionContentBean *bean = (AccordionContentBean*)obj; 380 | bean.open = NO; 381 | }]; 382 | 383 | [self.table reloadData]; 384 | } 385 | 386 | - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation 387 | { 388 | [self.table reloadSections:sections withRowAnimation:animation]; 389 | } 390 | 391 | - (void)selectSection:(NSInteger)section 392 | { 393 | [self didSelectSection:section]; 394 | } 395 | 396 | - (void)registerNibForHeaderView:(UINib *)nib 397 | { 398 | [self.table registerNib:nib forHeaderFooterViewReuseIdentifier:[AccordionHeaderView reuseIdentifier]]; 399 | 400 | } 401 | 402 | - (void)registerClassForHeaderView:(Class)aClass 403 | { 404 | [self.table registerClass:aClass forHeaderFooterViewReuseIdentifier:[AccordionHeaderView reuseIdentifier]]; 405 | } 406 | 407 | - (void)registerNibForCell:(UINib *)nib 408 | { 409 | [self.table registerNib:nib forCellReuseIdentifier:[AccordionViewCell reuseIdentifier]]; 410 | } 411 | 412 | - (void)registerClassForCell:(Class)aClass 413 | { 414 | [self.table registerClass:aClass forCellReuseIdentifier:[AccordionViewCell reuseIdentifier]]; 415 | } 416 | 417 | @end 418 | -------------------------------------------------------------------------------- /AccordionTableView/AccordionTableView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /AccordionTableView/AccordionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /AccordionTableView/Utility/UIView+NibLoading.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+NibLoading.h 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 04/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Category of UIView to load a view from a NIB file 12 | 13 | @interface UIView (NibLoading) 14 | 15 | - (void) loadContentsFromNibNamed:(NSString*)nibName; 16 | - (void) loadContentsFromNib; 17 | 18 | @end 19 | 20 | // Utility extension of UIView for init methods 21 | 22 | @interface NibLoadedView : UIView 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /AccordionTableView/Utility/UIView+NibLoading.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+NibLoading.m 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 04/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import "UIView+NibLoading.h" 10 | #import 11 | 12 | #pragma mark UIView(NibLoading) 13 | 14 | @implementation UIView(NibLoading) 15 | 16 | + (UINib*) _nibLoadingAssociatedNibWithName:(NSString*)nibName 17 | { 18 | // Load the NIB file at runtime 19 | static char kUIViewNibLoading_associatedNibsKey; 20 | 21 | NSDictionary * associatedNibs = objc_getAssociatedObject(self, &kUIViewNibLoading_associatedNibsKey); 22 | UINib * nib = associatedNibs[nibName]; 23 | if (nil == nib) 24 | { 25 | nib = [UINib nibWithNibName:nibName bundle:nil]; 26 | if (nib) 27 | { 28 | NSMutableDictionary * newNibs = [NSMutableDictionary dictionaryWithDictionary:associatedNibs]; 29 | newNibs[nibName] = nib; 30 | objc_setAssociatedObject(self, &kUIViewNibLoading_associatedNibsKey, [NSDictionary dictionaryWithDictionary:newNibs], OBJC_ASSOCIATION_RETAIN); 31 | } 32 | } 33 | 34 | return nib; 35 | } 36 | 37 | - (void) loadContentsFromNibNamed:(NSString*)nibName 38 | { 39 | // Load the NIB file setting self as owner 40 | UINib * nib = [[self class] _nibLoadingAssociatedNibWithName:nibName]; 41 | NSAssert(nib != nil, @"UIView+NibLoading : Can't load NIB named %@.",nibName); 42 | NSArray * views = [nib instantiateWithOwner:self options:nil]; 43 | NSAssert(views != nil, @"UIView+NibLoading : Can't instantiate NIB named %@.",nibName); 44 | 45 | // Search for the first container view 46 | UIView * containerView = nil; 47 | for (id view in views) 48 | { 49 | if ([view isKindOfClass:[UIView class]]) 50 | { 51 | containerView = view; 52 | break; 53 | } 54 | } 55 | NSAssert(containerView != nil, @"UIView+NibLoading : There is no container UIView found at the root of NIB %@.",nibName); 56 | 57 | // autolayout setting 58 | [containerView setTranslatesAutoresizingMaskIntoConstraints:NO]; 59 | 60 | if (CGRectEqualToRect(self.bounds, CGRectZero)) 61 | // this view has no size so use the container view's size 62 | self.bounds = containerView.bounds; 63 | else 64 | // this view has a specific size so resize the container view to this size 65 | containerView.bounds = self.bounds; 66 | NSArray *constraints = containerView.constraints; 67 | 68 | // re-parent the subviews from the container view 69 | for (UIView * view in containerView.subviews) 70 | { 71 | [view removeFromSuperview]; 72 | [self addSubview:view]; 73 | } 74 | 75 | // re-add constraints 76 | for (NSLayoutConstraint *constraint in constraints) 77 | { 78 | id firstItem = constraint.firstItem; 79 | id secondItem = constraint.secondItem; 80 | if (firstItem == containerView) 81 | firstItem = self; 82 | if (secondItem == containerView) 83 | secondItem = self; 84 | 85 | [self addConstraint:[NSLayoutConstraint constraintWithItem:firstItem attribute:constraint.firstAttribute relatedBy:constraint.relation toItem:secondItem attribute:constraint.secondAttribute multiplier:constraint.multiplier constant:constraint.constant]]; 86 | } 87 | } 88 | 89 | - (void) loadContentsFromNib 90 | { 91 | [self loadContentsFromNibNamed:NSStringFromClass([self class])]; 92 | } 93 | 94 | @end 95 | 96 | #pragma mark NibLoadedView 97 | 98 | @implementation NibLoadedView : UIView 99 | 100 | - (id) initWithCoder:(NSCoder *)aDecoder 101 | { 102 | self = [super initWithCoder:aDecoder]; 103 | if (self) { 104 | [self loadContentsFromNib]; 105 | } 106 | return self; 107 | } 108 | 109 | - (id) initWithFrame:(CGRect)frame 110 | { 111 | self = [super initWithFrame:frame]; 112 | if (self) { 113 | [self loadContentsFromNib]; 114 | } 115 | return self; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1761C0AD1AA776B600DF594A /* AccordionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1761C0A31AA776B600DF594A /* AccordionHeaderView.xib */; }; 11 | 1761C0B01AA776B600DF594A /* AccordionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1761C0A81AA776B600DF594A /* AccordionViewCell.xib */; }; 12 | 17DE52061AB4D7FF003A59E7 /* UIView+NibLoading.m in Sources */ = {isa = PBXBuildFile; fileRef = 17DE52051AB4D7FF003A59E7 /* UIView+NibLoading.m */; }; 13 | 17DE52091AB4E001003A59E7 /* AccordionTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 17DE52081AB4E001003A59E7 /* AccordionTableView.m */; }; 14 | 17E9D10B1AA61BB00005E0C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E9D10A1AA61BB00005E0C7 /* main.m */; }; 15 | 17E9D10E1AA61BB00005E0C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E9D10D1AA61BB00005E0C7 /* AppDelegate.m */; }; 16 | 17E9D1111AA61BB00005E0C7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E9D1101AA61BB00005E0C7 /* ViewController.m */; }; 17 | 17E9D12F1AA61C1B0005E0C7 /* ViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E9D12E1AA61C1B0005E0C7 /* ViewController_iPad.xib */; }; 18 | DF1E60591AC75A0000EBE52B /* ViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = DF1E60581AC75A0000EBE52B /* ViewController_iPhone.xib */; }; 19 | DF1E605B1AC75A8800EBE52B /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DF1E605A1AC75A8800EBE52B /* Default-568h@2x.png */; }; 20 | DF6E7FA01AC8504C0012D052 /* AccordionTableView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DF6E7F9F1AC8504C0012D052 /* AccordionTableView.xib */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 1761C0A31AA776B600DF594A /* AccordionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccordionHeaderView.xib; sourceTree = ""; }; 25 | 1761C0A81AA776B600DF594A /* AccordionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccordionViewCell.xib; sourceTree = ""; }; 26 | 17DE52041AB4D7FF003A59E7 /* UIView+NibLoading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+NibLoading.h"; sourceTree = ""; }; 27 | 17DE52051AB4D7FF003A59E7 /* UIView+NibLoading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+NibLoading.m"; sourceTree = ""; }; 28 | 17DE52071AB4E001003A59E7 /* AccordionTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccordionTableView.h; sourceTree = ""; }; 29 | 17DE52081AB4E001003A59E7 /* AccordionTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AccordionTableView.m; sourceTree = ""; }; 30 | 17E9D1051AA61BB00005E0C7 /* Accordion.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Accordion.app; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 17E9D1091AA61BB00005E0C7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | 17E9D10A1AA61BB00005E0C7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 33 | 17E9D10C1AA61BB00005E0C7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 34 | 17E9D10D1AA61BB00005E0C7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 35 | 17E9D10F1AA61BB00005E0C7 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 36 | 17E9D1101AA61BB00005E0C7 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 37 | 17E9D12E1AA61C1B0005E0C7 /* ViewController_iPad.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ViewController_iPad.xib; sourceTree = ""; }; 38 | DF1E60581AC75A0000EBE52B /* ViewController_iPhone.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ViewController_iPhone.xib; sourceTree = ""; }; 39 | DF1E605A1AC75A8800EBE52B /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 40 | DF6E7F9F1AC8504C0012D052 /* AccordionTableView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccordionTableView.xib; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 17E9D1021AA61BB00005E0C7 /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 1761C09E1AA776B600DF594A /* AccordionTableView */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 17DE52031AB4D7FF003A59E7 /* Utility */, 58 | 17DE52071AB4E001003A59E7 /* AccordionTableView.h */, 59 | 17DE52081AB4E001003A59E7 /* AccordionTableView.m */, 60 | DF6E7F9F1AC8504C0012D052 /* AccordionTableView.xib */, 61 | 1761C0A31AA776B600DF594A /* AccordionHeaderView.xib */, 62 | 1761C0A81AA776B600DF594A /* AccordionViewCell.xib */, 63 | ); 64 | path = AccordionTableView; 65 | sourceTree = ""; 66 | }; 67 | 17DE52031AB4D7FF003A59E7 /* Utility */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 17DE52041AB4D7FF003A59E7 /* UIView+NibLoading.h */, 71 | 17DE52051AB4D7FF003A59E7 /* UIView+NibLoading.m */, 72 | ); 73 | path = Utility; 74 | sourceTree = ""; 75 | }; 76 | 17E9D0FC1AA61BB00005E0C7 = { 77 | isa = PBXGroup; 78 | children = ( 79 | DF1E605A1AC75A8800EBE52B /* Default-568h@2x.png */, 80 | 17E9D1071AA61BB00005E0C7 /* Accordion */, 81 | 17E9D1061AA61BB00005E0C7 /* Products */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 17E9D1061AA61BB00005E0C7 /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 17E9D1051AA61BB00005E0C7 /* Accordion.app */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 17E9D1071AA61BB00005E0C7 /* Accordion */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 1761C09E1AA776B600DF594A /* AccordionTableView */, 97 | 17E9D10C1AA61BB00005E0C7 /* AppDelegate.h */, 98 | 17E9D10D1AA61BB00005E0C7 /* AppDelegate.m */, 99 | 17E9D10F1AA61BB00005E0C7 /* ViewController.h */, 100 | 17E9D1101AA61BB00005E0C7 /* ViewController.m */, 101 | DF1E60581AC75A0000EBE52B /* ViewController_iPhone.xib */, 102 | 17E9D12E1AA61C1B0005E0C7 /* ViewController_iPad.xib */, 103 | 17E9D1081AA61BB00005E0C7 /* Supporting Files */, 104 | ); 105 | path = Accordion; 106 | sourceTree = ""; 107 | }; 108 | 17E9D1081AA61BB00005E0C7 /* Supporting Files */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 17E9D1091AA61BB00005E0C7 /* Info.plist */, 112 | 17E9D10A1AA61BB00005E0C7 /* main.m */, 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | /* End PBXGroup section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | 17E9D1041AA61BB00005E0C7 /* Accordion */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = 17E9D1281AA61BB00005E0C7 /* Build configuration list for PBXNativeTarget "Accordion" */; 123 | buildPhases = ( 124 | 17E9D1011AA61BB00005E0C7 /* Sources */, 125 | 17E9D1021AA61BB00005E0C7 /* Frameworks */, 126 | 17E9D1031AA61BB00005E0C7 /* Resources */, 127 | ); 128 | buildRules = ( 129 | ); 130 | dependencies = ( 131 | ); 132 | name = Accordion; 133 | productName = Accordion; 134 | productReference = 17E9D1051AA61BB00005E0C7 /* Accordion.app */; 135 | productType = "com.apple.product-type.application"; 136 | }; 137 | /* End PBXNativeTarget section */ 138 | 139 | /* Begin PBXProject section */ 140 | 17E9D0FD1AA61BB00005E0C7 /* Project object */ = { 141 | isa = PBXProject; 142 | attributes = { 143 | LastUpgradeCheck = 0610; 144 | ORGANIZATIONNAME = "Francesca Corsini"; 145 | TargetAttributes = { 146 | 17E9D1041AA61BB00005E0C7 = { 147 | CreatedOnToolsVersion = 6.1.1; 148 | }; 149 | }; 150 | }; 151 | buildConfigurationList = 17E9D1001AA61BB00005E0C7 /* Build configuration list for PBXProject "Accordion" */; 152 | compatibilityVersion = "Xcode 3.2"; 153 | developmentRegion = English; 154 | hasScannedForEncodings = 0; 155 | knownRegions = ( 156 | en, 157 | Base, 158 | ); 159 | mainGroup = 17E9D0FC1AA61BB00005E0C7; 160 | productRefGroup = 17E9D1061AA61BB00005E0C7 /* Products */; 161 | projectDirPath = ""; 162 | projectRoot = ""; 163 | targets = ( 164 | 17E9D1041AA61BB00005E0C7 /* Accordion */, 165 | ); 166 | }; 167 | /* End PBXProject section */ 168 | 169 | /* Begin PBXResourcesBuildPhase section */ 170 | 17E9D1031AA61BB00005E0C7 /* Resources */ = { 171 | isa = PBXResourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | 17E9D12F1AA61C1B0005E0C7 /* ViewController_iPad.xib in Resources */, 175 | 1761C0AD1AA776B600DF594A /* AccordionHeaderView.xib in Resources */, 176 | 1761C0B01AA776B600DF594A /* AccordionViewCell.xib in Resources */, 177 | DF1E605B1AC75A8800EBE52B /* Default-568h@2x.png in Resources */, 178 | DF6E7FA01AC8504C0012D052 /* AccordionTableView.xib in Resources */, 179 | DF1E60591AC75A0000EBE52B /* ViewController_iPhone.xib in Resources */, 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXResourcesBuildPhase section */ 184 | 185 | /* Begin PBXSourcesBuildPhase section */ 186 | 17E9D1011AA61BB00005E0C7 /* Sources */ = { 187 | isa = PBXSourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 17DE52061AB4D7FF003A59E7 /* UIView+NibLoading.m in Sources */, 191 | 17E9D1111AA61BB00005E0C7 /* ViewController.m in Sources */, 192 | 17E9D10E1AA61BB00005E0C7 /* AppDelegate.m in Sources */, 193 | 17E9D10B1AA61BB00005E0C7 /* main.m in Sources */, 194 | 17DE52091AB4E001003A59E7 /* AccordionTableView.m in Sources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXSourcesBuildPhase section */ 199 | 200 | /* Begin XCBuildConfiguration section */ 201 | 17E9D1261AA61BB00005E0C7 /* Debug */ = { 202 | isa = XCBuildConfiguration; 203 | buildSettings = { 204 | ALWAYS_SEARCH_USER_PATHS = NO; 205 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 206 | CLANG_CXX_LIBRARY = "libc++"; 207 | CLANG_ENABLE_MODULES = YES; 208 | CLANG_ENABLE_OBJC_ARC = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_CONSTANT_CONVERSION = YES; 211 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 212 | CLANG_WARN_EMPTY_BODY = YES; 213 | CLANG_WARN_ENUM_CONVERSION = YES; 214 | CLANG_WARN_INT_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_UNREACHABLE_CODE = YES; 217 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 218 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 219 | COPY_PHASE_STRIP = NO; 220 | ENABLE_STRICT_OBJC_MSGSEND = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu99; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_OPTIMIZATION_LEVEL = 0; 224 | GCC_PREPROCESSOR_DEFINITIONS = ( 225 | "DEBUG=1", 226 | "$(inherited)", 227 | ); 228 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 236 | MTL_ENABLE_DEBUG_INFO = YES; 237 | ONLY_ACTIVE_ARCH = YES; 238 | SDKROOT = iphoneos; 239 | TARGETED_DEVICE_FAMILY = 2; 240 | }; 241 | name = Debug; 242 | }; 243 | 17E9D1271AA61BB00005E0C7 /* Release */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_UNREACHABLE_CODE = YES; 259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 260 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 261 | COPY_PHASE_STRIP = YES; 262 | ENABLE_NS_ASSERTIONS = NO; 263 | ENABLE_STRICT_OBJC_MSGSEND = YES; 264 | GCC_C_LANGUAGE_STANDARD = gnu99; 265 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 266 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 267 | GCC_WARN_UNDECLARED_SELECTOR = YES; 268 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 269 | GCC_WARN_UNUSED_FUNCTION = YES; 270 | GCC_WARN_UNUSED_VARIABLE = YES; 271 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 272 | MTL_ENABLE_DEBUG_INFO = NO; 273 | SDKROOT = iphoneos; 274 | TARGETED_DEVICE_FAMILY = 2; 275 | VALIDATE_PRODUCT = YES; 276 | }; 277 | name = Release; 278 | }; 279 | 17E9D1291AA61BB00005E0C7 /* Debug */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 283 | INFOPLIST_FILE = Accordion/Info.plist; 284 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 285 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 286 | PRODUCT_NAME = "$(TARGET_NAME)"; 287 | TARGETED_DEVICE_FAMILY = "1,2"; 288 | }; 289 | name = Debug; 290 | }; 291 | 17E9D12A1AA61BB00005E0C7 /* Release */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 295 | INFOPLIST_FILE = Accordion/Info.plist; 296 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 297 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | TARGETED_DEVICE_FAMILY = "1,2"; 300 | }; 301 | name = Release; 302 | }; 303 | /* End XCBuildConfiguration section */ 304 | 305 | /* Begin XCConfigurationList section */ 306 | 17E9D1001AA61BB00005E0C7 /* Build configuration list for PBXProject "Accordion" */ = { 307 | isa = XCConfigurationList; 308 | buildConfigurations = ( 309 | 17E9D1261AA61BB00005E0C7 /* Debug */, 310 | 17E9D1271AA61BB00005E0C7 /* Release */, 311 | ); 312 | defaultConfigurationIsVisible = 0; 313 | defaultConfigurationName = Release; 314 | }; 315 | 17E9D1281AA61BB00005E0C7 /* Build configuration list for PBXNativeTarget "Accordion" */ = { 316 | isa = XCConfigurationList; 317 | buildConfigurations = ( 318 | 17E9D1291AA61BB00005E0C7 /* Debug */, 319 | 17E9D12A1AA61BB00005E0C7 /* Release */, 320 | ); 321 | defaultConfigurationIsVisible = 0; 322 | defaultConfigurationName = Release; 323 | }; 324 | /* End XCConfigurationList section */ 325 | }; 326 | rootObject = 17E9D0FD1AA61BB00005E0C7 /* Project object */; 327 | } 328 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/project.xcworkspace/xcshareddata/Accordion.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 3DB54B2B-C8A4-4FF2-898D-2AD07E1854B0 9 | IDESourceControlProjectName 10 | Accordion 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | CA1DAAC6826165C5E8E6C1965EAC3AE8D83AC94E 14 | https://github.com/LigeiaRowena/AccordionTableView.git 15 | 16 | IDESourceControlProjectPath 17 | Demo Project/Accordion.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | CA1DAAC6826165C5E8E6C1965EAC3AE8D83AC94E 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/LigeiaRowena/AccordionTableView.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | CA1DAAC6826165C5E8E6C1965EAC3AE8D83AC94E 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | CA1DAAC6826165C5E8E6C1965EAC3AE8D83AC94E 36 | IDESourceControlWCCName 37 | AccordionTableView 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/project.xcworkspace/xcuserdata/francescacorsini.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LigeiaRowena/AccordionTableView/80ea30e96626c9d351b30a0d2004a0149f6c4bee/Demo Project/Accordion.xcodeproj/project.xcworkspace/xcuserdata/francescacorsini.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/project.xcworkspace/xcuserdata/ligeia9.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LigeiaRowena/AccordionTableView/80ea30e96626c9d351b30a0d2004a0149f6c4bee/Demo Project/Accordion.xcodeproj/project.xcworkspace/xcuserdata/ligeia9.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/xcuserdata/francescacorsini.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/xcuserdata/francescacorsini.xcuserdatad/xcschemes/Accordion.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/xcuserdata/francescacorsini.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Accordion.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 17E9D1041AA61BB00005E0C7 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/xcuserdata/ligeia9.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/xcuserdata/ligeia9.xcuserdatad/xcschemes/Accordion.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Demo Project/Accordion.xcodeproj/xcuserdata/ligeia9.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Accordion.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 17E9D1041AA61BB00005E0C7 16 | 17 | primary 18 | 19 | 20 | 17E9D11D1AA61BB00005E0C7 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AccordionTableView/AccordionHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AccordionTableView/AccordionTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AccordionTableView.h 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 14/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIView+NibLoading.h" 11 | 12 | @class AccordionTableView; 13 | 14 | #pragma mark - Defines 15 | 16 | // available types of animations 17 | typedef NS_ENUM(NSInteger, AccordionAnimation) { 18 | AccordionAnimationFade, 19 | AccordionAnimationRight, 20 | AccordionAnimationLeft, 21 | AccordionAnimationTop, 22 | AccordionAnimationBottom, 23 | AccordionAnimationNone, 24 | AccordionAnimationMiddle 25 | }; 26 | 27 | #pragma mark - AccordionContentBean 28 | 29 | // the content bean of the accordion tableview: AccordionContentBean 30 | @interface AccordionContentBean : NSObject 31 | 32 | @end 33 | 34 | #pragma mark - AccordionHeaderView 35 | 36 | @protocol AccordionHeaderViewDelegate; 37 | 38 | // the header of the accordion tableview: AccordionHeaderView 39 | @interface AccordionHeaderView : UITableViewHeaderFooterView 40 | 41 | @end 42 | 43 | #pragma mark - AccordionViewCell 44 | 45 | @protocol AccordionViewCellDelegate; 46 | 47 | // the cell of the accordion tableview: AccordionViewCell 48 | @interface AccordionViewCell : UITableViewCell 49 | 50 | @end 51 | 52 | #pragma mark - AccordionTableView 53 | 54 | // protocol delegate of AccordionTableView 55 | @protocol AccordionViewDelegate 56 | 57 | // returns the array of the headers 58 | // objects can be NSString or id 59 | - (NSArray *)accordionViewHeaders; 60 | 61 | // returns the array of the content for each section 62 | // objects can be NSString or id 63 | - (NSArray *)accordionView:(AccordionTableView *)accordionView dataForHeaderInSection:(NSInteger)section; 64 | 65 | @optional 66 | 67 | // returns the header being tapped for each section 68 | - (AccordionHeaderView *)accordionView:(AccordionTableView *)accordionView viewForHeaderInSection:(NSInteger)section; 69 | 70 | // returns the cell of a section 71 | // the parameter isOpenCell tells if the cell is opened or not 72 | - (AccordionViewCell *)accordionView:(AccordionTableView *)accordionView cellForRowAtIndexPath:(NSIndexPath *)indexPath isOpenCell:(BOOL)isOpenCell; 73 | 74 | // tells the delegate that the specified row is now selected 75 | - (void)accordionView:(AccordionTableView *)accordionView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; 76 | 77 | @end 78 | 79 | // the view that contains the accordion tableview: AccordionTableView 80 | @interface AccordionTableView : NibLoadedView 81 | 82 | // the tableview 83 | @property (nonatomic, weak) IBOutlet UITableView *table; 84 | 85 | // the delegate object 86 | @property (nonatomic, weak) IBOutlet id delegate; 87 | 88 | // the animation type 89 | @property (nonatomic) AccordionAnimation animation; 90 | 91 | // YES: a tap gesture on an open section performs a close animation 92 | // NO: a tap gesture on an open section doesn't perform a close animation (default value) 93 | @property (nonatomic) BOOL handleOpenSection; 94 | 95 | // height of the header being tapped 96 | @property (nonatomic) CGFloat headerHeight; 97 | 98 | // load the accordion for the first time 99 | - (void)load; 100 | 101 | // reloads the rows and sections of the table view 102 | - (void)reloadData; 103 | 104 | // reloads the specified sections using a given animation effect 105 | - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; 106 | 107 | // selects a section in the accordion 108 | - (void)selectSection:(NSInteger)section; 109 | 110 | // registers a nib object containing a header with the accordion table view 111 | - (void)registerNibForHeaderView:(UINib *)nib; 112 | 113 | // registers a class for use in creating new accordion table header 114 | - (void)registerClassForHeaderView:(Class)aClass; 115 | 116 | // registers a nib object containing a cell with the accordion table view 117 | - (void)registerNibForCell:(UINib *)nib; 118 | 119 | // registers a class for use in creating new accordion table cells 120 | - (void)registerClassForCell:(Class)aClass; 121 | 122 | @end -------------------------------------------------------------------------------- /Demo Project/Accordion/AccordionTableView/AccordionTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AccordionTableView.m 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 14/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import "AccordionTableView.h" 10 | 11 | #pragma mark - AccordionContentBean 12 | 13 | @interface AccordionContentBean() 14 | 15 | // YES: the content is open 16 | // NO: the content isn't open 17 | @property (nonatomic) BOOL open; 18 | 19 | // the data of the content 20 | @property (nonatomic, strong) id content; 21 | 22 | // init 23 | - (id)initWithContent:(id)content; 24 | 25 | @end 26 | 27 | @implementation AccordionContentBean 28 | 29 | #pragma mark Init 30 | 31 | - (id)initWithContent:(id)content 32 | { 33 | self = [super init]; 34 | if (self) { 35 | self.content = content; 36 | self.open = NO; 37 | } 38 | return self; 39 | } 40 | 41 | @end 42 | 43 | #pragma mark - AccordionHeaderView 44 | 45 | typedef void (^AccordionHeaderBlock)(NSInteger); 46 | 47 | @interface AccordionHeaderView() 48 | 49 | // utility block for notification of the section being tapped 50 | @property (copy) AccordionHeaderBlock accordionHeaderBlock; 51 | 52 | // the area tapped 53 | @property (nonatomic, weak) IBOutlet UIButton *button; 54 | 55 | // the index section of the accordion 56 | @property (nonatomic) NSInteger section; 57 | 58 | @end 59 | 60 | @implementation AccordionHeaderView 61 | 62 | #pragma mark Init 63 | 64 | 65 | - (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier 66 | { 67 | self = [super initWithReuseIdentifier:reuseIdentifier]; 68 | if (self) { 69 | } 70 | return self; 71 | } 72 | 73 | - (id)initWithCoder:(NSCoder *)aDecoder 74 | { 75 | self = [super initWithCoder:aDecoder]; 76 | if (self) { 77 | } 78 | return self; 79 | } 80 | 81 | - (id)initWithFrame:(CGRect)frame 82 | { 83 | self = [super initWithFrame:frame]; 84 | if (self) { 85 | } 86 | return self; 87 | } 88 | 89 | - (void)awakeFromNib 90 | { 91 | [super awakeFromNib]; 92 | } 93 | 94 | #pragma mark Identifier 95 | 96 | + (NSString *)reuseIdentifier 97 | { 98 | return NSStringFromClass([self class]); 99 | } 100 | 101 | #pragma mark UI 102 | 103 | - (void)setContent:(id)obj block:(AccordionHeaderBlock)block 104 | { 105 | self.accordionHeaderBlock = [block copy]; 106 | [self.button setTitle:(NSString*)obj forState:UIControlStateNormal]; 107 | } 108 | 109 | - (void)setBlock:(AccordionHeaderBlock)block 110 | { 111 | self.accordionHeaderBlock = [block copy]; 112 | } 113 | 114 | - (IBAction)selectSection:(id)sender 115 | { 116 | if (self.accordionHeaderBlock != nil) 117 | self.accordionHeaderBlock(self.section); 118 | } 119 | 120 | @end 121 | 122 | #pragma mark - AccordionViewCell 123 | 124 | typedef void (^AccordionViewCellBlock)(NSString*, NSInteger, id); 125 | 126 | @interface AccordionViewCell() 127 | 128 | // utility block for notification of some actions 129 | @property (copy) AccordionViewCellBlock accordionViewCellBlock; 130 | 131 | // the main label of the cell 132 | @property (nonatomic, weak) IBOutlet UILabel *label; 133 | 134 | // the index row of the cell 135 | @property (nonatomic) NSInteger row; 136 | 137 | @end 138 | 139 | @implementation AccordionViewCell 140 | 141 | #pragma mark Init 142 | 143 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 144 | { 145 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 146 | if (self) { 147 | } 148 | return self; 149 | } 150 | 151 | - (id)initWithCoder:(NSCoder *)aDecoder 152 | { 153 | self = [super initWithCoder:aDecoder]; 154 | if (self) { 155 | } 156 | return self; 157 | } 158 | 159 | - (id)initWithFrame:(CGRect)frame 160 | { 161 | self = [super initWithFrame:frame]; 162 | if (self) { 163 | } 164 | return self; 165 | } 166 | 167 | - (void)awakeFromNib 168 | { 169 | [super awakeFromNib]; 170 | } 171 | 172 | #pragma mark Identifier 173 | 174 | + (NSString *)reuseIdentifier 175 | { 176 | return NSStringFromClass([self class]); 177 | } 178 | 179 | #pragma mark UI 180 | 181 | - (void)setContent:(id)obj block:(AccordionViewCellBlock)block 182 | { 183 | self.accordionViewCellBlock = [block copy]; 184 | [self.label setText:(NSString*)obj]; 185 | } 186 | 187 | - (void)setBlock:(AccordionViewCellBlock)block 188 | { 189 | self.accordionViewCellBlock = [block copy]; 190 | } 191 | 192 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 193 | { 194 | [super setSelected:selected animated:animated]; 195 | } 196 | 197 | - (IBAction)someAction:(id)sender 198 | { 199 | if (self.accordionViewCellBlock != nil) 200 | self.accordionViewCellBlock(@"action_name", self.row, @[@"1"]); 201 | } 202 | 203 | @end 204 | 205 | 206 | #pragma mark - AccordionTableView 207 | 208 | @interface AccordionTableView() 209 | 210 | // the data of contents of the tableview 211 | @property (nonatomic, strong) NSMutableArray *data; 212 | 213 | // the data of headers of the tableview 214 | @property (nonatomic, strong) NSArray *headers; 215 | 216 | @end 217 | 218 | @implementation AccordionTableView 219 | 220 | #pragma mark Init 221 | 222 | - (id)initWithCoder:(NSCoder *)aDecoder 223 | { 224 | self = [super initWithCoder:aDecoder]; 225 | if (self) { 226 | [self commonInit]; 227 | } 228 | return self; 229 | } 230 | 231 | - (id)initWithFrame:(CGRect)frame 232 | { 233 | self = [super initWithFrame:frame]; 234 | if (self) { 235 | [self commonInit]; 236 | } 237 | return self; 238 | } 239 | 240 | - (void)commonInit 241 | { 242 | // init properties 243 | self.animation = AccordionAnimationNone; 244 | self.handleOpenSection = NO; 245 | self.headerHeight = 60; 246 | } 247 | 248 | - (void)load 249 | { 250 | // init headers 251 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionViewHeaders)]) 252 | self.headers = [self.delegate accordionViewHeaders]; 253 | 254 | // init data 255 | self.data = @[].mutableCopy; 256 | [self.headers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 257 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:dataForHeaderInSection:)]) 258 | { 259 | NSArray *content = [self.delegate accordionView:self dataForHeaderInSection:idx]; 260 | if (content == nil) 261 | NSAssert(content != nil, @"Warning: You didn't passed any array for section %lu", (unsigned long)idx); 262 | 263 | AccordionContentBean *bean = [[AccordionContentBean alloc] initWithContent:content]; 264 | [self.data addObject:bean]; 265 | } 266 | }]; 267 | 268 | [self.table reloadData]; 269 | } 270 | 271 | #pragma mark UITableViewDataSource/UITableViewDelegate Methods 272 | 273 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 274 | { 275 | return [self.headers count]; 276 | } 277 | 278 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 279 | { 280 | // the content bean of the data 281 | AccordionContentBean *bean = self.data[section]; 282 | if (bean.open) 283 | return [bean.content count]; 284 | else 285 | return 0; 286 | } 287 | 288 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 289 | { 290 | AccordionHeaderView *headerView = nil; 291 | 292 | // overrided in the view controller 293 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:viewForHeaderInSection:)]) 294 | { 295 | headerView = [self.delegate accordionView:self viewForHeaderInSection:section]; 296 | headerView.section = section; 297 | [headerView setBlock:^(NSInteger section) { 298 | [self didSelectSection:section]; 299 | }]; 300 | } 301 | else 302 | { 303 | headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:[AccordionHeaderView reuseIdentifier]]; 304 | headerView.section = section; 305 | id content = self.headers[section]; 306 | if ([content isKindOfClass:[NSString class]]) 307 | [headerView setContent:content block:^(NSInteger section) { 308 | [self didSelectSection:section]; 309 | }]; 310 | } 311 | 312 | return headerView; 313 | } 314 | 315 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 316 | { 317 | return self.headerHeight; 318 | } 319 | 320 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 321 | { 322 | AccordionViewCell *cell = nil; 323 | AccordionContentBean *bean = self.data[indexPath.section]; 324 | 325 | // overrided in the view controller 326 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:cellForRowAtIndexPath:isOpenCell:)]) 327 | { 328 | cell = [self.delegate accordionView:self cellForRowAtIndexPath:indexPath isOpenCell:bean.open]; 329 | cell.row = indexPath.row; 330 | [cell setBlock:^(NSString * actionName, NSInteger index, id object) { 331 | NSLog(@"Perform some action %@ with index %li and object %@", actionName, (long)index, object); 332 | }]; 333 | } 334 | else 335 | { 336 | cell = [tableView dequeueReusableCellWithIdentifier:[AccordionViewCell reuseIdentifier]]; 337 | cell.row = indexPath.row; 338 | if (bean.open) 339 | { 340 | id content = bean.content[indexPath.row]; 341 | if ([content isKindOfClass:[NSString class]]) 342 | [cell setContent:content block:^(NSString * actionName, NSInteger index, id object) { 343 | NSLog(@"Perform some action %@ with index %li and object %@", actionName, (long)index, object); 344 | }]; 345 | } 346 | } 347 | 348 | return cell; 349 | } 350 | 351 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 352 | { 353 | if (self.delegate && [self.delegate respondsToSelector:@selector(accordionView:didSelectRowAtIndexPath:)]) 354 | [self.delegate accordionView:self didSelectRowAtIndexPath:indexPath]; 355 | } 356 | 357 | - (void)didSelectSection:(NSInteger)section 358 | { 359 | AccordionContentBean *beanSelected = self.data[section]; 360 | 361 | if (!beanSelected.open || self.handleOpenSection) 362 | { 363 | // change data 364 | if (self.handleOpenSection && beanSelected.open) 365 | beanSelected.open = NO; 366 | else 367 | [self.data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 368 | AccordionContentBean *bean = (AccordionContentBean*)obj; 369 | bean.open = (section == idx); 370 | }]; 371 | 372 | 373 | // perform the open/close animation 374 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self.data count])]; 375 | switch (self.animation) 376 | { 377 | case AccordionAnimationFade: 378 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationFade]; 379 | break; 380 | case AccordionAnimationRight: 381 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationRight]; 382 | break; 383 | case AccordionAnimationLeft: 384 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationLeft]; 385 | break; 386 | case AccordionAnimationTop: 387 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationTop]; 388 | break; 389 | case AccordionAnimationBottom: 390 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationBottom]; 391 | break; 392 | case AccordionAnimationMiddle: 393 | [self.table reloadSections:indexSet withRowAnimation:UITableViewRowAnimationMiddle]; 394 | break; 395 | case AccordionAnimationNone: 396 | [self.table reloadData]; 397 | break; 398 | default: 399 | break; 400 | } 401 | } 402 | } 403 | 404 | #pragma mark Public 405 | 406 | - (void)reloadData 407 | { 408 | [self.data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 409 | AccordionContentBean *bean = (AccordionContentBean*)obj; 410 | bean.open = NO; 411 | }]; 412 | 413 | [self.table reloadData]; 414 | } 415 | 416 | - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation 417 | { 418 | [self.table reloadSections:sections withRowAnimation:animation]; 419 | } 420 | 421 | - (void)selectSection:(NSInteger)section 422 | { 423 | [self didSelectSection:section]; 424 | } 425 | 426 | - (void)registerNibForHeaderView:(UINib *)nib 427 | { 428 | [self.table registerNib:nib forHeaderFooterViewReuseIdentifier:[AccordionHeaderView reuseIdentifier]]; 429 | 430 | } 431 | 432 | - (void)registerClassForHeaderView:(Class)aClass 433 | { 434 | [self.table registerClass:aClass forHeaderFooterViewReuseIdentifier:[AccordionHeaderView reuseIdentifier]]; 435 | } 436 | 437 | - (void)registerNibForCell:(UINib *)nib 438 | { 439 | [self.table registerNib:nib forCellReuseIdentifier:[AccordionViewCell reuseIdentifier]]; 440 | } 441 | 442 | - (void)registerClassForCell:(Class)aClass 443 | { 444 | [self.table registerClass:aClass forCellReuseIdentifier:[AccordionViewCell reuseIdentifier]]; 445 | } 446 | 447 | @end 448 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AccordionTableView/AccordionTableView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AccordionTableView/AccordionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AccordionTableView/Utility/UIView+NibLoading.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+NibLoading.h 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 04/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Category of UIView to load a view from a NIB file 12 | 13 | @interface UIView (NibLoading) 14 | 15 | - (void) loadContentsFromNibNamed:(NSString*)nibName; 16 | - (void) loadContentsFromNib; 17 | 18 | @end 19 | 20 | // Utility extension of UIView for init methods 21 | 22 | @interface NibLoadedView : UIView 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AccordionTableView/Utility/UIView+NibLoading.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+NibLoading.m 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 04/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import "UIView+NibLoading.h" 10 | #import 11 | 12 | #pragma mark UIView(NibLoading) 13 | 14 | @implementation UIView(NibLoading) 15 | 16 | + (UINib*) _nibLoadingAssociatedNibWithName:(NSString*)nibName 17 | { 18 | // Load the NIB file at runtime 19 | static char kUIViewNibLoading_associatedNibsKey; 20 | 21 | NSDictionary * associatedNibs = objc_getAssociatedObject(self, &kUIViewNibLoading_associatedNibsKey); 22 | UINib * nib = associatedNibs[nibName]; 23 | if (nil == nib) 24 | { 25 | nib = [UINib nibWithNibName:nibName bundle:nil]; 26 | if (nib) 27 | { 28 | NSMutableDictionary * newNibs = [NSMutableDictionary dictionaryWithDictionary:associatedNibs]; 29 | newNibs[nibName] = nib; 30 | objc_setAssociatedObject(self, &kUIViewNibLoading_associatedNibsKey, [NSDictionary dictionaryWithDictionary:newNibs], OBJC_ASSOCIATION_RETAIN); 31 | } 32 | } 33 | 34 | return nib; 35 | } 36 | 37 | - (void) loadContentsFromNibNamed:(NSString*)nibName 38 | { 39 | // Load the NIB file setting self as owner 40 | UINib * nib = [[self class] _nibLoadingAssociatedNibWithName:nibName]; 41 | NSAssert(nib != nil, @"UIView+NibLoading : Can't load NIB named %@.",nibName); 42 | NSArray * views = [nib instantiateWithOwner:self options:nil]; 43 | NSAssert(views != nil, @"UIView+NibLoading : Can't instantiate NIB named %@.",nibName); 44 | 45 | // Search for the first container view 46 | UIView * containerView = nil; 47 | for (id view in views) 48 | { 49 | if ([view isKindOfClass:[UIView class]]) 50 | { 51 | containerView = view; 52 | break; 53 | } 54 | } 55 | NSAssert(containerView != nil, @"UIView+NibLoading : There is no container UIView found at the root of NIB %@.",nibName); 56 | 57 | // autolayout setting 58 | [containerView setTranslatesAutoresizingMaskIntoConstraints:NO]; 59 | 60 | if (CGRectEqualToRect(self.bounds, CGRectZero)) 61 | // this view has no size so use the container view's size 62 | self.bounds = containerView.bounds; 63 | else 64 | // this view has a specific size so resize the container view to this size 65 | containerView.bounds = self.bounds; 66 | NSArray *constraints = containerView.constraints; 67 | 68 | // re-parent the subviews from the container view 69 | for (UIView * view in containerView.subviews) 70 | { 71 | [view removeFromSuperview]; 72 | [self addSubview:view]; 73 | } 74 | 75 | // re-add constraints 76 | for (NSLayoutConstraint *constraint in constraints) 77 | { 78 | id firstItem = constraint.firstItem; 79 | id secondItem = constraint.secondItem; 80 | if (firstItem == containerView) 81 | firstItem = self; 82 | if (secondItem == containerView) 83 | secondItem = self; 84 | 85 | [self addConstraint:[NSLayoutConstraint constraintWithItem:firstItem attribute:constraint.firstAttribute relatedBy:constraint.relation toItem:secondItem attribute:constraint.secondAttribute multiplier:constraint.multiplier constant:constraint.constant]]; 86 | } 87 | } 88 | 89 | - (void) loadContentsFromNib 90 | { 91 | [self loadContentsFromNibNamed:NSStringFromClass([self class])]; 92 | } 93 | 94 | @end 95 | 96 | #pragma mark NibLoadedView 97 | 98 | @implementation NibLoadedView : UIView 99 | 100 | - (id) initWithCoder:(NSCoder *)aDecoder 101 | { 102 | self = [super initWithCoder:aDecoder]; 103 | if (self) { 104 | [self loadContentsFromNib]; 105 | } 106 | return self; 107 | } 108 | 109 | - (id) initWithFrame:(CGRect)frame 110 | { 111 | self = [super initWithFrame:frame]; 112 | if (self) { 113 | [self loadContentsFromNib]; 114 | } 115 | return self; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 03/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import 10 | @class ViewController; 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | @property (strong, nonatomic) ViewController *viewController; 16 | 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /Demo Project/Accordion/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 03/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | 21 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 22 | if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) 23 | { 24 | self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil]; 25 | } 26 | else if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) 27 | { 28 | self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil]; 29 | } 30 | self.window.rootViewController = self.viewController; 31 | [self.window makeKeyAndVisible]; 32 | 33 | return YES; 34 | } 35 | 36 | - (void)applicationWillResignActive:(UIApplication *)application { 37 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 38 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 39 | } 40 | 41 | - (void)applicationDidEnterBackground:(UIApplication *)application { 42 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 43 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 44 | } 45 | 46 | - (void)applicationWillEnterForeground:(UIApplication *)application { 47 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 48 | } 49 | 50 | - (void)applicationDidBecomeActive:(UIApplication *)application { 51 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 52 | } 53 | 54 | - (void)applicationWillTerminate:(UIApplication *)application { 55 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Demo Project/Accordion/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | fbi.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationLandscapeRight 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationPortraitUpsideDown 34 | UIInterfaceOrientationPortrait 35 | 36 | UISupportedInterfaceOrientations~ipad 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationPortraitUpsideDown 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Demo Project/Accordion/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 03/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AccordionTableView.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @property (nonatomic, weak) IBOutlet AccordionTableView *accordionView; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Demo Project/Accordion/ViewController.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // ViewController.m 4 | // Accordion 5 | // 6 | // Created by Francesca Corsini on 03/03/15. 7 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 8 | // 9 | 10 | #import "ViewController.h" 11 | 12 | @implementation ViewController 13 | 14 | #pragma mark - Init 15 | 16 | - (void)viewDidLoad 17 | { 18 | [super viewDidLoad]; 19 | 20 | // register header NIB for the accordion 21 | UINib *headerNib = [UINib nibWithNibName:@"AccordionHeaderView" bundle:nil]; 22 | [self.accordionView registerNibForHeaderView:headerNib]; 23 | 24 | // register cell NIB 25 | UINib *cellNib = [UINib nibWithNibName:@"AccordionViewCell" bundle:nil]; 26 | [self.accordionView registerNibForCell:cellNib]; 27 | } 28 | 29 | - (void)viewDidAppear:(BOOL)animated 30 | { 31 | [super viewDidAppear:animated]; 32 | 33 | // set the kind of animation 34 | self.accordionView.animation = AccordionAnimationFade; 35 | 36 | // a tap gesture on an open section doesn't perform a close animation 37 | self.accordionView.handleOpenSection = NO; 38 | 39 | // at first select a particular section 40 | [self.accordionView selectSection:1]; 41 | 42 | // height of the header/section 43 | self.accordionView.headerHeight = 60; 44 | 45 | // load the accordion 46 | [self.accordionView load]; 47 | } 48 | 49 | #pragma mark - AccordionViewDelegate 50 | 51 | - (NSArray *)accordionViewHeaders 52 | { 53 | return @[@"First Section", @"Second Section", @"Third Section"]; 54 | } 55 | 56 | - (NSArray *)accordionView:(AccordionTableView *)accordionView dataForHeaderInSection:(NSInteger)section 57 | { 58 | switch (section) { 59 | case 0: 60 | return @[@"first"]; 61 | break; 62 | case 1: 63 | return @[@"second"]; 64 | break; 65 | case 2: 66 | return @[@"1", @"2", @"3"]; 67 | break; 68 | default: 69 | break; 70 | } 71 | return nil; 72 | } 73 | 74 | - (void)accordionView:(AccordionTableView *)accordionView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 75 | { 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Demo Project/Accordion/ViewController_iPad.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Demo Project/Accordion/ViewController_iPhone.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Demo Project/Accordion/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Accordion 4 | // 5 | // Created by Francesca Corsini on 03/03/15. 6 | // Copyright (c) 2015 Francesca Corsini. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo Project/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LigeiaRowena/AccordionTableView/80ea30e96626c9d351b30a0d2004a0149f6c4bee/Demo Project/Default-568h@2x.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a simple tableview with accordion features. The accordion is made by simply creating a multi-sections tableview: each section has a tappable header that opens/closes the section showing/hiding the rows. 2 | Main features: 3 | 4 | - Minimum OS: iOS 6.0 5 | - Devices: iPad/iPhone 6 | - ARC 7 | - Interface orientations: landscape/portrait 8 | - AutoLayout 9 | - Possibility to use XIB for header/cell (registerNibForHeaderView: and registerNibForCell: metods) 10 | - Possibility to subclass header/cell classes 11 | - Language used: Objective-C (you have to wait for swift, I prefer swiffer :P) 12 | 13 | There is a demo projects that explains how to use AccordionTableView. 14 | 15 | #INSTALLATION 16 | 17 | First you have to import in header file of your view controller: 18 | 19 | `#import "AccordionTableView.h"` 20 | 21 | Then your view controller has to be conform to the AccordionViewDelegate protocol: 22 | 23 | `@interface ViewController : UIViewController ` 24 | 25 | #USING 26 | 27 | You can create an instance of AccordionTableView programmatically: 28 | 29 | `@property (nonatomic, strong) AccordionTableView *accordionView;` 30 | 31 | and the init by: 32 | 33 | ``` 34 | self.accordionView = [[AccordionTableView alloc] initWithFrame:myFrame]; 35 | self.accordionView.delegate = self; 36 | [self.view addSubview:self.accordionView]; 37 | ``` 38 | 39 | or you can simply create an IBOutlet of it, and of course in Interface Builder you have to set it as a subview of the main view of your view controller (see the example): 40 | 41 | `@property (nonatomic, weak) IBOutlet AccordionTableView *accordionView;` 42 | 43 | Remember also to set the delegate of the AccordionTableView object, programmatically or by using Interface Builder: 44 | 45 | `self.accordionView.delegate = self;` 46 | 47 | In the viewDidLoad method of your view controller you have to register the header NIB or the custom class of the cell/header of the AccordionTableView object: 48 | 49 | ``` 50 | // register header NIB for the accordion 51 | UINib *headerNib = [UINib nibWithNibName:@"AccordionHeaderView" bundle:nil]; 52 | [self.accordionView registerNibForHeaderView:headerNib]; 53 | 54 | // register cell NIB 55 | UINib *cellNib = [UINib nibWithNibName:@"AccordionViewCell" bundle:nil]; 56 | [self.accordionView registerNibForCell:cellNib]; 57 | ``` 58 | 59 | In the viewDidAppear: method of your view controller you can set some optional properties of the AccordionTableView object: 60 | 61 | ``` 62 | // set the kind of animation 63 | self.accordionView.animation = AccordionAnimationFade; 64 | 65 | // a tap gesture on an open section doesn't perform a close animation 66 | self.accordionView.handleOpenSection = NO; 67 | 68 | // at first select a particular section 69 | [self.accordionView selectSection:1]; 70 | 71 | // height of the header/section 72 | self.accordionView.headerHeight = 60; 73 | ``` 74 | 75 | And then you HAVE to call the load method: 76 | 77 | ``` 78 | // load the accordion 79 | [self.accordionView load]; 80 | ``` 81 | 82 | #PROTOCOL METHODS 83 | 84 | You have to implement the required methods of the protocol: 85 | 86 | 1. 87 | Returns the array of the headers: you can pass an array of titles (NSString) or an array of objects (id) 88 | 89 | `- (NSArray *)accordionViewHeaders;` 90 | 91 | 2. 92 | Returns the array of the content for each section:you can pass an array of titles (NSString) or an array of objects (id). 93 | The arrays's length has to be the same of the headers array's length, if not there will be a NSAssert. 94 | 95 | `- (NSArray *)accordionView:(AccordionTableView *)accordionView dataForHeaderInSection:(NSInteger)section;` 96 | 97 | Then you can implement the optional methods of the protocol: 98 | 99 | 1. 100 | Returns the header being tapped for each section: you implement this method if in the accordionViewHeaders method you pass an array of objects (and not an array of strings). 101 | Please remember that if you want to make a custom class for the header, you have to subclass the AccordionHeaderView class. 102 | 103 | `- (AccordionHeaderView *)accordionView:(AccordionTableView *)accordionView viewForHeaderInSection:(NSInteger)section;` 104 | 105 | 2. 106 | Returns the cell of a section: you implement this method if in the accordionView:dataForHeaderInSection method you pass an array of objects (and not an array of strings). 107 | The parameter isOpenCell tells if the cell is opened or not: of course you have to set the content cell only if isOpenCell=TRUE. 108 | Please remember that if you want to make a custom class for the cell, you have to subclass the AccordionViewCell class. 109 | 110 | `- (AccordionViewCell *)accordionView:(AccordionTableView *)accordionView cellForRowAtIndexPath:(NSIndexPath *)indexPath isOpenCell:(BOOL)isOpenCell;` 111 | 112 | 3. 113 | Tells the delegate that the specified row is now selected 114 | 115 | `- (void)accordionView:(AccordionTableView *)accordionView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;` 116 | 117 | 118 | Screens: 119 | 120 | ![alt text](https://s3.amazonaws.com/cocoacontrols_production/uploads/control_image/image/6381/iOS_Simulator_Screen_Shot_09.mag.2015_19.26.22.png "Screen") 121 | 122 | --------------------------------------------------------------------------------