2
2
3
3
#import " MaterialShadowElevations.h"
4
4
#import " MaterialShadowLayer.h"
5
+ #import " MaterialTypography.h"
5
6
6
7
static CGFloat kPestoSideViewAnimationDuration = 0 .2f ;
8
+ static CGFloat kPestoSideViewAvatarDim = 64 .f;
9
+ static CGFloat kPestoSideViewCollectionViewInset = 5 .f;
10
+ static CGFloat kPestoSideViewHideThreshhold = 64 .f;
11
+ static CGFloat kPestoSideViewUserItemHeight = 200 .f;
7
12
static CGFloat kPestoSideViewWidth = 240 .f;
13
+ static NSString *const kPestoSideViewWidthBaseURL =
14
+ @" https://www.gstatic.com/angular/material-adaptive/pesto/" ;
8
15
9
- @interface PestoSideContentView : UIView
16
+ @interface PestoSideViewCollectionViewCell : UICollectionViewCell
17
+
18
+ @property (nonatomic ) NSString *title;
19
+
20
+ @end
21
+
22
+ @implementation PestoSideViewCollectionViewCell
23
+
24
+ - (id )initWithFrame : (CGRect)frame {
25
+ self = [super initWithFrame: frame];
26
+ if (self) {
27
+
28
+ }
29
+ return self;
30
+ }
31
+
32
+ - (void )layoutSubviews {
33
+ [super layoutSubviews ];
34
+
35
+ self.backgroundColor = [UIColor whiteColor ];
36
+ UILabel *title = [[UILabel alloc ] initWithFrame: self .bounds];
37
+ title.text = _title;
38
+ title.font = [MDCTypography body1Font ];
39
+ title.textAlignment = NSTextAlignmentCenter;
40
+ [self addSubview: title];
41
+ }
42
+
43
+ - (void )prepareForReuse {
44
+ [super prepareForReuse ];
45
+
46
+ for (UIView *subview in [self .contentView subviews ]) {
47
+ [subview removeFromSuperview ];
48
+ }
49
+ }
50
+
51
+ @end
52
+
53
+ @interface PestoSideContentView : UIView <UICollectionViewDataSource,
54
+ UICollectionViewDelegateFlowLayout>
55
+
56
+ @property (nonatomic ) NSArray *titles;
57
+ @property (nonatomic ) NSCache *imageCache;
58
+ @property (nonatomic ) UICollectionView *collectionView;
10
59
11
60
@end
12
61
13
62
@implementation PestoSideContentView
14
63
64
+ - (void )layoutSubviews {
65
+ _titles = @[ @" Home" , @" Favorite" , @" Saved" , @" Trending" , @" Settings" ];
66
+
67
+ CGRect avatarRect = CGRectMake (0 , 0 , kPestoSideViewAvatarDim , kPestoSideViewAvatarDim );
68
+ NSString *imageURL = [kPestoSideViewWidthBaseURL stringByAppendingString: @" avatar.jpg" ];
69
+ UIImageView *avatar = [self imageViewWithURL: imageURL];
70
+ avatar.frame = avatarRect;
71
+ avatar.layer .cornerRadius = avatar.bounds .size .width / 2 .f ;
72
+ avatar.center = CGPointMake (self.bounds .size .width / 2 .f ,
73
+ kPestoSideViewUserItemHeight / 2 .f - 12 .f );
74
+ avatar.layer .masksToBounds = YES ;
75
+ [self addSubview: avatar];
76
+
77
+ CGRect cirlceRect = CGRectMake (-3 .f ,
78
+ -3 .f ,
79
+ kPestoSideViewAvatarDim + 6 .f ,
80
+ kPestoSideViewAvatarDim + 6 .f );
81
+ UIColor *teal = [UIColor colorWithRed: 0 green: 0 .67f blue: 0 .55f alpha: 1 ];
82
+ UIView *circleView = [[UIView alloc ] initWithFrame: avatarRect];
83
+ CAShapeLayer *circleLayer = [CAShapeLayer layer ];
84
+ [circleLayer setPath: [[UIBezierPath bezierPathWithOvalInRect: cirlceRect] CGPath ]];
85
+ [circleLayer setStrokeColor: teal.CGColor];
86
+ [circleLayer setFillColor: [UIColor clearColor ].CGColor];
87
+ [circleLayer setLineWidth: 2 .f];
88
+ [circleView.layer addSublayer: circleLayer];
89
+ circleView.center = avatar.center ;
90
+ [self addSubview: circleView];
91
+
92
+ CGRect nameRect = CGRectMake (0 ,
93
+ 110 .f ,
94
+ self.bounds .size .width ,
95
+ kPestoSideViewAvatarDim );
96
+ UILabel *name = [[UILabel alloc ] initWithFrame: nameRect];
97
+ name.text = @" Jonathan" ;
98
+ name.font = [MDCTypography titleFont ];
99
+ name.textAlignment = NSTextAlignmentCenter;
100
+ [self addSubview: name];
101
+
102
+ CGFloat lightHeight = 0 .5f ;
103
+ UIView *lineView =
104
+ [[UIView alloc ] initWithFrame: CGRectMake (15 .f,
105
+ 180 .f,
106
+ self .bounds.size.width - 30 .f,
107
+ lightHeight)];
108
+ [lineView.heightAnchor constraintEqualToConstant: lightHeight].active = YES ;
109
+ lineView.backgroundColor = [UIColor lightGrayColor ];
110
+ [self addSubview: lineView];
111
+
112
+ UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc ] init ];
113
+ layout.minimumInteritemSpacing = 0 ;
114
+ [layout setSectionInset: UIEdgeInsetsMake (kPestoSideViewCollectionViewInset ,
115
+ kPestoSideViewCollectionViewInset ,
116
+ kPestoSideViewCollectionViewInset ,
117
+ kPestoSideViewCollectionViewInset )];
118
+ CGRect collectionViewFrame = CGRectMake (0 ,
119
+ kPestoSideViewUserItemHeight ,
120
+ self.bounds .size .width ,
121
+ self.bounds .size .height );
122
+ _collectionView = [[UICollectionView alloc ] initWithFrame: collectionViewFrame
123
+ collectionViewLayout: layout];
124
+ _collectionView.contentSize = _collectionView.bounds .size ;
125
+ _collectionView.backgroundColor = [UIColor whiteColor ];
126
+ _collectionView.autoresizingMask =
127
+ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
128
+ [self addSubview: _collectionView];
129
+ [_collectionView registerClass: [PestoSideViewCollectionViewCell class ]
130
+ forCellWithReuseIdentifier: @" PestoSideViewCollectionViewCell" ];
131
+ [_collectionView setDataSource: self ];
132
+ [_collectionView setDelegate: self ];
133
+ _collectionView.delegate = self;
134
+ }
135
+
136
+ - (UIImageView *)imageViewWithURL : (NSString *)url {
137
+ UIImageView *imageView = [[UIImageView alloc ] initWithFrame: CGRectZero];
138
+ dispatch_async (dispatch_get_global_queue (0 , 0 ), ^{
139
+ NSData *imageData = [_imageCache objectForKey: url];
140
+ if (!imageData) {
141
+ imageData = [[NSData alloc ] initWithContentsOfURL: [NSURL URLWithString: url]];
142
+ [_imageCache setObject: imageData forKey: url];
143
+ }
144
+ if (imageData == nil ) {
145
+ return ;
146
+ }
147
+ dispatch_async (dispatch_get_main_queue (), ^{
148
+ UIImage *image = [UIImage imageWithData: imageData];
149
+ imageView.image = image;
150
+ });
151
+ });
152
+ return imageView;
153
+ }
154
+
15
155
+ (Class )layerClass {
16
156
return [MDCShadowLayer class ];
17
157
}
18
158
159
+ # pragma mark - UICollectionViewDataSource
160
+
161
+ - (NSInteger )collectionView : (UICollectionView *)collectionView
162
+ numberOfItemsInSection : (NSInteger )section {
163
+ return _titles.count ;
164
+ }
165
+
166
+ - (UICollectionViewCell *)collectionView : (UICollectionView *)collectionView
167
+ cellForItemAtIndexPath : (NSIndexPath *)indexPath {
168
+ PestoSideViewCollectionViewCell *cell =
169
+ [collectionView dequeueReusableCellWithReuseIdentifier: @" PestoSideViewCollectionViewCell"
170
+ forIndexPath: indexPath];
171
+ NSInteger itemNum = indexPath.row ;
172
+ cell.title = _titles[itemNum];
173
+ [cell setNeedsLayout ];
174
+ return cell;
175
+ }
176
+
177
+ - (void )collectionView : (UICollectionView *)collectionView
178
+ didSelectItemAtIndexPath : (NSIndexPath *)indexPath {
179
+ }
180
+
181
+ # pragma mark - UICollectionViewDelegateFlowLayout
182
+
183
+ - (CGSize)collectionView : (UICollectionView *)collectionView
184
+ layout : (UICollectionViewLayout*)collectionViewLayout
185
+ sizeForItemAtIndexPath : (NSIndexPath *)indexPath {
186
+ return CGSizeMake (collectionView.bounds .size .width , 40 .f );
187
+ }
188
+
19
189
@end
20
190
21
- @interface PestoSideView ()
191
+ @interface PestoSideView () <UIGestureRecognizerDelegate>
22
192
193
+ @property (nonatomic ) CGFloat xDelta;
194
+ @property (nonatomic ) CGFloat xStart;
23
195
@property (nonatomic ) PestoSideContentView *contentView;
24
196
@property (nonatomic ) UIButton *dismissButton;
25
197
@@ -36,10 +208,16 @@ - (id)initWithFrame:(CGRect)frame {
36
208
_contentView.backgroundColor = [UIColor whiteColor ];
37
209
[self addSubview: _contentView];
38
210
39
- UITapGestureRecognizer *tap =
211
+ UITapGestureRecognizer *tapRecognizer =
40
212
[[UITapGestureRecognizer alloc ] initWithTarget: self
41
213
action: @selector (hideSideView )];
42
- [self addGestureRecognizer: tap];
214
+ [self addGestureRecognizer: tapRecognizer];
215
+ UIPanGestureRecognizer *panRecognizer =
216
+ [[UIPanGestureRecognizer alloc ] initWithTarget: self
217
+ action: @selector (panGestureRecognized: )];
218
+ panRecognizer.minimumNumberOfTouches = 1 ;
219
+ panRecognizer.delegate = self;
220
+ [self addGestureRecognizer: panRecognizer];
43
221
}
44
222
return self;
45
223
}
@@ -72,6 +250,36 @@ - (void)hideSideView {
72
250
}];
73
251
}
74
252
253
+ - (BOOL )gestureRecognizer : (UIGestureRecognizer *)gestureRecognizer
254
+ shouldRecognizeSimultaneouslyWithGestureRecognizer : (UIGestureRecognizer *)otherGestureRecognizer {
255
+ return YES ;
256
+ }
257
+
258
+ - (void )panGestureRecognized : (UIPanGestureRecognizer *)recognizer {
259
+ CGPoint tappedPoint = [recognizer locationInView: self ];
260
+ CGFloat xCoordinate = tappedPoint.x ;
261
+ switch (recognizer.state ) {
262
+ case UIGestureRecognizerStateBegan:
263
+ _xStart = xCoordinate;
264
+ break ;
265
+ case UIGestureRecognizerStateChanged:
266
+ _xDelta = kPestoSideViewWidth - (_xStart - xCoordinate);
267
+ if (_xDelta > kPestoSideViewWidth ) {
268
+ _xDelta = kPestoSideViewWidth ;
269
+ }
270
+ _contentView.transform = CGAffineTransformMakeTranslation (_xDelta, 0 );
271
+ break ;
272
+ case UIGestureRecognizerStateEnded:
273
+ if (_xDelta > kPestoSideViewWidth - kPestoSideViewHideThreshhold ) {
274
+ [self showSideView ];
275
+ } else {
276
+ [self hideSideView ];
277
+ }
278
+ default :
279
+ break ;
280
+ }
281
+ }
282
+
75
283
+ (CGAffineTransform)showTransform {
76
284
return CGAffineTransformMakeTranslation (kPestoSideViewWidth , 0 );
77
285
}
0 commit comments