JSQMessagesViewController/JSQMessagesViewController/Views/JSQMessagesCollectionViewCell.m
2014-09-01 11:43:13 -07:00

338 lines
12 KiB
Objective-C

//
// Created by Jesse Squires
// http://www.hexedbits.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "JSQMessagesCollectionViewCell.h"
#import "JSQMessagesCollectionViewCellIncoming.h"
#import "JSQMessagesCollectionViewCellOutgoing.h"
#import "JSQMessagesCollectionViewLayoutAttributes.h"
#import "UIView+JSQMessages.h"
@interface JSQMessagesCollectionViewCell ()
@property (weak, nonatomic) IBOutlet JSQMessagesLabel *cellTopLabel;
@property (weak, nonatomic) IBOutlet JSQMessagesLabel *messageBubbleTopLabel;
@property (weak, nonatomic) IBOutlet JSQMessagesLabel *cellBottomLabel;
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (weak, nonatomic) IBOutlet UIView *messageBubbleContainerView;
@property (weak, nonatomic) IBOutlet UIView *avatarContainerView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewTopVerticalSpaceConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewBottomVerticalSpaceConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewAvatarHorizontalSpaceConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewMarginHorizontalSpaceConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cellTopLabelHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *messageBubbleTopLabelHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cellBottomLabelHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *avatarContainerViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *avatarContainerViewHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *messageBubbleLeftRightMarginConstraint;
@property (assign, nonatomic) UIEdgeInsets textViewFrameInsets;
@property (assign, nonatomic) CGSize avatarViewSize;
@property (weak, nonatomic, readwrite) UITapGestureRecognizer *tapGestureRecognizer;
- (void)jsq_handleTapGesture:(UITapGestureRecognizer *)tap;
- (void)jsq_updateConstraint:(NSLayoutConstraint *)constraint withConstant:(CGFloat)constant;
@end
@implementation JSQMessagesCollectionViewCell
#pragma mark - Class methods
+ (UINib *)nib
{
NSAssert(NO, @"ERROR: method must be overridden in subclasses: %s", __PRETTY_FUNCTION__);
return nil;
}
+ (NSString *)cellReuseIdentifier
{
NSAssert(NO, @"ERROR: method must be overridden in subclasses: %s", __PRETTY_FUNCTION__);
return nil;
}
#pragma mark - Initialization
- (void)awakeFromNib
{
[super awakeFromNib];
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
self.backgroundColor = [UIColor whiteColor];
self.cellTopLabelHeightConstraint.constant = 0.0f;
self.messageBubbleTopLabelHeightConstraint.constant = 0.0f;
self.cellBottomLabelHeightConstraint.constant = 0.0f;
self.avatarViewSize = CGSizeZero;
self.cellTopLabel.textAlignment = NSTextAlignmentCenter;
self.cellTopLabel.font = [UIFont boldSystemFontOfSize:12.0f];
self.cellTopLabel.textColor = [UIColor lightGrayColor];
self.messageBubbleTopLabel.font = [UIFont systemFontOfSize:12.0f];
self.messageBubbleTopLabel.textColor = [UIColor lightGrayColor];
self.cellBottomLabel.font = [UIFont systemFontOfSize:11.0f];
self.cellBottomLabel.textColor = [UIColor lightGrayColor];
self.textView.textColor = [UIColor whiteColor];
self.textView.editable = NO;
self.textView.selectable = NO;
self.textView.userInteractionEnabled = YES;
self.textView.dataDetectorTypes = UIDataDetectorTypeNone;
self.textView.showsHorizontalScrollIndicator = NO;
self.textView.showsVerticalScrollIndicator = NO;
self.textView.scrollEnabled = NO;
self.textView.backgroundColor = [UIColor clearColor];
self.textView.contentInset = UIEdgeInsetsZero;
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
self.textView.contentOffset = CGPointZero;
self.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : [UIColor whiteColor],
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) };
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(jsq_handleTapGesture:)];
[self addGestureRecognizer:tap];
self.tapGestureRecognizer = tap;
}
- (void)dealloc
{
_delegate = nil;
_cellTopLabel = nil;
_messageBubbleTopLabel = nil;
_cellBottomLabel = nil;
_textView = nil;
_messageBubbleImageView = nil;
_avatarImageView = nil;
[_tapGestureRecognizer removeTarget:nil action:NULL];
_tapGestureRecognizer = nil;
}
#pragma mark - Collection view cell
- (void)prepareForReuse
{
[super prepareForReuse];
self.cellTopLabel.text = nil;
self.messageBubbleTopLabel.text = nil;
self.cellBottomLabel.text = nil;
self.textView.dataDetectorTypes = UIDataDetectorTypeNone;
self.textView.text = nil;
self.textView.attributedText = nil;
}
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
[super applyLayoutAttributes:layoutAttributes];
JSQMessagesCollectionViewLayoutAttributes *customAttributes = (JSQMessagesCollectionViewLayoutAttributes *)layoutAttributes;
if (self.textView.font != customAttributes.messageBubbleFont) {
self.textView.font = customAttributes.messageBubbleFont;
}
if (!UIEdgeInsetsEqualToEdgeInsets(self.textView.textContainerInset, customAttributes.textViewTextContainerInsets)) {
self.textView.textContainerInset = customAttributes.textViewTextContainerInsets;
}
self.textViewFrameInsets = customAttributes.textViewFrameInsets;
[self jsq_updateConstraint:self.messageBubbleLeftRightMarginConstraint
withConstant:customAttributes.messageBubbleLeftRightMargin];
[self jsq_updateConstraint:self.cellTopLabelHeightConstraint
withConstant:customAttributes.cellTopLabelHeight];
[self jsq_updateConstraint:self.messageBubbleTopLabelHeightConstraint
withConstant:customAttributes.messageBubbleTopLabelHeight];
[self jsq_updateConstraint:self.cellBottomLabelHeightConstraint
withConstant:customAttributes.cellBottomLabelHeight];
if ([self isKindOfClass:[JSQMessagesCollectionViewCellIncoming class]]) {
self.avatarViewSize = customAttributes.incomingAvatarViewSize;
}
else if ([self isKindOfClass:[JSQMessagesCollectionViewCellOutgoing class]]) {
self.avatarViewSize = customAttributes.outgoingAvatarViewSize;
}
}
- (void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
self.messageBubbleImageView.highlighted = highlighted;
}
- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
self.messageBubbleImageView.highlighted = selected;
}
#pragma mark - Setters
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
[super setBackgroundColor:backgroundColor];
self.cellTopLabel.backgroundColor = backgroundColor;
self.messageBubbleTopLabel.backgroundColor = backgroundColor;
self.cellBottomLabel.backgroundColor = backgroundColor;
self.messageBubbleImageView.backgroundColor = backgroundColor;
self.avatarImageView.backgroundColor = backgroundColor;
self.messageBubbleContainerView.backgroundColor = backgroundColor;
self.avatarContainerView.backgroundColor = backgroundColor;
}
- (void)setMessageBubbleImageView:(UIImageView *)messageBubbleImageView
{
if (_messageBubbleImageView) {
[_messageBubbleImageView removeFromSuperview];
}
if (!messageBubbleImageView) {
_messageBubbleImageView = nil;
return;
}
messageBubbleImageView.frame = CGRectMake(0.0f,
0.0f,
CGRectGetWidth(self.messageBubbleContainerView.bounds),
CGRectGetHeight(self.messageBubbleContainerView.bounds));
[messageBubbleImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.messageBubbleContainerView insertSubview:messageBubbleImageView belowSubview:self.textView];
[self.messageBubbleContainerView jsq_pinAllEdgesOfSubview:messageBubbleImageView];
[self setNeedsUpdateConstraints];
_messageBubbleImageView = messageBubbleImageView;
}
- (void)setAvatarImageView:(UIImageView *)avatarImageView
{
if (_avatarImageView) {
[_avatarImageView removeFromSuperview];
}
if (!avatarImageView) {
self.avatarViewSize = CGSizeZero;
_avatarImageView = nil;
self.avatarContainerView.hidden = YES;
return;
}
self.avatarContainerView.hidden = NO;
self.avatarViewSize = CGSizeMake(CGRectGetWidth(avatarImageView.bounds), CGRectGetHeight(avatarImageView.bounds));
[avatarImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.avatarContainerView addSubview:avatarImageView];
[self.avatarContainerView jsq_pinAllEdgesOfSubview:avatarImageView];
[self setNeedsUpdateConstraints];
_avatarImageView = avatarImageView;
}
- (void)setAvatarViewSize:(CGSize)avatarViewSize
{
if (CGSizeEqualToSize(avatarViewSize, self.avatarViewSize)) {
return;
}
[self jsq_updateConstraint:self.avatarContainerViewWidthConstraint withConstant:avatarViewSize.width];
[self jsq_updateConstraint:self.avatarContainerViewHeightConstraint withConstant:avatarViewSize.height];
}
- (void)setTextViewFrameInsets:(UIEdgeInsets)textViewFrameInsets
{
if (UIEdgeInsetsEqualToEdgeInsets(textViewFrameInsets, self.textViewFrameInsets)) {
return;
}
[self jsq_updateConstraint:self.textViewTopVerticalSpaceConstraint withConstant:textViewFrameInsets.top];
[self jsq_updateConstraint:self.textViewBottomVerticalSpaceConstraint withConstant:textViewFrameInsets.bottom];
[self jsq_updateConstraint:self.textViewAvatarHorizontalSpaceConstraint withConstant:textViewFrameInsets.right];
[self jsq_updateConstraint:self.textViewMarginHorizontalSpaceConstraint withConstant:textViewFrameInsets.left];
}
#pragma mark - Getters
- (CGSize)avatarViewSize
{
return CGSizeMake(self.avatarContainerViewWidthConstraint.constant,
self.avatarContainerViewHeightConstraint.constant);
}
- (UIEdgeInsets)textViewFrameInsets
{
return UIEdgeInsetsMake(self.textViewTopVerticalSpaceConstraint.constant,
self.textViewMarginHorizontalSpaceConstraint.constant,
self.textViewBottomVerticalSpaceConstraint.constant,
self.textViewAvatarHorizontalSpaceConstraint.constant);
}
#pragma mark - Utilities
- (void)jsq_updateConstraint:(NSLayoutConstraint *)constraint withConstant:(CGFloat)constant
{
if (constraint.constant == constant) {
return;
}
constraint.constant = constant;
[self setNeedsUpdateConstraints];
}
#pragma mark - Gesture recognizers
- (void)jsq_handleTapGesture:(UITapGestureRecognizer *)tap
{
CGPoint touchPt = [tap locationInView:self];
if (CGRectContainsPoint(self.avatarContainerView.frame, touchPt)) {
[self.delegate messagesCollectionViewCellDidTapAvatar:self];
}
else if (CGRectContainsPoint(self.messageBubbleContainerView.frame, touchPt)) {
[self.delegate messagesCollectionViewCellDidTapMessageBubble:self];
}
else {
[self.delegate messagesCollectionViewCellDidTapCell:self atPosition:touchPt];
}
}
@end