Skip to content

Commit 1af92b1

Browse files
author
Jose Alcalá Correa
committed
Fix collection view header view issue on iOS 11, modify Airbnb example to showcase
1 parent 49c42ca commit 1af92b1

13 files changed

+192
-65
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#import "GSKExampleBaseTableViewController.h"
1+
@import UIKit;
22

3-
@interface GSKAirbnbExampleViewController : GSKExampleBaseTableViewController
3+
@interface GSKAirbnbExampleViewController : UICollectionViewController
44

55
@end

Example/GSKStretchyHeaderView/GSKAirbnbExampleViewController.m

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
#import "GSKAirbnbExampleViewController.h"
22
#import "GSKAirbnbStretchyHeaderView.h"
3+
#import "GSKExampleDataSource.h"
34

45
@interface GSKAirbnbExampleViewController () <GSKAirbnbStretchyHeaderViewDelegate>
6+
@property (nonatomic, readonly) UICollectionViewFlowLayout *collectionViewFlowLayout;
7+
@property (nonatomic) GSKStretchyHeaderView *stretchyHeaderView;
8+
@property (nonatomic) GSKExampleDataSource *dataSource;
59
@end
610

711
@interface GSKPresentableViewController : UIViewController
@@ -10,18 +14,44 @@ @interface GSKPresentableViewController : UIViewController
1014

1115
@implementation GSKAirbnbExampleViewController
1216

17+
- (instancetype)init {
18+
UICollectionViewFlowLayout *collectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
19+
collectionViewFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
20+
collectionViewFlowLayout.minimumLineSpacing = 0;
21+
collectionViewFlowLayout.minimumInteritemSpacing = 0;
22+
23+
return [super initWithCollectionViewLayout:collectionViewFlowLayout];
24+
}
25+
26+
- (UICollectionViewFlowLayout *)collectionViewFlowLayout {
27+
return (UICollectionViewFlowLayout *)self.collectionViewLayout;
28+
}
29+
1330
- (void)viewDidLoad {
1431
[super viewDidLoad];
32+
33+
self.collectionView.backgroundColor = [UIColor whiteColor];
34+
if (@available(iOS 11.0, *)) {
35+
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
36+
}
1537

16-
self.refreshControl = [[UIRefreshControl alloc] init];
17-
[self.refreshControl addTarget:self action:@selector(beginRefreshing:) forControlEvents:UIControlEventValueChanged];
18-
19-
[self.tableView addSubview:self.refreshControl];
38+
self.stretchyHeaderView = [self loadStretchyHeaderView];
39+
[self.collectionView addSubview:self.stretchyHeaderView];
40+
41+
self.dataSource = [[GSKExampleDataSource alloc] init];
42+
[self.dataSource registerForCollectionView:self.collectionView];
43+
44+
self.dataSource.numberOfSections = 10;
45+
self.dataSource.numberOfRowsInEverySection = 6;
46+
self.dataSource.displaysSectionHeaders = YES;
2047
self.dataSource.cellColors = @[[UIColor whiteColor], [UIColor lightGrayColor]];
48+
49+
self.collectionView.refreshControl = [[UIRefreshControl alloc] init];
50+
[self.collectionView.refreshControl addTarget:self action:@selector(beginRefreshing:) forControlEvents:UIControlEventValueChanged];
2151
}
2252

2353
- (GSKStretchyHeaderView *)loadStretchyHeaderView {
24-
CGRect frame = CGRectMake(0, 0, self.tableView.frame.size.width, 250);
54+
CGRect frame = CGRectMake(0, 0, self.collectionView.frame.size.width, 250);
2555
GSKAirbnbStretchyHeaderView *headerView = [[GSKAirbnbStretchyHeaderView alloc] initWithFrame:frame];
2656
headerView.maximumContentHeight = 300;
2757
headerView.minimumContentHeight = 84;
@@ -42,10 +72,10 @@ - (void)airbnbStretchyHeaderView:(GSKAirbnbStretchyHeaderView *)headerView
4272

4373
- (void)airbnbStretchyHeaderView:(GSKAirbnbStretchyHeaderView *)headerView
4474
didTapSearchButton:(id)sender {
45-
CGPoint contentOffset = self.tableView.contentOffset;
75+
CGPoint contentOffset = self.collectionView.contentOffset;
4676
if (contentOffset.y < -self.stretchyHeaderView.minimumContentHeight) {
4777
contentOffset.y = -self.stretchyHeaderView.minimumContentHeight;
48-
[self.tableView setContentOffset:contentOffset animated:YES];
78+
[self.collectionView setContentOffset:contentOffset animated:YES];
4979
}
5080
}
5181

@@ -59,13 +89,22 @@ - (void)airbnbStretchyHeaderView:(GSKAirbnbStretchyHeaderView *)headerView didTa
5989

6090
- (void)beginRefreshing:(id)sender {
6191
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
62-
[self.refreshControl endRefreshing];
92+
[self.collectionView.refreshControl endRefreshing];
6393
});
6494
}
6595

