diff --git a/.gitignore b/.gitignore index 8e30d61..b10b263 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ *.xcuserstate *.xcsettings + +*.xcuserdatad \ No newline at end of file diff --git a/DemoProject/SOSimpleChatDemo.xcodeproj/project.pbxproj b/DemoProject/SOSimpleChatDemo.xcodeproj/project.pbxproj index 96303ac..1ba8b51 100644 --- a/DemoProject/SOSimpleChatDemo.xcodeproj/project.pbxproj +++ b/DemoProject/SOSimpleChatDemo.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 277BCE8F1950D7FE00413CF5 /* SOPhotoMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 277BCE8A1950D7FE00413CF5 /* SOPhotoMessageCell.m */; }; + 277BCE901950D7FE00413CF5 /* SOTextMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 277BCE8C1950D7FE00413CF5 /* SOTextMessageCell.m */; }; + 277BCE911950D7FE00413CF5 /* SOVideoMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 277BCE8E1950D7FE00413CF5 /* SOVideoMessageCell.m */; }; 3E207894193DFC6300EC9FA4 /* Message.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E207893193DFC6300EC9FA4 /* Message.m */; }; 3E3FB9A81920C3DC00986D8E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E3FB9A71920C3DC00986D8E /* AppDelegate.m */; }; 3E70D69A1918D10D00D77D8B /* arturdev.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3E70D6991918D10D00D77D8B /* arturdev.jpg */; }; @@ -37,7 +40,6 @@ 75493C3D19070071006C6BCD /* SOSimpleChatDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 75493C3C19070071006C6BCD /* SOSimpleChatDemoTests.m */; }; 75493C52190703C7006C6BCD /* Type1VC.m in Sources */ = {isa = PBXBuildFile; fileRef = 75493C51190703C7006C6BCD /* Type1VC.m */; }; 75586C551908FFA1003BDEB1 /* doggy.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 75586C541908FFA1003BDEB1 /* doggy.jpg */; }; - 75B35A2D1922B80800513423 /* NSString+Calculation.m in Sources */ = {isa = PBXBuildFile; fileRef = 75B35A101922B80800513423 /* NSString+Calculation.m */; }; 75B35A2E1922B80800513423 /* attachment.png in Resources */ = {isa = PBXBuildFile; fileRef = 75B35A121922B80800513423 /* attachment.png */; }; 75B35A2F1922B80800513423 /* attachment@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 75B35A131922B80800513423 /* attachment@2x.png */; }; 75B35A301922B80800513423 /* bubble.png in Resources */ = {isa = PBXBuildFile; fileRef = 75B35A141922B80800513423 /* bubble.png */; }; @@ -67,6 +69,12 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 277BCE891950D7FE00413CF5 /* SOPhotoMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOPhotoMessageCell.h; sourceTree = ""; }; + 277BCE8A1950D7FE00413CF5 /* SOPhotoMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOPhotoMessageCell.m; sourceTree = ""; }; + 277BCE8B1950D7FE00413CF5 /* SOTextMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOTextMessageCell.h; sourceTree = ""; }; + 277BCE8C1950D7FE00413CF5 /* SOTextMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOTextMessageCell.m; sourceTree = ""; }; + 277BCE8D1950D7FE00413CF5 /* SOVideoMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOVideoMessageCell.h; sourceTree = ""; }; + 277BCE8E1950D7FE00413CF5 /* SOVideoMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOVideoMessageCell.m; sourceTree = ""; }; 3E207892193DFC6300EC9FA4 /* Message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Message.h; sourceTree = ""; }; 3E207893193DFC6300EC9FA4 /* Message.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Message.m; sourceTree = ""; }; 3E3FB9A61920C3DC00986D8E /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -106,8 +114,6 @@ 75493C50190703C7006C6BCD /* Type1VC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Type1VC.h; sourceTree = ""; }; 75493C51190703C7006C6BCD /* Type1VC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Type1VC.m; sourceTree = ""; }; 75586C541908FFA1003BDEB1 /* doggy.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = doggy.jpg; sourceTree = ""; }; - 75B35A0F1922B80800513423 /* NSString+Calculation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Calculation.h"; sourceTree = ""; }; - 75B35A101922B80800513423 /* NSString+Calculation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Calculation.m"; sourceTree = ""; }; 75B35A121922B80800513423 /* attachment.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = attachment.png; sourceTree = ""; }; 75B35A131922B80800513423 /* attachment@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "attachment@2x.png"; sourceTree = ""; }; 75B35A141922B80800513423 /* bubble.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bubble.png; sourceTree = ""; }; @@ -339,14 +345,18 @@ 75B35A0E1922B80800513423 /* SOMessaging */ = { isa = PBXGroup; children = ( - 75B35A0F1922B80800513423 /* NSString+Calculation.h */, - 75B35A101922B80800513423 /* NSString+Calculation.m */, 75B35A111922B80800513423 /* Resources */, 75B35A1C1922B80800513423 /* SOImageBrowserView.h */, 75B35A1D1922B80800513423 /* SOImageBrowserView.m */, 75B35A1E1922B80800513423 /* SOMessage.h */, 75B35A201922B80800513423 /* SOMessageCell.h */, 75B35A211922B80800513423 /* SOMessageCell.m */, + 277BCE891950D7FE00413CF5 /* SOPhotoMessageCell.h */, + 277BCE8A1950D7FE00413CF5 /* SOPhotoMessageCell.m */, + 277BCE8B1950D7FE00413CF5 /* SOTextMessageCell.h */, + 277BCE8C1950D7FE00413CF5 /* SOTextMessageCell.m */, + 277BCE8D1950D7FE00413CF5 /* SOVideoMessageCell.h */, + 277BCE8E1950D7FE00413CF5 /* SOVideoMessageCell.m */, 75B35A221922B80800513423 /* SOMessageInputView.h */, 75B35A231922B80800513423 /* SOMessageInputView.m */, 75B35A241922B80800513423 /* SOMessageType.h */, @@ -497,15 +507,17 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 277BCE8F1950D7FE00413CF5 /* SOPhotoMessageCell.m in Sources */, 3EBD810C191A4D2B0007B68A /* Type3VC.m in Sources */, 75B35A3C1922B80800513423 /* SOMessagingViewController.m in Sources */, 75B35A3A1922B80800513423 /* SOMessageCell.m in Sources */, 3E207894193DFC6300EC9FA4 /* Message.m in Sources */, 3EBD8109191A4D0A0007B68A /* Type2VC.m in Sources */, 75493C1E19070071006C6BCD /* main.m in Sources */, + 277BCE911950D7FE00413CF5 /* SOVideoMessageCell.m in Sources */, 75B35A381922B80800513423 /* SOImageBrowserView.m in Sources */, - 75B35A2D1922B80800513423 /* NSString+Calculation.m in Sources */, 3EBD8113191A566B0007B68A /* ContentManager.m in Sources */, + 277BCE901950D7FE00413CF5 /* SOTextMessageCell.m in Sources */, 75B35A3E1922B80800513423 /* UINavigationController+Rotation.m in Sources */, 75B35A3B1922B80800513423 /* SOMessageInputView.m in Sources */, 75B35A3D1922B80800513423 /* SOPlaceholderedTextView.m in Sources */, diff --git a/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type1VC.m b/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type1VC.m index c765ac5..9ef46b1 100644 --- a/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type1VC.m +++ b/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type1VC.m @@ -51,10 +51,8 @@ - (void)configureMessageCell:(SOMessageCell *)cell forMessageAtIndex:(NSInteger) // Adjusting content for 3pt. (In this demo the width of bubble's tail is 6pt) if (!message.fromMe) { cell.contentInsets = UIEdgeInsetsMake(0, 3.0f, 0, 0); //Move content for 3 pt. to right - cell.textView.textColor = [UIColor blackColor]; } else { cell.contentInsets = UIEdgeInsetsMake(0, 0, 0, 3.0f); //Move content for 3 pt. to left - cell.textView.textColor = [UIColor whiteColor]; } } diff --git a/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type2VC.m b/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type2VC.m index 9f832b1..edefcf6 100644 --- a/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type2VC.m +++ b/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type2VC.m @@ -57,10 +57,8 @@ - (void)configureMessageCell:(SOMessageCell *)cell forMessageAtIndex:(NSInteger) // Adjusting content for 3pt. (In this demo the width of bubble's tail is 3pt) if (!message.fromMe) { cell.contentInsets = UIEdgeInsetsMake(0, 3.0f, 0, 0); //Move content for 3 pt. to right - cell.textView.textColor = [UIColor blackColor]; } else { cell.contentInsets = UIEdgeInsetsMake(0, 0, 0, 3.0f); //Move content for 3 pt. to left - cell.textView.textColor = [UIColor whiteColor]; } cell.userImageView.layer.cornerRadius = self.userImageSize.width/2; diff --git a/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type3VC.m b/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type3VC.m index 8fb100a..c6b4f8c 100644 --- a/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type3VC.m +++ b/DemoProject/SOSimpleChatDemo/Sources/Controllers/Type3VC.m @@ -9,6 +9,7 @@ #import "Type3VC.h" #import "ContentManager.h" #import "Message.h" +#import "SOTextMessageCell.h" @interface Type3VC () @@ -73,7 +74,9 @@ - (void)configureMessageCell:(SOMessageCell *)cell forMessageAtIndex:(NSInteger) cell.contentInsets = UIEdgeInsetsMake(0, 0, 0, 4.0f); //Move content for 4 pt. to left } - cell.textView.textColor = [UIColor blackColor]; + if([cell isKindOfClass:[SOTextMessageCell class]]){ + ((SOTextMessageCell*)cell).textView.textColor = [UIColor blackColor]; + } cell.userImageView.layer.cornerRadius = 3; @@ -90,6 +93,8 @@ - (void)configureMessageCell:(SOMessageCell *)cell forMessageAtIndex:(NSInteger) //-----------------------------------------------// // Adding datetime label under balloon //-----------------------------------------------// + [cell adjustCell]; + UILabel *label = [self generateLabelForCell:cell]; UILabel *existingLabel = [[cell.contentView.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tag == %d",label.tag]] lastObject];; diff --git a/SOMessaging/NSString+Calculation.m b/SOMessaging/NSString+Calculation.m deleted file mode 100644 index fedd6d7..0000000 --- a/SOMessaging/NSString+Calculation.m +++ /dev/null @@ -1,62 +0,0 @@ -// -// NSString+Calculation.m -// SOMessaging -// -// Created by : arturdev -// Copyright (c) 2014 SocialObjects Software. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE - -#import "NSString+Calculation.h" - -@implementation NSString (Calculation) - -- (CGSize)usedSizeForMaxWidth:(CGFloat)width withFont:(UIFont *)font -{ - NSTextStorage *textStorage = [[NSTextStorage alloc] - initWithString:self]; - NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize: CGSizeMake(width, MAXFLOAT)]; - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; - [layoutManager addTextContainer:textContainer]; - [textStorage addLayoutManager:layoutManager]; - [textStorage addAttribute:NSFontAttributeName value:font - range:NSMakeRange(0, [textStorage length])]; - [textContainer setLineFragmentPadding:0.0]; - - [layoutManager glyphRangeForTextContainer:textContainer]; - CGRect frame = [layoutManager usedRectForTextContainer:textContainer]; - return frame.size; -} - -- (CGSize)usedSizeForMaxWidth:(CGFloat)width withAttributes:(NSDictionary *)attributes -{ - NSAttributedString *attrutedString = [[NSAttributedString alloc] initWithString:self attributes:attributes]; - - UITextView *tempTextView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, width, 0)]; - [tempTextView setTextContainerInset:UIEdgeInsetsZero]; - tempTextView.textContainer.lineFragmentPadding = 0; - - tempTextView.attributedText = attrutedString; - [tempTextView.layoutManager glyphRangeForTextContainer:tempTextView.textContainer]; - - CGRect usedFrame = [tempTextView.layoutManager usedRectForTextContainer:tempTextView.textContainer]; - - return usedFrame.size; -} - -@end diff --git a/SOMessaging/SOMessageCell.h b/SOMessaging/SOMessageCell.h index 6f2f470..87ac130 100644 --- a/SOMessaging/SOMessageCell.h +++ b/SOMessaging/SOMessageCell.h @@ -30,6 +30,8 @@ #define kBubbleRightMargin 7 #define kBubbleBottomMargin 20 +static const int userImageViewLeftMargin = 3; + @class SOMessageCell; @protocol SOMessageCellDelegate @@ -47,10 +49,7 @@ @property (strong, nonatomic) UIFont *messageFont; @property (strong, nonatomic) UIImageView *userImageView; -@property (strong, nonatomic) UITextView *textView; @property (strong, nonatomic) UILabel *timeLabel; //appears while dragging cell -@property (strong, nonatomic) UIImageView *mediaImageView; -@property (strong, nonatomic) UIView *mediaOverlayView; // For video only @property (strong, nonatomic) UIImageView *balloonImageView; @@ -68,8 +67,6 @@ + (CGFloat) maxContentOffsetX; + (void) setMaxContentOffsetX:(CGFloat)offsetX; -+ (void)setDefaultConfigs; - @property (nonatomic) CGFloat balloonMinWidth; @property (nonatomic) CGFloat balloonMinHeight; @property (nonatomic) CGFloat messageMaxWidth; @@ -81,9 +78,11 @@ @property (weak, nonatomic) id delegate; - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth; -- (void)setMediaImageViewSize:(CGSize)size; -- (void)setUserImageViewSize:(CGSize)size; + +@property (nonatomic) CGSize mediaImageViewSize; +@property (nonatomic) CGSize userImageViewSize; - (void)adjustCell; +- (void)layoutChatBalloon; @end diff --git a/SOMessaging/SOMessageCell.m b/SOMessaging/SOMessageCell.m index d18c752..a6ed9de 100644 --- a/SOMessaging/SOMessageCell.m +++ b/SOMessaging/SOMessageCell.m @@ -23,45 +23,28 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE #import "SOMessageCell.h" -#import "NSString+Calculation.h" @interface SOMessageCell() < UIGestureRecognizerDelegate> { BOOL isHorizontalPan; } -@property (nonatomic) CGSize mediaImageViewSize; -@property (nonatomic) CGSize userImageViewSize; @end @implementation SOMessageCell -static CGFloat messageTopMargin; -static CGFloat messageBottomMargin; -static CGFloat messageLeftMargin; -static CGFloat messageRightMargin; +static CGFloat messageTopMargin = 9; +static CGFloat messageBottomMargin = 9; +static CGFloat messageLeftMargin = 15; +static CGFloat messageRightMargin = 15; -static CGFloat maxContentOffsetX; -static CGFloat contentOffsetX; +static CGFloat maxContentOffsetX = 50; +static CGFloat contentOffsetX = 0; static CGFloat initialTimeLabelPosX; static BOOL cellIsDragging; -+ (void)load -{ - [self setDefaultConfigs]; -} - -+ (void)setDefaultConfigs -{ - messageTopMargin = 9; - messageBottomMargin = 9; - messageLeftMargin = 15; - messageRightMargin = 15; - - contentOffsetX = 0; - maxContentOffsetX = 50; -} +static NSDateFormatter* dateFormatter; - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth { @@ -74,7 +57,10 @@ - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reus self.panGesture.delegate = self; [self addGestureRecognizer:self.panGesture]; - [self setInitialSizes]; + [self initContainerView]; + [self initUserImageView]; + [self initBalloon]; + [self initTimeLabel]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOrientationWillChandeNote:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil]; @@ -83,69 +69,48 @@ - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reus return self; } -- (void)setInitialSizes +-(void)initContainerView { - if (self.containerView) { - [self.containerView removeFromSuperview]; - } - if (self.timeLabel) { - [self.timeLabel removeFromSuperview]; - } + self.containerView = [[UIView alloc] initWithFrame:self.contentView.bounds]; + self.containerView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; + [self.contentView addSubview:self.containerView]; +} + +-(void)initUserImageView +{ self.userImageView = [[UIImageView alloc] init]; self.userImageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; - self.textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, self.messageMaxWidth, 0)]; - self.timeLabel = [[UILabel alloc] init]; - self.mediaImageView = [[UIImageView alloc] init]; - self.mediaOverlayView = [[UIView alloc] init]; - self.balloonImageView = [[UIImageView alloc] init]; - + if (!CGSizeEqualToSize(self.userImageViewSize, CGSizeZero)) { CGRect frame = self.userImageView.frame; frame.size = self.userImageViewSize; self.userImageView.frame = frame; } - self.userImageView.contentMode = UIViewContentModeScaleAspectFill; self.userImageView.clipsToBounds = YES; self.userImageView.backgroundColor = [UIColor clearColor]; self.userImageView.layer.cornerRadius = 5; + self.userImageView.hidden = YES; - if (!CGSizeEqualToSize(self.mediaImageViewSize, CGSizeZero)) { - CGRect frame = self.mediaImageView.frame; - frame.size = self.mediaImageViewSize; - self.mediaImageView.frame = frame; - } - - self.mediaImageView.contentMode = UIViewContentModeScaleAspectFill; - self.mediaImageView.clipsToBounds = YES; - self.mediaImageView.backgroundColor = [UIColor clearColor]; - self.mediaImageView.userInteractionEnabled = YES; -// self.mediaImageView.layer.cornerRadius = 10; - UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMediaTapped:)]; - [self.mediaImageView addGestureRecognizer:tap]; - - self.mediaOverlayView.backgroundColor = [UIColor clearColor]; - [self.mediaImageView addSubview:self.mediaOverlayView]; - - self.textView.textColor = [UIColor whiteColor]; - self.textView.backgroundColor = [UIColor clearColor]; - [self.textView setTextContainerInset:UIEdgeInsetsZero]; - self.textView.textContainer.lineFragmentPadding = 0; - - [self hideSubViews]; - - self.containerView = [[UIView alloc] initWithFrame:self.contentView.bounds]; - self.containerView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; - - [self.contentView addSubview:self.containerView]; + [self.containerView addSubview:self.userImageView]; +} + +-(void)initBalloon +{ + self.balloonImageView = [[UIImageView alloc] init]; [self.containerView addSubview:self.balloonImageView]; - [self.containerView addSubview:self.textView]; - [self.containerView addSubview:self.mediaImageView]; - [self.containerView addSubview:self.userImageView]; +} + +- (void)initTimeLabel +{ + if(!dateFormatter){ + dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"HH:mm"];; + } - [self.contentView addSubview:self.timeLabel]; + self.timeLabel = [[UILabel alloc] init]; self.contentView.clipsToBounds = NO; self.clipsToBounds = NO; @@ -153,29 +118,13 @@ - (void)setInitialSizes self.timeLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:12]; self.timeLabel.textColor = [UIColor grayColor]; self.timeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + + [self.contentView addSubview:self.timeLabel]; } - (void)hideSubViews { self.userImageView.hidden = YES; - self.textView.hidden = YES; - self.mediaImageView.hidden = YES; -} - -- (void)setMediaImageViewSize:(CGSize)size -{ - _mediaImageViewSize = size; - CGRect frame = self.mediaImageView.frame; - frame.size = size; - self.mediaImageView.frame = frame; -} - -- (void)setUserImageViewSize:(CGSize)size -{ - _userImageViewSize = size; - CGRect frame = self.userImageView.frame; - frame.size = size; - self.userImageView.frame = frame; } - (void)setUserImage:(UIImage *)userImage @@ -184,301 +133,91 @@ - (void)setUserImage:(UIImage *)userImage if (!userImage) { self.userImageViewSize = CGSizeZero; } - [self adjustCell]; -} -#pragma mark - -- (void)handleMediaTapped:(UITapGestureRecognizer *)tap -{ - if (self.delegate && [self.delegate respondsToSelector:@selector(messageCell:didTapMedia:)]) { - [self.delegate messageCell:self didTapMedia:self.message.media]; - } -} - -#pragma mark - -- (void)setMessage:(id)message -{ - _message = message; - - [self setInitialSizes]; -// [self adjustCell]; } - (void)adjustCell { [self hideSubViews]; - - if (self.message.type == SOMessageTypeText) { - self.textView.hidden = NO; - [self adjustForTextOnly]; - } else if (self.message.type == SOMessageTypePhoto) { - self.mediaImageView.hidden = NO; - [self adjustForPhotoOnly]; - } else if (self.message.type == SOMessageTypeVideo) { - self.mediaImageView.hidden = NO; - [self adjustForVideoOnly]; - } else if (self.message.type == SOMessageTypeOther) { - if (!CGSizeEqualToSize(self.userImageViewSize, CGSizeZero) && self.userImage) { - self.userImageView.hidden = NO; - } - } - - self.containerView.autoresizingMask = self.message.fromMe ? UIViewAutoresizingFlexibleLeftMargin : UIViewAutoresizingFlexibleRightMargin; - initialTimeLabelPosX = self.timeLabel.frame.origin.x; -/* --- Not implemented --- - else if (self.message.type & (SOMessageTypePhoto | SOMessageTypeText)) { - self.textView.hidden = NO; - self.mediaImageView.hidden = NO; - } else if (self.message.type & (SOMessageTypeVideo | SOMessageTypeText)) { - self.textView.hidden = NO; - self.mediaImageView.hidden = NO; - } -*/ - -} - -- (void)adjustForTextOnly -{ - CGFloat userImageViewLeftMargin = 3; - - CGRect usedFrame = [self usedRectForWidth:self.messageMaxWidth];; - if (self.balloonMinWidth) { - CGFloat messageMinWidth = self.balloonMinWidth - messageLeftMargin - messageRightMargin; - if (usedFrame.size.width < messageMinWidth) { - usedFrame.size.width = messageMinWidth; - - usedFrame.size.height = [self usedRectForWidth:messageMinWidth].size.height; - } - } - - CGFloat messageMinHeight = self.balloonMinHeight - messageTopMargin - messageBottomMargin; - - if (self.balloonMinHeight && usedFrame.size.height < messageMinHeight) { - usedFrame.size.height = messageMinHeight; - } - - self.textView.font = self.messageFont; - - CGRect frame = self.textView.frame; - frame.size.width = usedFrame.size.width; - frame.size.height = usedFrame.size.height; - frame.origin.y = messageTopMargin; - - CGRect balloonFrame = self.balloonImageView.frame; - balloonFrame.size.width = frame.size.width + messageLeftMargin + messageRightMargin; - balloonFrame.size.height = frame.size.height + messageTopMargin + messageBottomMargin; - balloonFrame.origin.y = 0; - frame.origin.x = self.message.fromMe ? messageLeftMargin : (balloonFrame.size.width - frame.size.width - messageLeftMargin); - if (!self.message.fromMe && self.userImage) { - frame.origin.x += userImageViewLeftMargin + self.userImageViewSize.width; - balloonFrame.origin.x = userImageViewLeftMargin + self.userImageViewSize.width; - } - - frame.origin.x += self.contentInsets.left - self.contentInsets.right; - - self.textView.frame = frame; - - CGRect userRect = self.userImageView.frame; - - if (!CGSizeEqualToSize(userRect.size, CGSizeZero) && self.userImage) { - if (balloonFrame.size.height < userRect.size.height) { - balloonFrame.size.height = userRect.size.height; - } - } - - self.balloonImageView.frame = balloonFrame; - self.balloonImageView.backgroundColor = [UIColor clearColor]; - self.balloonImageView.image = self.balloonImage; - - self.textView.editable = NO; - self.textView.scrollEnabled = NO; - self.textView.dataDetectorTypes = UIDataDetectorTypeLink | UIDataDetectorTypePhoneNumber; - - - - if (self.userImageView.autoresizingMask & UIViewAutoresizingFlexibleTopMargin) { - userRect.origin.y = balloonFrame.origin.y + balloonFrame.size.height - userRect.size.height; - } else { - userRect.origin.y = 0; - } - - if (self.message.fromMe) { - userRect.origin.x = balloonFrame.origin.x + userImageViewLeftMargin + balloonFrame.size.width; - } else { - userRect.origin.x = balloonFrame.origin.x - userImageViewLeftMargin - userRect.size.width; - } - self.userImageView.frame = userRect; - self.userImageView.image = self.userImage; - - CGRect frm = self.containerView.frame; - frm.origin.x = self.message.fromMe ? self.contentView.frame.size.width - balloonFrame.size.width - kBubbleRightMargin : kBubbleLeftMargin; - frm.origin.y = kBubbleTopMargin; - frm.size.height = balloonFrame.size.height; - frm.size.width = balloonFrame.size.width; - if (!CGSizeEqualToSize(userRect.size, CGSizeZero) && self.userImage) { - self.userImageView.hidden = NO; - frm.size.width += userImageViewLeftMargin + userRect.size.width; - if (self.message.fromMe) { - frm.origin.x -= userImageViewLeftMargin + userRect.size.width; - } - } - - - if (frm.size.height < self.userImageViewSize.height) { - CGFloat delta = self.userImageViewSize.height - frm.size.height; - frm.size.height = self.userImageViewSize.height; - - for (UIView *sub in self.containerView.subviews) { - CGRect fr = sub.frame; - fr.origin.y += delta; - sub.frame = fr; - } - } - self.containerView.frame = frm; + [self layoutChatBalloon]; + [self adjustContentViewAndImageView]; // Adjusing time label - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; - [formatter setDateFormat:@"HH:mm"]; - self.timeLabel.frame = CGRectZero; - self.timeLabel.text = [formatter stringFromDate:self.message.date]; - + self.timeLabel.text = [dateFormatter stringFromDate:self.message.date]; + [self.timeLabel sizeToFit]; CGRect timeLabel = self.timeLabel.frame; timeLabel.origin.x = self.contentView.frame.size.width + 5; self.timeLabel.frame = timeLabel; self.timeLabel.center = CGPointMake(self.timeLabel.center.x, self.containerView.center.y); -} - -- (CGRect)usedRectForWidth:(CGFloat)width -{ - CGRect usedFrame = CGRectZero; - - if (self.message.attributes) { - NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.message.text attributes:self.message.attributes]; - self.textView.attributedText = attributedText; - usedFrame.size = [self.message.text usedSizeForMaxWidth:width withAttributes:self.message.attributes]; - } else { - self.textView.text = self.message.text; - usedFrame.size = [self.message.text usedSizeForMaxWidth:width withFont:self.messageFont]; - } + self.containerView.autoresizingMask = self.message.fromMe ? UIViewAutoresizingFlexibleLeftMargin : UIViewAutoresizingFlexibleRightMargin; + initialTimeLabelPosX = self.timeLabel.frame.origin.x; - return usedFrame; + /* + -- Not implemented --- + else if (self.message.type & (SOMessageTypePhoto | SOMessageTypeText)) { + self.textView.hidden = NO; + self.mediaImageView.hidden = NO; + } else if (self.message.type & (SOMessageTypeVideo | SOMessageTypeText)) { + self.textView.hidden = NO; + self.mediaImageView.hidden = NO; + } + */ } -- (void)adjustForPhotoOnly +-(void)adjustContentViewAndImageView { - CGFloat userImageViewLeftMargin = 3; - - UIImage *image = self.message.thumbnail; - if (!image) { - image = [[UIImage alloc] initWithData:self.message.media]; - } - self.mediaImageView.image = image; - - CGRect frame = CGRectZero; - frame.size = self.mediaImageViewSize; - - if (!self.message.fromMe && self.userImage) { - frame.origin.x += userImageViewLeftMargin + self.userImageViewSize.width; - } - - self.mediaImageView.frame = frame; - - self.balloonImageView.frame = frame; - self.balloonImageView.backgroundColor = [UIColor clearColor]; - self.balloonImageView.image = self.balloonImage; - - CGRect userRect = self.userImageView.frame; + CGRect balloonFrame = self.balloonImageView.frame; + CGRect userRect = CGRectZero; + userRect.size = self.userImageViewSize; if (self.userImageView.autoresizingMask & UIViewAutoresizingFlexibleTopMargin) { - userRect.origin.y = frame.origin.y + frame.size.height - userRect.size.height; + userRect.origin.y = balloonFrame.origin.y + balloonFrame.size.height - userRect.size.height; } else { userRect.origin.y = 0; } if (self.message.fromMe) { - userRect.origin.x = frame.origin.x + userImageViewLeftMargin + frame.size.width; + userRect.origin.x = balloonFrame.origin.x + userImageViewLeftMargin + balloonFrame.size.width; } else { - userRect.origin.x = frame.origin.x - userImageViewLeftMargin - userRect.size.width; + userRect.origin.x = balloonFrame.origin.x - userImageViewLeftMargin - userRect.size.width; } self.userImageView.frame = userRect; self.userImageView.image = self.userImage; - CGRect frm = self.containerView.frame; - frm.origin.x = self.message.fromMe ? self.contentView.frame.size.width - frame.size.width - kBubbleRightMargin : kBubbleLeftMargin; - frm.origin.y = kBubbleTopMargin; - frm.size.width = frame.size.width; + CGRect frm = CGRectMake(self.message.fromMe ? self.contentView.frame.size.width - balloonFrame.size.width - kBubbleRightMargin : kBubbleLeftMargin, + kBubbleTopMargin, + balloonFrame.size.width, + balloonFrame.size.height); if (!CGSizeEqualToSize(userRect.size, CGSizeZero) && self.userImage) { self.userImageView.hidden = NO; - frm.size.width += userImageViewLeftMargin + userRect.size.width; + + CGFloat offset = userImageViewLeftMargin + userRect.size.width; + frm.size.width += offset; if (self.message.fromMe) { - frm.origin.x -= userImageViewLeftMargin + userRect.size.width; + frm.origin.x -= offset; } } - - frm.size.height = frame.size.height; + if (frm.size.height < self.userImageViewSize.height) { CGFloat delta = self.userImageViewSize.height - frm.size.height; - frm.size.height = self.userImageViewSize.height; - for (UIView *sub in self.containerView.subviews) { - CGRect fr = sub.frame; - fr.origin.y += delta; - sub.frame = fr; - } + frm.size.height = self.userImageViewSize.height; + frm.origin.y += delta; } - self.containerView.frame = frm; - - //Masking mediaImageView with balloon image - CALayer *layer = self.balloonImageView.layer; - layer.frame = (CGRect){{0,0},self.balloonImageView.layer.frame.size}; - self.mediaImageView.layer.mask = layer; - [self.mediaImageView setNeedsDisplay]; + UIViewAutoresizing m = self.userImageView.autoresizingMask; + self.userImageView.autoresizingMask = UIViewAutoresizingNone; + self.containerView.frame = frm; - // Adjusing time label - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; - [formatter setDateFormat:@"HH:mm"]; - self.timeLabel.frame = CGRectZero; - self.timeLabel.text = [formatter stringFromDate:self.message.date]; - - [self.timeLabel sizeToFit]; - CGRect timeLabel = self.timeLabel.frame; - timeLabel.origin.x = self.contentView.frame.size.width + 5; - self.timeLabel.frame = timeLabel; - self.timeLabel.center = CGPointMake(self.timeLabel.center.x, self.containerView.center.y); + self.userImageView.autoresizingMask = m; } -- (void)adjustForVideoOnly +-(void)layoutChatBalloon { - [self adjustForPhotoOnly]; - - CGRect frame = self.mediaOverlayView.frame; - frame.origin = CGPointZero; - frame.size = self.mediaImageView.frame.size; - self.mediaOverlayView.frame = frame; - - [self.mediaOverlayView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; - UIView *bgView = [[UIView alloc] init]; - bgView.frame = self.mediaImageView.bounds; - bgView.backgroundColor = [UIColor blackColor]; - bgView.alpha = 0.4f; - [self.mediaOverlayView addSubview:bgView]; - - UIImageView *playButtonImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"play_button.png"]]; - playButtonImageView.contentMode = UIViewContentModeScaleAspectFit; - playButtonImageView.clipsToBounds = YES; - playButtonImageView.backgroundColor = [UIColor clearColor]; - CGRect playFrame = playButtonImageView.frame; - playFrame.size = CGSizeMake(20, 20); - playButtonImageView.frame = playFrame; - playButtonImageView.center = CGPointMake(self.mediaOverlayView.frame.size.width/2 + self.contentInsets.left - self.contentInsets.right, self.mediaOverlayView.frame.size.height/2); - [self.mediaOverlayView addSubview:playButtonImageView]; } - #pragma mark - GestureRecognizer delegates - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { diff --git a/SOMessaging/SOMessagingViewController.m b/SOMessaging/SOMessagingViewController.m index 24de9a6..65f175d 100644 --- a/SOMessaging/SOMessagingViewController.m +++ b/SOMessaging/SOMessagingViewController.m @@ -25,8 +25,9 @@ #import "SOMessagingViewController.h" #import "SOMessage.h" #import "SOMessageCell.h" - -#import "NSString+Calculation.h" +#import "SOPhotoMessageCell.h" +#import "SOTextMessageCell.h" +#import "SOVideoMessageCell.h" #import "SOImageBrowserView.h" #import @@ -55,6 +56,8 @@ @implementation SOMessagingViewController { dispatch_once_t onceToken; } +static NSDateFormatter* dateFormatter; + - (void)setup { self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; @@ -170,10 +173,12 @@ - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger id firstMessageInGroup = [self.conversation[section] firstObject]; NSDate *date = [firstMessageInGroup date]; - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; - [formatter setDateFormat:@"dd MMM, eee, HH:mm"]; UILabel *label = [[UILabel alloc] init]; - label.text = [formatter stringFromDate:date]; + if(!dateFormatter){ + dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"dd MMM, eee, HH:mm"]; + } + label.text = [dateFormatter stringFromDate:date]; label.textColor = [UIColor grayColor]; label.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:12]; @@ -189,18 +194,32 @@ - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *cellIdentifier = @"sendCell"; + NSString *cellIdentifier; SOMessageCell *cell; id message = self.conversation[indexPath.section][indexPath.row]; + Class class; + if(message.type == SOMessageTypeText){ + cellIdentifier = @"textCell"; + class = [SOTextMessageCell class]; + }else if(message.type == SOMessageTypeVideo){ + cellIdentifier = @"videoCell"; + class = [SOVideoMessageCell class]; + }else if(message.type == SOMessageTypePhoto){ + cellIdentifier = @"photoCell"; + class = [SOPhotoMessageCell class]; + } + cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (!cell) { - cell = [[SOMessageCell alloc] initWithStyle:UITableViewCellStyleDefault + cell = [[class alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier messageMaxWidth:[self messageMaxWidth]]; } + + [cell setMessageMaxWidth:[self messageMaxWidth]]; [cell setMediaImageViewSize:[self mediaThumbnailSize]]; [cell setUserImageViewSize:[self userImageSize]]; cell.tableView = self.tableView; @@ -210,8 +229,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.messageFont = [self messageFont]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.balloonImage = message.fromMe ? self.balloonSendImage : self.balloonReceiveImage; - cell.textView.textColor = message.fromMe ? [UIColor whiteColor] : [UIColor blackColor]; - cell.message = message; + cell.message = message; // For user customization int index = (int)[[self messages] indexOfObject:message]; @@ -235,21 +253,15 @@ - (CGFloat)heightForMessageForIndex:(NSInteger)index id message = [self messages][index]; if (message.type == SOMessageTypeText) { - CGSize size = [message.text usedSizeForMaxWidth:[self messageMaxWidth] withFont:[self messageFont]]; - if (message.attributes) { - size = [message.text usedSizeForMaxWidth:[self messageMaxWidth] withAttributes:message.attributes]; - } + CGSize size = [SOTextMessageCell sizeForMessage:message constrainedToWidth:[self messageMaxWidth] withFont:[self messageFont]]; if (self.balloonMinWidth) { CGFloat messageMinWidth = self.balloonMinWidth - [SOMessageCell messageLeftMargin] - [SOMessageCell messageRightMargin]; if (size.width < messageMinWidth) { size.width = messageMinWidth; - CGSize newSize = [message.text usedSizeForMaxWidth:messageMinWidth withFont:[self messageFont]]; - if (message.attributes) { - newSize = [message.text usedSizeForMaxWidth:messageMinWidth withAttributes:message.attributes]; - } - + CGSize newSize = [SOTextMessageCell sizeForMessage:message constrainedToWidth:messageMinWidth withFont:[self messageFont]]; + size.height = newSize.height; } } diff --git a/SOMessaging/SOPhotoMessageCell.h b/SOMessaging/SOPhotoMessageCell.h new file mode 100644 index 0000000..ffec67b --- /dev/null +++ b/SOMessaging/SOPhotoMessageCell.h @@ -0,0 +1,30 @@ +// +// SOPhotoMessageCell.h +// SOMessaging +// +// Created by : mspensieri +// Copyright (c) 2014 SocialObjects Software. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +#import "SOMessageCell.h" + +@interface SOPhotoMessageCell : SOMessageCell + +@property (strong, nonatomic) UIImageView *mediaImageView; + +@end diff --git a/SOMessaging/SOPhotoMessageCell.m b/SOMessaging/SOPhotoMessageCell.m new file mode 100644 index 0000000..430aa81 --- /dev/null +++ b/SOMessaging/SOPhotoMessageCell.m @@ -0,0 +1,102 @@ +// +// SOPhotoMessageCell.m +// SOMessaging +// +// Created by : mspensieri +// Copyright (c) 2014 SocialObjects Software. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE + +#import "SOPhotoMessageCell.h" + +@implementation SOPhotoMessageCell + +-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier messageMaxWidth:messageMaxWidth]; + + if (self) { + [self initMediaImageView]; + } + + return self; +} + +-(void)initMediaImageView +{ + self.mediaImageView = [[UIImageView alloc] init]; + + if (!CGSizeEqualToSize(self.mediaImageViewSize, CGSizeZero)) { + CGRect frame = self.mediaImageView.frame; + frame.size = self.mediaImageViewSize; + self.mediaImageView.frame = frame; + } + + self.mediaImageView.contentMode = UIViewContentModeScaleAspectFill; + self.mediaImageView.clipsToBounds = YES; + self.mediaImageView.backgroundColor = [UIColor clearColor]; + self.mediaImageView.userInteractionEnabled = YES; + // self.mediaImageView.layer.cornerRadius = 10; + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMediaTapped:)]; + [self.mediaImageView addGestureRecognizer:tap]; + + [self.containerView addSubview:self.mediaImageView]; +} + +-(void)layoutChatBalloon +{ + UIImage *image = self.message.thumbnail; + if (!image) { + image = [[UIImage alloc] initWithData:self.message.media]; + } + self.mediaImageView.image = image; + + CGRect frame = CGRectZero; + frame.size = self.mediaImageViewSize; + + if (!self.message.fromMe && self.userImage) { + frame.origin.x += userImageViewLeftMargin + self.userImageViewSize.width; + } + + self.mediaImageView.frame = frame; + + self.balloonImageView.frame = frame; + self.balloonImageView.backgroundColor = [UIColor clearColor]; + self.balloonImageView.image = self.balloonImage; +} + +-(void)adjustCell +{ + [super adjustCell]; + + //Masking mediaImageView with balloon image + CALayer *layer = self.balloonImageView.layer; + layer.frame = (CGRect){{0,0},self.balloonImageView.layer.frame.size}; + self.mediaImageView.layer.mask = layer; + [self.mediaImageView setNeedsDisplay]; +} + +#pragma mark - +- (void)handleMediaTapped:(UITapGestureRecognizer *)tap +{ + if (self.delegate && [self.delegate respondsToSelector:@selector(messageCell:didTapMedia:)]) { + [self.delegate messageCell:self didTapMedia:self.message.media]; + } +} + +@end diff --git a/SOMessaging/SOTextMessageCell.h b/SOMessaging/SOTextMessageCell.h new file mode 100644 index 0000000..5005fa2 --- /dev/null +++ b/SOMessaging/SOTextMessageCell.h @@ -0,0 +1,33 @@ +// +// SOTextMessageCell.h +// SOMessaging +// +// Created by : mspensieri +// Copyright (c) 2014 SocialObjects Software. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE + +#import "SOMessageCell.h" + +@interface SOTextMessageCell : SOMessageCell + +@property (strong, nonatomic) UITextView *textView; + ++(CGSize)sizeForMessage:(id)message constrainedToWidth:(CGFloat)width withFont:(UIFont*)font; + +@end diff --git a/SOMessaging/SOTextMessageCell.m b/SOMessaging/SOTextMessageCell.m new file mode 100644 index 0000000..56b9451 --- /dev/null +++ b/SOMessaging/SOTextMessageCell.m @@ -0,0 +1,152 @@ +// +// SOTextMessageCell.m +// SOMessaging +// +// Created by : mspensieri +// Copyright (c) 2014 SocialObjects Software. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE + +#import "SOTextMessageCell.h" + + +@implementation SOTextMessageCell + ++(CGSize)sizeForMessage:(id)message constrainedToWidth:(CGFloat)width withFont:(UIFont*)font +{ + static UITextView* textMeasurementView; + if(!textMeasurementView){ + textMeasurementView = [self newTextView]; + textMeasurementView.frame = CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX); + } + + // Performance optimization + if(textMeasurementView.font != font){ + textMeasurementView.font = font; + } + + [self setTextForMessage:message onTextView:textMeasurementView]; + + return [textMeasurementView sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)]; +} + ++(void)setTextForMessage:(id)message onTextView:(UITextView*)textView +{ + if (message.attributes) { + NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:message.text attributes:message.attributes]; + textView.attributedText = attributedText; + } else { + textView.text = message.text; + } +} + +-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier messageMaxWidth:messageMaxWidth]; + + if (self) { + [self initTextView]; + } + + return self; +} + +-(void)initTextView +{ + self.textView = [self.class newTextView]; + self.textView.frame = CGRectMake(0, 0, self.messageMaxWidth, 0); + + [self.containerView addSubview:self.textView]; +} + ++(UITextView*)newTextView +{ + UITextView* textView = [[UITextView alloc] init]; + + textView.textColor = [UIColor whiteColor]; + textView.backgroundColor = [UIColor clearColor]; + [textView setTextContainerInset:UIEdgeInsetsZero]; + textView.textContainer.lineFragmentPadding = 0; + textView.editable = NO; + textView.scrollEnabled = NO; + textView.dataDetectorTypes = UIDataDetectorTypeLink | UIDataDetectorTypePhoneNumber; + + return textView; +} + +-(void)setMessage:(id)message +{ + [super setMessage:message]; + self.textView.textColor = message.fromMe ? [UIColor whiteColor] : [UIColor blackColor]; +} + +-(void)layoutChatBalloon +{ + // Performance optimization + if(self.textView.font != self.messageFont){ + self.textView.font = self.messageFont; + } + + [self.class setTextForMessage:self.message onTextView:self.textView]; + + CGSize textSize = [self.class sizeForMessage:self.message constrainedToWidth:self.messageMaxWidth withFont:self.messageFont]; + if (self.balloonMinWidth) { + CGFloat messageMinWidth = self.balloonMinWidth - [self.class messageLeftMargin] - [self.class messageRightMargin]; + if (textSize.width < messageMinWidth) { + textSize.width = messageMinWidth; + + textSize.height = [self.class sizeForMessage:self.message constrainedToWidth:messageMinWidth withFont:self.messageFont].height; + } + } + + CGFloat messageMinHeight = self.balloonMinHeight - [self.class messageTopMargin] - [self.class messageBottomMargin]; + + if (self.balloonMinHeight && textSize.height < messageMinHeight) { + textSize.height = messageMinHeight; + } + + CGRect frame = self.textView.frame; + frame.size = textSize; + frame.origin.y = [self.class messageTopMargin]; + + CGRect balloonFrame = CGRectZero; + balloonFrame.size.width = frame.size.width + [self.class messageLeftMargin] + [self.class messageRightMargin]; + balloonFrame.size.height = frame.size.height + [self.class messageTopMargin] + [self.class messageBottomMargin]; + balloonFrame.origin.y = 0; + frame.origin.x = self.message.fromMe ? [self.class messageLeftMargin] : (balloonFrame.size.width - frame.size.width - [self.class messageLeftMargin]); + if (!self.message.fromMe && self.userImage) { + frame.origin.x += userImageViewLeftMargin + self.userImageViewSize.width; + balloonFrame.origin.x = userImageViewLeftMargin + self.userImageViewSize.width; + } + + frame.origin.x += self.contentInsets.left - self.contentInsets.right; + + self.textView.frame = frame; + + if (!CGSizeEqualToSize(self.userImageViewSize, CGSizeZero) && self.userImage) { + if (balloonFrame.size.height < self.userImageViewSize.height) { + balloonFrame.size.height = self.userImageViewSize.height; + } + } + + self.balloonImageView.frame = balloonFrame; + self.balloonImageView.backgroundColor = [UIColor clearColor]; + self.balloonImageView.image = self.balloonImage; +} + +@end diff --git a/SOMessaging/NSString+Calculation.h b/SOMessaging/SOVideoMessageCell.h similarity index 80% rename from SOMessaging/NSString+Calculation.h rename to SOMessaging/SOVideoMessageCell.h index 4a4cd6f..54d183e 100644 --- a/SOMessaging/NSString+Calculation.h +++ b/SOMessaging/SOVideoMessageCell.h @@ -1,8 +1,8 @@ // -// NSString+Calculation.h +// SOVideoMessageCell.h // SOMessaging // -// Created by : arturdev +// Created by : mspensieri // Copyright (c) 2014 SocialObjects Software. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -22,11 +22,10 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE -#import +#import "SOPhotoMessageCell.h" -@interface NSString (Calculation) +@interface SOVideoMessageCell : SOPhotoMessageCell -- (CGSize)usedSizeForMaxWidth:(CGFloat)width withFont:(UIFont *)font; -- (CGSize)usedSizeForMaxWidth:(CGFloat)width withAttributes:(NSDictionary *)attributes; +@property (strong, nonatomic) UIView *mediaOverlayView; // For video only @end diff --git a/SOMessaging/SOVideoMessageCell.m b/SOMessaging/SOVideoMessageCell.m new file mode 100644 index 0000000..e2c7b42 --- /dev/null +++ b/SOMessaging/SOVideoMessageCell.m @@ -0,0 +1,91 @@ +// +// SOVideoMessageCell.m +// SOMessaging +// +// Created by : mspensieri +// Copyright (c) 2014 SocialObjects Software. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE + +#import "SOVideoMessageCell.h" + +@interface SOVideoMessageCell() + +@property UIView* dimmingView; +@property UIImageView *playButtonImageView; + +@end + +@implementation SOVideoMessageCell + +-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier messageMaxWidth:messageMaxWidth]; + + if (self) { + [self initMediaOverlayView]; + [self initDimmingView]; + [self initPlayButton]; + } + + return self; +} + +-(void)initMediaOverlayView +{ + self.mediaOverlayView = [[UIView alloc] init]; + + self.mediaOverlayView.backgroundColor = [UIColor clearColor]; + [self.mediaImageView addSubview:self.mediaOverlayView]; +} + +-(void)initDimmingView +{ + self.dimmingView = [[UIView alloc] init]; + self.dimmingView.backgroundColor = [UIColor blackColor]; + self.dimmingView.alpha = 0.4f; + [self.mediaOverlayView addSubview:self.dimmingView]; +} + +-(void)initPlayButton +{ + self.playButtonImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"play_button.png"]]; + self.playButtonImageView.contentMode = UIViewContentModeScaleAspectFit; + self.playButtonImageView.clipsToBounds = YES; + self.playButtonImageView.backgroundColor = [UIColor clearColor]; + [self.mediaOverlayView addSubview:self.playButtonImageView]; +} + +-(void)layoutChatBalloon +{ + [super layoutChatBalloon]; + + CGRect frame = self.mediaOverlayView.frame; + frame.origin = CGPointZero; + frame.size = self.mediaImageView.frame.size; + self.mediaOverlayView.frame = frame; + + self.dimmingView.frame = self.mediaImageView.bounds; + + CGRect playFrame = self.playButtonImageView.frame; + playFrame.size = CGSizeMake(20, 20); + self.playButtonImageView.frame = playFrame; + self.playButtonImageView.center = CGPointMake(self.mediaOverlayView.frame.size.width/2 + self.contentInsets.left - self.contentInsets.right, self.mediaOverlayView.frame.size.height/2); +} + +@end