Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(231)

Side by Side Diff: ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm

Issue 2887433002: Move the ContentSuggestions items to a new target (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_ite m.h" 5 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_ite m.h"
6 6
7 #include "base/time/time.h" 7 #include "base/time/time.h"
8 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" 8 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cel l.h"
9 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion _identifier.h" 9 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion _identifier.h"
10 #import "ios/chrome/browser/ui/favicon/favicon_attributes.h" 10 #import "ios/chrome/browser/ui/favicon/favicon_attributes.h"
11 #import "ios/chrome/browser/ui/favicon/favicon_view.h" 11 #import "ios/chrome/browser/ui/favicon/favicon_view.h"
12 #import "ios/chrome/browser/ui/uikit_ui_util.h"
13 #import "ios/chrome/browser/ui/util/i18n_string.h"
14 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat erialPalettes.h"
15 #import "ios/third_party/material_components_ios/src/components/Typography/src/M aterialTypography.h"
16 #include "url/gurl.h" 12 #include "url/gurl.h"
17 13
18 #if !defined(__has_feature) || !__has_feature(objc_arc) 14 #if !defined(__has_feature) || !__has_feature(objc_arc)
19 #error "This file requires ARC support." 15 #error "This file requires ARC support."
20 #endif 16 #endif
21 17
22 namespace {
23 const CGFloat kImageSize = 72;
24 const CGFloat kStandardSpacing = 16;
25 const CGFloat kSmallSpacing = 8;
26
27 // Name of the icon displayed when a suggestion is available offline.
28 NSString* const kOfflineIconName = @"content_suggestions_offline";
29 // Size of the icon displayed when a suggestion is available offline.
30 const CGFloat kOfflineIconSize = 24;
31
32 // Size of the favicon view.
33 const CGFloat kFaviconSize = 16;
34 // Size of the icon displayed when there is not image but one should be
35 // displayed.
36 const CGFloat kIconSize = 24;
37 // Name of the icon displayed when there is not image but one should be
38 // displayed.
39 NSString* const kNoImageIconName = @"content_suggestions_no_image";
40 // No image icon percentage of white.
41 const CGFloat kNoImageIconWhite = 0.38;
42 // No image background percentage of white.
43 const CGFloat kNoImageBackgroundWhite = 0.95;
44 // Duration of the animation to display the image.
45 const CGFloat kAnimationDuration = 0.3;
46 }
47
48 @interface ContentSuggestionsItem () 18 @interface ContentSuggestionsItem ()
49 19
50 @property(nonatomic, copy) NSString* subtitle; 20 @property(nonatomic, copy) NSString* subtitle;
51 // Used to check if the image has already been fetched. There is no way to 21 // Used to check if the image has already been fetched. There is no way to
52 // discriminate between failed image download and nonexitent image. The 22 // discriminate between failed image download and nonexitent image. The
53 // suggestion tries to download the image only once. 23 // suggestion tries to download the image only once.
54 @property(nonatomic, assign) BOOL imageFetched; 24 @property(nonatomic, assign) BOOL imageFetched;
55 25
56 @end 26 @end
57 27
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 if (self.hasImage && !self.imageFetched) { 61 if (self.hasImage && !self.imageFetched) {
92 self.imageFetched = YES; 62 self.imageFetched = YES;
93 // Fetch the image. During the fetch the cell's image should still be set. 63 // Fetch the image. During the fetch the cell's image should still be set.
94 [self.delegate loadImageForSuggestedItem:self]; 64 [self.delegate loadImageForSuggestedItem:self];
95 } 65 }
96 [cell.faviconView configureWithAttributes:self.attributes]; 66 [cell.faviconView configureWithAttributes:self.attributes];
97 cell.titleLabel.text = self.title; 67 cell.titleLabel.text = self.title;
98 [cell setSubtitleText:self.subtitle]; 68 [cell setSubtitleText:self.subtitle];
99 cell.displayImage = self.hasImage; 69 cell.displayImage = self.hasImage;
100 [cell setContentImage:self.image]; 70 [cell setContentImage:self.image];
71 NSDate* date =
72 [NSDate dateWithTimeIntervalSince1970:self.publishDate.ToDoubleT()];
101 [cell setAdditionalInformationWithPublisherName:self.publisher 73 [cell setAdditionalInformationWithPublisherName:self.publisher
102 date:self.publishDate 74 date:date
103 offlineAvailability:self.availableOffline]; 75 offlineAvailability:self.availableOffline];
104 } 76 }
105 77
106 @end 78 @end
107
108 #pragma mark - ContentSuggestionsCell
109
110 @interface ContentSuggestionsCell ()
111
112 @property(nonatomic, strong) UILabel* additionalInformationLabel;
113 // Contains the no-image icon or the image.
114 @property(nonatomic, strong) UIView* imageContainer;
115 // The no-image icon displayed when there is no image.
116 @property(nonatomic, strong) UIImageView* noImageIcon;
117 // Displays the image associated with this suggestion. It is added to the
118 // imageContainer only if there is an image to display, hiding the no-image
119 // icon.
120 @property(nonatomic, strong) UIImageView* contentImageView;
121 // Constraint for the size of the image.
122 @property(nonatomic, strong) NSLayoutConstraint* imageSize;
123 // Constraint for the distance between the texts and the image.
124 @property(nonatomic, strong) NSLayoutConstraint* imageTitleSpacing;
125 // Constraint for the vertical distance between the title and the subtitle.
126 @property(nonatomic, strong) NSLayoutConstraint* titleSubtitleSpacing;
127 // Label for the subtitle.
128 @property(nonatomic, readonly, strong) UILabel* subtitleLabel;
129
130 // Applies the constraints on the elements. Called in the init.
131 - (void)applyConstraints;
132
133 @end
134
135 @implementation ContentSuggestionsCell
136
137 @synthesize titleLabel = _titleLabel;
138 @synthesize subtitleLabel = _subtitleLabel;
139 @synthesize imageContainer = _imageContainer;
140 @synthesize noImageIcon = _noImageIcon;
141 @synthesize additionalInformationLabel = _additionalInformationLabel;
142 @synthesize contentImageView = _contentImageView;
143 @synthesize faviconView = _faviconView;
144 @synthesize imageSize = _imageSize;
145 @synthesize imageTitleSpacing = _imageTitleSpacing;
146 @synthesize displayImage = _displayImage;
147 @synthesize titleSubtitleSpacing = _titleSubtitleSpacing;
148
149 - (instancetype)initWithFrame:(CGRect)frame {
150 self = [super initWithFrame:frame];
151 if (self) {
152 _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
153 _subtitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
154 _imageContainer = [[UIView alloc] initWithFrame:CGRectZero];
155 _noImageIcon = [[UIImageView alloc] initWithFrame:CGRectZero];
156 _additionalInformationLabel = [[UILabel alloc] initWithFrame:CGRectZero];
157 _contentImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
158 _faviconView = [[FaviconViewNew alloc] init];
159
160 _titleLabel.numberOfLines = 2;
161 _subtitleLabel.numberOfLines = 2;
162 [_subtitleLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh
163 forAxis:UILayoutConstraintAxisVertical];
164 [_titleLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh
165 forAxis:UILayoutConstraintAxisVertical];
166
167 _contentImageView.contentMode = UIViewContentModeScaleAspectFill;
168 _contentImageView.clipsToBounds = YES;
169 _contentImageView.hidden = YES;
170
171 _imageContainer.translatesAutoresizingMaskIntoConstraints = NO;
172 _noImageIcon.translatesAutoresizingMaskIntoConstraints = NO;
173 _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
174 _subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
175 _additionalInformationLabel.translatesAutoresizingMaskIntoConstraints = NO;
176 _contentImageView.translatesAutoresizingMaskIntoConstraints = NO;
177 _faviconView.translatesAutoresizingMaskIntoConstraints = NO;
178
179 [self.contentView addSubview:_imageContainer];
180 [self.contentView addSubview:_titleLabel];
181 [self.contentView addSubview:_subtitleLabel];
182 [self.contentView addSubview:_additionalInformationLabel];
183 [self.contentView addSubview:_faviconView];
184
185 [_imageContainer addSubview:_noImageIcon];
186 [_imageContainer addSubview:_contentImageView];
187
188 _imageContainer.backgroundColor =
189 [UIColor colorWithWhite:kNoImageBackgroundWhite alpha:1];
190 _noImageIcon.image = [[UIImage imageNamed:kNoImageIconName]
191 imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
192 [_noImageIcon
193 setTintColor:[UIColor colorWithWhite:kNoImageIconWhite alpha:1]];
194
195 _titleLabel.font = [MDCTypography subheadFont];
196 _subtitleLabel.font = [MDCTypography body1Font];
197 _additionalInformationLabel.font = [MDCTypography captionFont];
198 _faviconView.font = [[MDCTypography fontLoader] mediumFontOfSize:10];
199
200 _subtitleLabel.textColor = [[MDCPalette greyPalette] tint700];
201 _additionalInformationLabel.textColor = [[MDCPalette greyPalette] tint700];
202
203 [self applyConstraints];
204 }
205 return self;
206 }
207
208 - (void)setContentImage:(UIImage*)image {
209 if (!image) {
210 self.contentImageView.hidden = YES;
211 return;
212 }
213
214 self.contentImageView.image = image;
215
216 self.contentImageView.alpha = 0;
217 self.contentImageView.hidden = NO;
218
219 [UIView animateWithDuration:kAnimationDuration
220 animations:^{
221 self.contentImageView.alpha = 1;
222 }];
223 }
224
225 - (void)setAdditionalInformationWithPublisherName:(NSString*)publisherName
226 date:(base::Time)publishDate
227 offlineAvailability:(BOOL)availableOffline {
228 NSDate* date = [NSDate dateWithTimeIntervalSince1970:publishDate.ToDoubleT()];
229 NSString* dateString =
230 [NSDateFormatter localizedStringFromDate:date
231 dateStyle:NSDateFormatterMediumStyle
232 timeStyle:NSDateFormatterNoStyle];
233
234 NSString* publisherString = AdjustStringForLocaleDirection(
235 [NSString stringWithFormat:@"%@ - %@ ", publisherName, dateString]);
236
237 NSMutableAttributedString* additionInformation =
238 [[NSMutableAttributedString alloc] initWithString:publisherString
239 attributes:nil];
240
241 if (availableOffline) {
242 NSTextAttachment* offlineIcon = [[NSTextAttachment alloc] init];
243 offlineIcon.image = [[UIImage imageNamed:kOfflineIconName]
244 imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
245 offlineIcon.bounds = CGRectMake(
246 0, (_additionalInformationLabel.font.xHeight - kOfflineIconSize) / 2,
247 kOfflineIconSize, kOfflineIconSize);
248
249 [additionInformation
250 appendAttributedString:[NSAttributedString
251 attributedStringWithAttachment:offlineIcon]];
252 }
253
254 self.additionalInformationLabel.attributedText = additionInformation;
255 }
256
257 - (void)setSubtitleText:(NSString*)subtitle {
258 self.subtitleLabel.text = subtitle;
259 if (subtitle.length > 0) {
260 self.titleSubtitleSpacing.constant = kSmallSpacing;
261 } else {
262 self.titleSubtitleSpacing.constant = 0;
263 }
264 }
265
266 - (void)setDisplayImage:(BOOL)displayImage {
267 if (displayImage) {
268 self.imageTitleSpacing.constant = kStandardSpacing;
269 self.imageSize.constant = kImageSize;
270 self.imageContainer.hidden = NO;
271 } else {
272 self.imageTitleSpacing.constant = 0;
273 self.imageSize.constant = 0;
274 self.imageContainer.hidden = YES;
275 }
276 _displayImage = displayImage;
277 }
278
279 #pragma mark - UICollectionViewCell
280
281 - (void)prepareForReuse {
282 self.contentImageView.hidden = YES;
283 }
284
285 #pragma mark - UIView
286
287 // Implements -layoutSubviews as per instructions in documentation for
288 // +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:].
289 - (void)layoutSubviews {
290 [super layoutSubviews];
291
292 // Adjust the text label preferredMaxLayoutWidth when the parent's width
293 // changes, for instance on screen rotation.
294 CGFloat parentWidth = CGRectGetWidth(self.contentView.bounds);
295
296 CGFloat offset = 0;
297 if (self.displayImage) {
298 offset = kImageSize + kStandardSpacing;
299 }
300
301 self.titleLabel.preferredMaxLayoutWidth =
302 parentWidth - 2 * kStandardSpacing - offset;
303 self.subtitleLabel.preferredMaxLayoutWidth =
304 parentWidth - 2 * kStandardSpacing - offset;
305 self.additionalInformationLabel.preferredMaxLayoutWidth =
306 parentWidth - kFaviconSize - kSmallSpacing - 2 * kStandardSpacing;
307
308 // Re-layout with the new preferred width to allow the label to adjust its
309 // height.
310 [super layoutSubviews];
311 }
312
313 #pragma mark - Private
314
315 - (void)applyConstraints {
316 _imageSize =
317 [_imageContainer.heightAnchor constraintEqualToConstant:kImageSize];
318 _imageTitleSpacing = [_imageContainer.leadingAnchor
319 constraintEqualToAnchor:_titleLabel.trailingAnchor
320 constant:kStandardSpacing];
321 _titleSubtitleSpacing =
322 [_subtitleLabel.topAnchor constraintEqualToAnchor:_titleLabel.bottomAnchor
323 constant:kSmallSpacing];
324
325 [NSLayoutConstraint activateConstraints:@[
326 // Image.
327 _imageSize,
328 [_imageContainer.widthAnchor
329 constraintEqualToAnchor:_imageContainer.heightAnchor],
330 [_imageContainer.topAnchor constraintEqualToAnchor:_titleLabel.topAnchor],
331
332 // Text.
333 _imageTitleSpacing, _titleSubtitleSpacing,
334 [_titleLabel.trailingAnchor
335 constraintEqualToAnchor:_subtitleLabel.trailingAnchor],
336
337 // Additional Information.
338 [_additionalInformationLabel.topAnchor
339 constraintGreaterThanOrEqualToAnchor:_imageContainer.bottomAnchor
340 constant:kStandardSpacing],
341 [_additionalInformationLabel.topAnchor
342 constraintGreaterThanOrEqualToAnchor:_subtitleLabel.bottomAnchor
343 constant:kStandardSpacing],
344 [_additionalInformationLabel.bottomAnchor
345 constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
346 constant:-kStandardSpacing],
347
348 // Favicon.
349 [_faviconView.topAnchor
350 constraintGreaterThanOrEqualToAnchor:_imageContainer.bottomAnchor
351 constant:kStandardSpacing],
352 [_faviconView.topAnchor
353 constraintGreaterThanOrEqualToAnchor:_subtitleLabel.bottomAnchor
354 constant:kStandardSpacing],
355 [_faviconView.centerYAnchor
356 constraintEqualToAnchor:_additionalInformationLabel.centerYAnchor],
357 [_faviconView.bottomAnchor
358 constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
359 constant:-kStandardSpacing],
360 [_faviconView.heightAnchor constraintEqualToConstant:kFaviconSize],
361 [_faviconView.widthAnchor
362 constraintEqualToAnchor:_faviconView.heightAnchor],
363
364 // No image icon.
365 [_noImageIcon.centerXAnchor
366 constraintEqualToAnchor:_imageContainer.centerXAnchor],
367 [_noImageIcon.centerYAnchor
368 constraintEqualToAnchor:_imageContainer.centerYAnchor],
369 [_noImageIcon.widthAnchor constraintEqualToConstant:kIconSize],
370 [_noImageIcon.heightAnchor constraintEqualToAnchor:_noImageIcon.widthAnchor]
371 ]];
372
373 AddSameConstraints(_contentImageView, _imageContainer);
374
375 ApplyVisualConstraintsWithMetrics(
376 @[
377 @"H:|-(space)-[title]",
378 @"H:[image]-(space)-|",
379 @"H:|-(space)-[text]",
380 @"V:|-(space)-[title]",
381 @"H:|-(space)-[favicon]-(small)-[additional]-(space)-|",
382 ],
383 @{
384 @"image" : _imageContainer,
385 @"title" : _titleLabel,
386 @"text" : _subtitleLabel,
387 @"additional" : _additionalInformationLabel,
388 @"favicon" : _faviconView,
389 },
390 @{ @"space" : @(kStandardSpacing),
391 @"small" : @(kSmallSpacing) });
392 }
393
394 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698