Skip to content

Commit 997adf1

Browse files
committed
Pesto: Refactor SideView avatar view into its own class
Summary: This commit refactors the Avatar View that was embedded in the side view out into its own class. The refactoring is being done so that the avatar view can be used within the settings view as well. This commit also refactors the image fetching/caching logic into its own service, so it may be used across the application. Test Plan: 1. Open Pesto 2. Tap the side menu icon 3. Observe that the Avatar is exactly the same as it was before. I did an eye-ball comparison, but the reviewer may want to check the dimensions of the avatar view to make sure they're the same as they were before. Reviewers: #material_components_ios_owners, junius Reviewed By: #material_components_ios_owners, junius Projects: #material_components_ios Differential Revision: http://codereview.cc/D102
1 parent 745f9b2 commit 997adf1

File tree

6 files changed

+147
-43
lines changed

6 files changed

+147
-43
lines changed

demos/Pesto/Pesto.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
C91F6A4A1C62BC0400EB4BA8 /* PestoAvatarView.m in Sources */ = {isa = PBXBuildFile; fileRef = C91F6A491C62BC0400EB4BA8 /* PestoAvatarView.m */; };
11+
C91F6A501C63A80F00EB4BA8 /* PestoRemoteImageService.m in Sources */ = {isa = PBXBuildFile; fileRef = C91F6A4F1C63A80F00EB4BA8 /* PestoRemoteImageService.m */; };
1012
C9A8552B1C4EDFAA003CADF7 /* PestoSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C9A8552A1C4EDFAA003CADF7 /* PestoSettingsViewController.m */; };
1113
CE6A9B25D9A958C060ABB21D /* libPods-Pesto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B0D8C7972AFCDCD5C9447F8E /* libPods-Pesto.a */; };
1214
DE381E1A1C457CEE00019C1D /* PestoCardCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DE381E191C457CEE00019C1D /* PestoCardCollectionViewCell.m */; };
@@ -23,6 +25,10 @@
2325
/* Begin PBXFileReference section */
2426
B0D8C7972AFCDCD5C9447F8E /* libPods-Pesto.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Pesto.a"; sourceTree = BUILT_PRODUCTS_DIR; };
2527
BD25922DF748FCA11BCF30C4 /* Pods-Pesto.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Pesto.release.xcconfig"; path = "Pods/Target Support Files/Pods-Pesto/Pods-Pesto.release.xcconfig"; sourceTree = "<group>"; };
28+
C91F6A481C62BC0400EB4BA8 /* PestoAvatarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PestoAvatarView.h; sourceTree = "<group>"; };
29+
C91F6A491C62BC0400EB4BA8 /* PestoAvatarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PestoAvatarView.m; sourceTree = "<group>"; };
30+
C91F6A4E1C63A80F00EB4BA8 /* PestoRemoteImageService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PestoRemoteImageService.h; sourceTree = "<group>"; };
31+
C91F6A4F1C63A80F00EB4BA8 /* PestoRemoteImageService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PestoRemoteImageService.m; sourceTree = "<group>"; };
2632
C9A855291C4EDFAA003CADF7 /* PestoSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PestoSettingsViewController.h; sourceTree = "<group>"; };
2733
C9A8552A1C4EDFAA003CADF7 /* PestoSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PestoSettingsViewController.m; sourceTree = "<group>"; };
2834
D36B00D81356A416449940A7 /* Pods-Pesto.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Pesto.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Pesto/Pods-Pesto.debug.xcconfig"; sourceTree = "<group>"; };
@@ -110,6 +116,10 @@
110116
DEBEDF2E1C3EF5D5004B614B /* PestoViewController.m */,
111117
C9A855291C4EDFAA003CADF7 /* PestoSettingsViewController.h */,
112118
C9A8552A1C4EDFAA003CADF7 /* PestoSettingsViewController.m */,
119+
C91F6A481C62BC0400EB4BA8 /* PestoAvatarView.h */,
120+
C91F6A491C62BC0400EB4BA8 /* PestoAvatarView.m */,
121+
C91F6A4E1C63A80F00EB4BA8 /* PestoRemoteImageService.h */,
122+
C91F6A4F1C63A80F00EB4BA8 /* PestoRemoteImageService.m */,
113123
DEBEDF271C3EF5D5004B614B /* Supporting Files */,
114124
);
115125
path = Pesto;
@@ -244,7 +254,9 @@
244254
isa = PBXSourcesBuildPhase;
245255
buildActionMask = 2147483647;
246256
files = (
257+
C91F6A501C63A80F00EB4BA8 /* PestoRemoteImageService.m in Sources */,
247258
C9A8552B1C4EDFAA003CADF7 /* PestoSettingsViewController.m in Sources */,
259+
C91F6A4A1C62BC0400EB4BA8 /* PestoAvatarView.m in Sources */,
248260
DEBEDF2F1C3EF5D5004B614B /* PestoViewController.m in Sources */,
249261
DE381E1A1C457CEE00019C1D /* PestoCardCollectionViewCell.m in Sources */,
250262
DE5EF8671C404E0E00D2D4B9 /* PestoDetailViewController.m in Sources */,

demos/Pesto/Pesto/PestoAvatarView.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <UIKit/UIKit.h>
2+
3+
@interface PestoAvatarView : UIView
4+
5+
@property(nonatomic) NSURL *avatarImageURL;
6+
7+
@end

demos/Pesto/Pesto/PestoAvatarView.m

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#import "PestoAvatarView.h"
2+
#import "PestoRemoteImageService.h"
3+
4+
static CGFloat kPestoAvatarViewImageInset = 3.f;
5+
static CGFloat kPestoAvatarViewCircleLineWidth = 2.f;
6+
7+
@interface PestoAvatarView ()
8+
9+
@property(nonatomic) UIImageView *imageView;
10+
11+
@end
12+
13+
@implementation PestoAvatarView
14+
15+
- (instancetype)initWithFrame:(CGRect)frame {
16+
self = [super initWithFrame:frame];
17+
if (self) {
18+
CGFloat imageInset = kPestoAvatarViewImageInset + kPestoAvatarViewCircleLineWidth;
19+
20+
_imageView = [[UIImageView alloc]
21+
initWithFrame:CGRectInset(self.bounds, imageInset, imageInset)];
22+
_imageView.layer.masksToBounds = YES;
23+
24+
CAShapeLayer *circleLayer = [CAShapeLayer layer];
25+
CGRect ovalRect = CGRectInset(self.bounds,
26+
kPestoAvatarViewCircleLineWidth,
27+
kPestoAvatarViewCircleLineWidth);
28+
circleLayer.path = [UIBezierPath bezierPathWithOvalInRect:ovalRect].CGPath;
29+
UIColor *teal = [UIColor colorWithRed:0 green:0.67f blue:0.55f alpha:1.f];
30+
circleLayer.strokeColor = teal.CGColor;
31+
circleLayer.fillColor = [UIColor clearColor].CGColor;
32+
circleLayer.lineWidth = kPestoAvatarViewCircleLineWidth;
33+
34+
[self.layer addSublayer:circleLayer];
35+
[self addSubview:_imageView];
36+
self.layer.masksToBounds = YES;
37+
}
38+
return self;
39+
}
40+
41+
- (void)layoutSubviews {
42+
_imageView.layer.cornerRadius = _imageView.bounds.size.width / 2.f;
43+
}
44+
45+
- (void)setAvatarImageURL:(NSURL *)avatarImageURL {
46+
_avatarImageURL = avatarImageURL;
47+
[[PestoRemoteImageService sharedService]
48+
fetchImageDataFromURL:_avatarImageURL
49+
completion:^(NSData *imageData) {
50+
if (!imageData) {
51+
return;
52+
}
53+
dispatch_async(dispatch_get_main_queue(), ^{
54+
UIImage *image = [UIImage imageWithData:imageData];
55+
_imageView.image = image;
56+
});
57+
}];
58+
}
59+
60+
@end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#import <UIKit/UIKit.h>
2+
3+
@interface PestoRemoteImageService : NSObject
4+
5+
- (void)fetchImageDataFromURL:(NSURL *)url completion:(void (^)(NSData *))completion;
6+
7+
- (void)fetchImageDataFromURL:(NSURL *)url priority:(dispatch_queue_priority_t)priority
8+
completion:(void (^)(NSData *))completion;
9+
10+
+ (instancetype)sharedService;
11+
12+
@end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#import "PestoRemoteImageService.h"
2+
3+
@interface PestoRemoteImageService ()
4+
5+
@property(nonatomic) NSCache *cache;
6+
7+
@end
8+
9+
@implementation PestoRemoteImageService
10+
11+
- (instancetype)init {
12+
self = [super init];
13+
if (self) {
14+
_cache = [[NSCache alloc] init];
15+
}
16+
return self;
17+
}
18+
19+
- (void)fetchImageDataFromURL:(NSURL *)url completion:(void (^)(NSData *))completion {
20+
[self fetchImageDataFromURL:url priority:DISPATCH_QUEUE_PRIORITY_DEFAULT completion:completion];
21+
}
22+
23+
- (void)fetchImageDataFromURL:(NSURL *)url priority:(dispatch_queue_priority_t)priority
24+
completion:(void (^)(NSData *))completion {
25+
dispatch_async(dispatch_get_global_queue(priority, 0), ^{
26+
NSData *imageData = [_cache objectForKey:url];
27+
if (!imageData) {
28+
imageData = [[NSData alloc] initWithContentsOfURL:url];
29+
[_cache setObject:imageData forKey:url];
30+
}
31+
completion(imageData);
32+
});
33+
}
34+
35+
+ (instancetype)sharedService {
36+
static PestoRemoteImageService *instance = nil;
37+
static dispatch_once_t onceToken = 0;
38+
dispatch_once(&onceToken, ^{
39+
instance = [[PestoRemoteImageService alloc] init];
40+
});
41+
42+
return instance;
43+
}
44+
45+
@end

demos/Pesto/Pesto/PestoSideView.m

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#import "PestoSideView.h"
2+
#import "PestoAvatarView.h"
3+
#import "PestoRemoteImageService.h"
24

35
#import "MaterialShadowElevations.h"
46
#import "MaterialShadowLayer.h"
57
#import "MaterialTypography.h"
68

79
static CGFloat kPestoSideViewAnimationDuration = 0.2f;
8-
static CGFloat kPestoSideViewAvatarDim = 64.f;
10+
static CGFloat kPestoSideViewAvatarDim = 70.f;
911
static CGFloat kPestoSideViewCollectionViewInset = 5.f;
1012
static CGFloat kPestoSideViewHideThreshhold = 64.f;
1113
static CGFloat kPestoSideViewUserItemHeight = 200.f;
@@ -74,29 +76,14 @@ - (void)layoutSubviews {
7476
_titles = @[ @"Home", @"Favorite", @"Saved", @"Trending", @"Settings" ];
7577

7678
CGRect avatarRect = CGRectMake(0, 0, kPestoSideViewAvatarDim, kPestoSideViewAvatarDim);
77-
NSString *imageURL = [kPestoSideViewWidthBaseURL stringByAppendingString:@"avatar.jpg"];
78-
UIImageView *avatar = [self imageViewWithURL:imageURL];
79-
avatar.frame = avatarRect;
80-
avatar.layer.cornerRadius = avatar.bounds.size.width / 2.f;
81-
avatar.center = CGPointMake(self.bounds.size.width / 2.f,
82-
kPestoSideViewUserItemHeight / 2.f - 12.f);
83-
avatar.layer.masksToBounds = YES;
84-
[self addSubview:avatar];
85-
86-
CGRect cirlceRect = CGRectMake(-3.f,
87-
-3.f,
88-
kPestoSideViewAvatarDim + 6.f,
89-
kPestoSideViewAvatarDim + 6.f);
90-
UIColor *teal = [UIColor colorWithRed:0 green:0.67f blue:0.55f alpha:1];
91-
UIView *circleView = [[UIView alloc] initWithFrame:avatarRect];
92-
CAShapeLayer *circleLayer = [CAShapeLayer layer];
93-
[circleLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:cirlceRect] CGPath]];
94-
[circleLayer setStrokeColor:teal.CGColor];
95-
[circleLayer setFillColor:[UIColor clearColor].CGColor];
96-
[circleLayer setLineWidth:2.f];
97-
[circleView.layer addSublayer:circleLayer];
98-
circleView.center = avatar.center;
99-
[self addSubview:circleView];
79+
NSURL *avatarURL = [NSURL
80+
URLWithString:[kPestoSideViewWidthBaseURL
81+
stringByAppendingString:@"avatar.jpg"]];
82+
PestoAvatarView *avatarView = [[PestoAvatarView alloc] initWithFrame:avatarRect];
83+
avatarView.avatarImageURL = avatarURL;
84+
avatarView.center = CGPointMake(self.bounds.size.width / 2.f,
85+
kPestoSideViewUserItemHeight / 2.f - 12.f);
86+
[self addSubview:avatarView];
10087

10188
CGRect nameRect = CGRectMake(0,
10289
110.f,
@@ -142,25 +129,6 @@ - (void)layoutSubviews {
142129
_collectionView.delegate = self;
143130
}
144131

145-
- (UIImageView *)imageViewWithURL:(NSString *)url {
146-
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
147-
dispatch_async(dispatch_get_global_queue(0, 0), ^{
148-
NSData *imageData = [_imageCache objectForKey:url];
149-
if (!imageData) {
150-
imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
151-
[_imageCache setObject:imageData forKey:url];
152-
}
153-
if (imageData == nil) {
154-
return;
155-
}
156-
dispatch_async(dispatch_get_main_queue(), ^{
157-
UIImage *image = [UIImage imageWithData:imageData];
158-
imageView.image = image;
159-
});
160-
});
161-
return imageView;
162-
}
163-
164132
+ (Class)layerClass {
165133
return [MDCShadowLayer class];
166134
}

0 commit comments

Comments
 (0)