66-
@end
96+
- (CGSize)collectionView:(UICollectionView *)collectionView
97+
layout:(UICollectionViewLayout *)collectionViewLayout
98+
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
99+
return CGSizeMake(collectionView.frame.size.width,
100+
[self.dataSource heightForItemAtIndexPath:indexPath]);
101+
}
67102

103+
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
104+
return CGSizeMake(collectionView.frame.size.width, 20);
105+
}
68106

107+
@end
69108

70109
@implementation GSKPresentableViewController
71110

Example/GSKStretchyHeaderView/GSKExampleBaseTableViewController.m

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#import "GSKExampleBaseTableViewController.h"
22
#import "UINavigationController+Transparency.h"
33

4-
static const NSUInteger kNumberOfRows = 100;
5-
64
@interface GSKExampleBaseTableViewController ()
75

86
@end
@@ -55,7 +53,7 @@ - (GSKStretchyHeaderView *)loadStretchyHeaderView {
5553
}
5654

5755
- (GSKExampleDataSource *)loadDataSource {
58-
return [[GSKExampleDataSource alloc] initWithNumberOfRows:kNumberOfRows];
56+
return [[GSKExampleDataSource alloc] init];
5957
}
6058

6159
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

Example/GSKStretchyHeaderView/GSKExampleCollectionViewController.m

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
NS_ASSUME_NONNULL_BEGIN
77

8-
static const NSUInteger kNumberOfRows = 100;
9-
108
@interface GSKExampleCollectionViewController () <UICollectionViewDelegateFlowLayout>
119
@property (nonatomic) GSKExampleData *data;
1210
@property (nonatomic) GSKStretchyHeaderView *stretchyHeaderView;
@@ -47,7 +45,7 @@ - (void)viewDidLoad {
4745
}
4846
[self.collectionView addSubview:self.stretchyHeaderView];
4947

50-
self.dataSource = [[GSKExampleDataSource alloc] initWithNumberOfRows:kNumberOfRows];
48+
self.dataSource = [[GSKExampleDataSource alloc] init];
5149
[self.dataSource registerForCollectionView:self.collectionView];
5250
}
5351

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
#import <Foundation/Foundation.h>
22

3+
static const NSUInteger kDefaultNumberOfRows = 100;
4+
35
@interface GSKExampleDataSource : NSObject<UITableViewDataSource,
46
UICollectionViewDataSource>
57

6-
@property (nonatomic, readonly) NSUInteger numberOfRows;
8+
// set to true to display basic titles: "Section #X"
9+
@property (nonatomic) BOOL displaysSectionHeaders; // default value: false
10+
@property (nonatomic) NSUInteger numberOfSections; // default value: 1
11+
@property (nonatomic) NSUInteger numberOfRowsInEverySection;
712
@property (nonatomic) NSArray<UIColor *> *cellColors;
813

