├── 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 | 
121 |
122 |
--------------------------------------------------------------------------------