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