9-
- (instancetype)initWithNumberOfRows:(NSUInteger)numberOfRows;
14+
- (instancetype)init;
1015
- (void)registerForTableView:(UITableView *)tableView;
1116
- (void)registerForCollectionView:(UICollectionView *)collectionView;
1217
- (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath;
18+
- (NSString *)titleForSection:(NSInteger)section;
1319

1420
@end

Example/GSKStretchyHeaderView/GSKExampleDataSource.m

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,29 @@ @interface GSKExampleDataSource ()
77
@property (nonatomic) NSMutableArray<NSNumber *> *rowHeights;
88
@end
99

10+
@interface GSKAirbnbSectionTitleView: UICollectionReusableView
11+
@property (nonatomic) UILabel *label;
12+
@end
13+
1014
@implementation GSKExampleDataSource
1115

12-
- (instancetype)initWithNumberOfRows:(NSUInteger)numberOfRows {
16+
- (instancetype)init {
1317
self = [super init];
1418
if (self) {
15-
_numberOfRows = numberOfRows;
16-
self.rowHeights = [NSMutableArray arrayWithCapacity:numberOfRows];
17-
for (NSUInteger i = 0; i < numberOfRows; ++i) {
18-
CGFloat height = 40 + arc4random_uniform(160);
19-
[self.rowHeights addObject:@(height)];
20-
}
19+
_displaysSectionHeaders = false;
20+
_numberOfSections = 1;
21+
_numberOfRowsInEverySection = kDefaultNumberOfRows;
22+
[self updateRowHeights];
2123
self.cellColors = @[[UIColor grayColor], [UIColor lightGrayColor]];
2224
}
2325
return self;
2426
}
2527

28+
- (void)setNumberOfRowsInEverySection:(NSUInteger)numberOfRowsInEverySection {
29+
_numberOfRowsInEverySection = numberOfRowsInEverySection;
30+
[self updateRowHeights];
31+
}
32+
2633
- (void)registerForTableView:(UITableView *)tableView {
2734
_scrollView = tableView;
2835

@@ -33,14 +40,32 @@ - (void)registerForTableView:(UITableView *)tableView {
3340
- (void)registerForCollectionView:(UICollectionView *)collectionView {
3441
collectionView.dataSource = self;
3542
[GSKCollectionViewCell registerIn:collectionView];
43+
[collectionView registerClass:[GSKAirbnbSectionTitleView class]
44+
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
45+
withReuseIdentifier:NSStringFromClass([GSKAirbnbSectionTitleView class])];
46+
}
47+
48+
- (void)updateRowHeights {
49+
self.rowHeights = [NSMutableArray arrayWithCapacity:self.numberOfRowsInEverySection];
50+
for (NSUInteger i = 0; i < self.numberOfRowsInEverySection; ++i) {
51+
CGFloat height = 40 + arc4random_uniform(160);
52+
[self.rowHeights addObject:@(height)];
53+
}
3654
}
3755

3856
#pragma mark - table view
3957

40-
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
41-
return self.numberOfRows;
58+
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
59+
return [self titleForSection:section];
4260
}
4361

62+
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
63+
return self.numberOfSections;
64+
}
65+
66+
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
67+
return self.numberOfRowsInEverySection;
68+
}
4469

4570
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
4671
GSKTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[GSKTableViewCell reuseIdentifier]];
@@ -50,6 +75,10 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
5075

5176
#pragma mark - collectionView
5277

78+
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
79+
return self.numberOfSections;
80+
}
81+
5382
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
5483
return self.rowHeights.count;
5584
}
@@ -61,8 +90,43 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
6190
return cell;
6291
}
6392

