Peter 9bc996374f Add 'submodules/AsyncDisplayKit/' from commit '02bedc12816e251ad71777f9d2578329b6d2bef6'
git-subtree-dir: submodules/AsyncDisplayKit
git-subtree-mainline: d06f423e0ed3df1fed9bd10d79ee312a9179b632
git-subtree-split: 02bedc12816e251ad71777f9d2578329b6d2bef6
2019-06-11 18:42:43 +01:00

426 lines
21 KiB
Objective-C

//
// PhotoTableViewCell.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "PhotoTableViewCell.h"
#import "Utilities.h"
#import "PINImageView+PINRemoteImage.h"
#import "PINButton+PINRemoteImage.h"
#define DEBUG_PHOTOCELL_LAYOUT 0
#define USE_UIKIT_AUTOLAYOUT 1
#define USE_UIKIT_MANUAL_LAYOUT !USE_UIKIT_AUTOLAYOUT
#define HEADER_HEIGHT 50
#define USER_IMAGE_HEIGHT 30
#define HORIZONTAL_BUFFER 10
#define VERTICAL_BUFFER 5
#define FONT_SIZE 14
@implementation PhotoTableViewCell
{
PhotoModel *_photoModel;
UIImageView *_userAvatarImageView;
UIImageView *_photoImageView;
UILabel *_userNameLabel;
UILabel *_photoLocationLabel;
UILabel *_photoTimeIntervalSincePostLabel;
UILabel *_photoLikesLabel;
UILabel *_photoDescriptionLabel;
NSLayoutConstraint *_userNameYPositionWithPhotoLocation;
NSLayoutConstraint *_userNameYPositionWithoutPhotoLocation;
NSLayoutConstraint *_photoLocationYPosition;
}
#pragma mark - Class Methods
+ (CGFloat)heightForPhotoModel:(PhotoModel *)photo withWidth:(CGFloat)width;
{
CGFloat photoHeight = width;
UIFont *font = [UIFont systemFontOfSize:FONT_SIZE];
CGFloat likesHeight = roundf([font lineHeight]);
NSAttributedString *descriptionAttrString = [photo descriptionAttributedStringWithFontSize:FONT_SIZE];
CGFloat availableWidth = (width - HORIZONTAL_BUFFER * 2);
CGFloat descriptionHeight = [descriptionAttrString boundingRectWithSize:CGSizeMake(availableWidth, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
context:nil].size.height;
return HEADER_HEIGHT + photoHeight + likesHeight + descriptionHeight + (4 * VERTICAL_BUFFER);
}
#pragma mark - Lifecycle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_userAvatarImageView = [[UIImageView alloc] init];
_photoImageView = [[UIImageView alloc] init];
_photoImageView.contentMode = UIViewContentModeScaleAspectFill;
_userNameLabel = [[UILabel alloc] init];
_photoLocationLabel = [[UILabel alloc] init];
_photoTimeIntervalSincePostLabel = [[UILabel alloc] init];
_photoLikesLabel = [[UILabel alloc] init];
_photoDescriptionLabel = [[UILabel alloc] init];
_photoDescriptionLabel.numberOfLines = 3;
[self addSubview:_userAvatarImageView];
[self addSubview:_photoImageView];
[self addSubview:_userNameLabel];
[self addSubview:_photoLocationLabel];
[self addSubview:_photoTimeIntervalSincePostLabel];
[self addSubview:_photoLikesLabel];
[self addSubview:_photoDescriptionLabel];
#if USE_UIKIT_AUTOLAYOUT
[_userAvatarImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_userNameLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoLocationLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoTimeIntervalSincePostLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoLikesLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoDescriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self setupConstraints];
[self updateConstraints];
#endif
#if DEBUG_PHOTOCELL_LAYOUT
_userAvatarImageView.backgroundColor = [UIColor greenColor];
_userNameLabel.backgroundColor = [UIColor greenColor];
_photoLocationLabel.backgroundColor = [UIColor greenColor];
_photoTimeIntervalSincePostLabel.backgroundColor = [UIColor greenColor];
_photoDescriptionLabel.backgroundColor = [UIColor greenColor];
_photoLikesLabel.backgroundColor = [UIColor greenColor];
#endif
}
return self;
}
-(void)setFrame:(CGRect)frame
{
[super setFrame:frame];
}
- (void)setupConstraints
{
// _userAvatarImageView
[self addConstraint:[NSLayoutConstraint constraintWithItem:_userAvatarImageView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView.superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:HORIZONTAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_userAvatarImageView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView.superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:HORIZONTAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_userAvatarImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0.0
constant:USER_IMAGE_HEIGHT]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_userAvatarImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0.0]];
// _userNameLabel
[self addConstraint:[NSLayoutConstraint constraintWithItem:_userNameLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:HORIZONTAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_userNameLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:_photoTimeIntervalSincePostLabel
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:-HORIZONTAL_BUFFER]];
_userNameYPositionWithoutPhotoLocation = [NSLayoutConstraint constraintWithItem:_userNameLabel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[self addConstraint:_userNameYPositionWithoutPhotoLocation];
_userNameYPositionWithPhotoLocation = [NSLayoutConstraint constraintWithItem:_userNameLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:-2];
_userNameYPositionWithPhotoLocation.active = NO;
[self addConstraint:_userNameYPositionWithPhotoLocation];
// _photoLocationLabel
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoLocationLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:HORIZONTAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoLocationLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:_photoTimeIntervalSincePostLabel
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:-HORIZONTAL_BUFFER]];
_photoLocationYPosition = [NSLayoutConstraint constraintWithItem:_photoLocationLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:2];
_photoLocationYPosition.active = NO;
[self addConstraint:_photoLocationYPosition];
// _photoTimeIntervalSincePostLabel
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoTimeIntervalSincePostLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:_photoTimeIntervalSincePostLabel.superview
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:-HORIZONTAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoTimeIntervalSincePostLabel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:_userAvatarImageView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
// _photoImageView
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoImageView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_photoImageView.superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:HEADER_HEIGHT]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:_photoImageView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0.0]];
// _photoLikesLabel
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoLikesLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_photoImageView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:VERTICAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoLikesLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:_photoLikesLabel.superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:HORIZONTAL_BUFFER]];
// _photoDescriptionLabel
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoDescriptionLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_photoLikesLabel
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:VERTICAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoDescriptionLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:_photoDescriptionLabel.superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:HORIZONTAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoDescriptionLabel
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_photoDescriptionLabel.superview
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:-HORIZONTAL_BUFFER]];
}
- (void)updateConstraints
{
[super updateConstraints];
if (_photoLocationLabel.attributedText) {
_userNameYPositionWithoutPhotoLocation.active = NO;
_userNameYPositionWithPhotoLocation.active = YES;
_photoLocationYPosition.active = YES;
} else {
_userNameYPositionWithoutPhotoLocation.active = YES;
_userNameYPositionWithPhotoLocation.active = NO;
_photoLocationYPosition.active = NO;
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
#if USE_UIKIT_PROGRAMMATIC_LAYOUT
CGSize boundsSize = self.bounds.size;
CGRect rect = CGRectMake(HORIZONTAL_BUFFER, (HEADER_HEIGHT - USER_IMAGE_HEIGHT) / 2.0,
USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
_userAvatarImageView.frame = rect;
rect.size = _photoTimeIntervalSincePostLabel.bounds.size;
rect.origin.x = boundsSize.width - HORIZONTAL_BUFFER - rect.size.width;
rect.origin.y = (HEADER_HEIGHT - rect.size.height) / 2.0;
_photoTimeIntervalSincePostLabel.frame = rect;
CGFloat availableWidth = CGRectGetMinX(_photoTimeIntervalSincePostLabel.frame) - HORIZONTAL_BUFFER;
rect.size = _userNameLabel.bounds.size;
rect.size.width = MIN(availableWidth, rect.size.width);
rect.origin.x = HORIZONTAL_BUFFER + USER_IMAGE_HEIGHT + HORIZONTAL_BUFFER;
if (_photoLocationLabel.attributedText) {
CGSize locationSize = _photoLocationLabel.bounds.size;
locationSize.width = MIN(availableWidth, locationSize.width);
rect.origin.y = (HEADER_HEIGHT - rect.size.height - locationSize.height) / 2.0;
_userNameLabel.frame = rect;
// FIXME: Name rects at least for this sub-condition
rect.origin.y += rect.size.height;
rect.size = locationSize;
_photoLocationLabel.frame = rect;
} else {
rect.origin.y = (HEADER_HEIGHT - rect.size.height) / 2.0;
_userNameLabel.frame = rect;
}
_photoImageView.frame = CGRectMake(0, HEADER_HEIGHT, boundsSize.width, boundsSize.width);
// FIXME: Make PhotoCellFooterView
rect.size = _photoLikesLabel.bounds.size;
rect.origin = CGPointMake(HORIZONTAL_BUFFER, CGRectGetMaxY(_photoImageView.frame) + VERTICAL_BUFFER);
_photoLikesLabel.frame = rect;
rect.size = _photoDescriptionLabel.bounds.size;
rect.size.width = MIN(boundsSize.width - HORIZONTAL_BUFFER * 2, rect.size.width);
rect.origin.y = CGRectGetMaxY(_photoLikesLabel.frame) + VERTICAL_BUFFER;
_photoDescriptionLabel.frame = rect;
#endif
}
- (void)prepareForReuse
{
[super prepareForReuse];
_userAvatarImageView.image = nil;
_photoImageView.image = nil;
_userNameLabel.attributedText = nil;
_photoLocationLabel.attributedText = nil;
_photoLocationLabel.frame = CGRectZero; // next cell might not have a _photoLocationLabel
_photoTimeIntervalSincePostLabel.attributedText = nil;
_photoLikesLabel.attributedText = nil;
_photoDescriptionLabel.attributedText = nil;
}
#pragma mark - Instance Methods
- (void)updateCellWithPhotoObject:(PhotoModel *)photo
{
_photoModel = photo;
_userNameLabel.attributedText = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE];
_photoTimeIntervalSincePostLabel.attributedText = [photo uploadDateAttributedStringWithFontSize:FONT_SIZE];
_photoLikesLabel.attributedText = [photo likesAttributedStringWithFontSize:FONT_SIZE];
_photoDescriptionLabel.attributedText = [photo descriptionAttributedStringWithFontSize:FONT_SIZE];
[_userNameLabel sizeToFit];
[_photoTimeIntervalSincePostLabel sizeToFit];
[_photoLikesLabel sizeToFit];
[_photoDescriptionLabel sizeToFit];
CGRect rect = _photoDescriptionLabel.frame;
CGFloat availableWidth = (self.bounds.size.width - HORIZONTAL_BUFFER * 2);
rect.size = [_photoDescriptionLabel sizeThatFits:CGSizeMake(availableWidth, CGFLOAT_MAX)];
_photoDescriptionLabel.frame = rect;
[UIImage downloadImageForURL:photo.URL completion:^(UIImage *image) {
_photoImageView.image = image;
}];
[self downloadAndProcessUserAvatarForPhoto:photo];
//update location
_photoLocationLabel.attributedText = [photo locationAttributedStringWithFontSize:FONT_SIZE];
[_photoLocationLabel sizeToFit];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateConstraints];
[self setNeedsLayout];
});
}
#pragma mark - Helper Methods
- (void)downloadAndProcessUserAvatarForPhoto:(PhotoModel *)photo
{
[UIImage downloadImageForURL:photo.URL completion:^(UIImage *image) {
CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
_userAvatarImageView.image = [image makeCircularImageWithSize:profileImageSize];
}];
}
@end