93+
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
94+
if (!self.displaysSectionHeaders) {
95+
return nil;
96+
}
97+
98+
GSKAirbnbSectionTitleView *titleView = [collectionView dequeueReusableSupplementaryViewOfKind:kind
99+
withReuseIdentifier:NSStringFromClass([GSKAirbnbSectionTitleView class])
100+
forIndexPath:indexPath];
101+
titleView.label.text = [self titleForSection:indexPath.section];
102+
return titleView;
103+
}
104+
105+
#pragma mark - generic
106+
64107
- (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath {
65108
return [self.rowHeights[indexPath.item] floatValue];
66109
}
67110

111+
- (NSString *)titleForSection:(NSInteger)section {
112+
return self.displaysSectionHeaders ? [NSString stringWithFormat:@"Section #%@", @(section)] : nil;
113+
}
114+
115+
@end
116+
117+
118+
@implementation GSKAirbnbSectionTitleView
119+
120+
- (instancetype)initWithFrame:(CGRect)frame {
121+
self = [super initWithFrame:frame];
122+
if (self) {
123+
self.label = [[UILabel alloc] initWithFrame:self.bounds];
124+
self.label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
125+
[self addSubview:self.label];
126+
127+
self.backgroundColor = [UIColor grayColor];
128+
}
129+
return self;
130+
}
131+
68132
@end

Example/GSKStretchyHeaderView/GSKExampleListViewController.m

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ - (void)viewDidLoad {
2626
[super viewDidLoad];
2727

2828
self.title = @"GSKStretchyHeaderView";
29+
self.tableView.rowHeight = [GSKExampleDataCell height];
30+
2931
[GSKExampleDataCell registerIn:self.tableView];
3032

3133
GSKExampleData *airbnb = [GSKExampleData dataWithTitle:@"airbnb-like header view"
@@ -73,10 +75,6 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger
7375
return self.exampleDatas.count;
7476
}
7577

76-
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
77-
return [GSKExampleDataCell height];
78-
}
79-
8078
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
8179
GSKExampleDataCell *cell = [tableView dequeueReusableCellWithIdentifier:[GSKExampleDataCell reuseIdentifier]];
8280
cell.data = self.exampleDatas[indexPath.row];

Example/GSKStretchyHeaderView/GSKExampleTabsViewController.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ - (void)viewDidLoad {
3535

3636
NSMutableArray *dataSources = [NSMutableArray array];
3737
for (NSUInteger i = 0; i < self.stretchyHeaderView.tabsCount; ++i) {
38-
GSKExampleDataSource *dataSource = [[GSKExampleDataSource alloc] initWithNumberOfRows:100];
38+
GSKExampleDataSource *dataSource = [[GSKExampleDataSource alloc] init];
3939
[dataSources addObject:dataSource];
4040
}
4141
self.dataSources = [dataSources copy];

Example/GSKStretchyHeaderView/GSKVisibleSectionHeadersViewController.m

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
#import "UINavigationController+Transparency.h"
44
#import "UIView+GSKLayoutHelper.h"
55

6-
@interface GSKVisibleSectionHeadersDataSource : GSKExampleDataSource
7-
8-
@end
9-
106
@implementation GSKVisibleSectionHeadersViewController
117

128
- (void)viewDidLoad {
139
[super viewDidLoad];
1410

11+
self.dataSource.numberOfSections = 10;
12+
self.dataSource.numberOfRowsInEverySection = 7;
13+
self.dataSource.displaysSectionHeaders = YES;
14+
1515
// by setting contentInset.top, we set where the section headers will be fixed
1616
UIEdgeInsets contentInset = self.tableView.contentInset;
1717
contentInset.top = self.stretchyHeaderView.minimumContentHeight;
@@ -41,24 +41,4 @@ - (GSKStretchyHeaderView *)loadStretchyHeaderView {
4141
return headerView;
4242
}
4343

44-
- (GSKExampleDataSource *)loadDataSource {
45-
return [[GSKVisibleSectionHeadersDataSource alloc] init];
46-
}
47-
48-
@end
49-
50-
@implementation GSKVisibleSectionHeadersDataSource
51-
52-
- (instancetype)init {
53-
return [self initWithNumberOfRows:7];
54-
}
55-
56-
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
57-
return 10;
58-
}
59-
60-
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
61-
return [NSString stringWithFormat:@"Section #%@", @(section)];
62-
}
63-
6444
@end

GSKStretchyHeaderView/Classes/GSKStretchyHeaderView.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,19 @@ typedef NS_ENUM(NSUInteger, GSKStretchyHeaderViewExpansionMode) {
105105
*/
106106
@property(nonatomic) BOOL manageScrollViewInsets;
107107

108+
/**
109+
* Indicates if the view hierarchy of the containing scroll view should be manipulated to fix some artifacts,
110+
* such as section header and supplementary views appearing on top of this header view.
111+
* This may involve moving views behind the header view, like UICollectionReusableView and UITableViewHeaderFooterView,
112+
* and adjusting `zPosition` for some views on iOS 11.
113+
*
114+
* - Please see UIScrollView+GSKStretchyHeaderView.m for more information.
115+
* - Have a look at this issue for more information: https://github.com/gskbyte/GSKStretchyHeaderView/issues/63
116+
*
117+
* Default value is YES
118+
*/
119+
@property(nonatomic) BOOL managesScrollViewSubviewHierarchy;
120+
108121
/**
109122
* Specifies wether the contentView height shrinks when scrolling up. Default is YES.
110123
*/

0 commit comments

Comments
 (0)