From 1d15e65c4a718cdc6424e39b1da2b3a46f5d1cbf Mon Sep 17 00:00:00 2001 From: ihla Date: Wed, 14 May 2014 10:43:33 +0200 Subject: [PATCH 1/3] =?UTF-8?q?-=20merged=20with=20pull=20request=20#50=20?= =?UTF-8?q?=E2=80=9CAdded=20spring=20to=20center=20date=20on=20orientation?= =?UTF-8?q?=20change=E2=80=9D=20-=20merged=20with=20pull=20request=20#65?= =?UTF-8?q?=20=E2=80=9CA=20few=20changes=20to=20improve=20compatibility?= =?UTF-8?q?=E2=80=9D=20-=20improved=20performance=20(partly)=20by=20image?= =?UTF-8?q?=20caching=20for=20static=20images,=20NSBubbleData=20images=20n?= =?UTF-8?q?ot=20cached=20yet!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/Default-568h@2x.png | Bin 0 -> 18594 bytes .../project.pbxproj | 37 ++++++-- .../UIBubbleTableViewExample/ViewController.m | 2 +- src/ImageCache.h | 24 +++++ src/ImageCache.m | 84 ++++++++++++++++++ src/NSBubbleData.m | 2 +- src/UIBubbleHeaderTableViewCell.m | 1 + src/UIBubbleTableView.m | 6 +- src/UIBubbleTableViewCell.m | 17 ++-- src/UIBubbleTableViewDataSource.h | 2 +- 10 files changed, 159 insertions(+), 16 deletions(-) create mode 100755 example/Default-568h@2x.png create mode 100644 src/ImageCache.h create mode 100644 src/ImageCache.m diff --git a/example/Default-568h@2x.png b/example/Default-568h@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..0891b7aabfcf3422423b109c8beed2bab838c607 GIT binary patch literal 18594 zcmeI4X;f257Jx&9fS`ixvS;&$x8J@slQFSel)6zJN=?13FB7H(lQjRkSy8x_-S~tvu2gzn1oS+dLcF#eqtq$ z%tf9TTvX?`)R@}3uBI;jzS-=ZR-Td&MHaS&;!0?Ni*#$#`n*~CcQK)Q9vAQ~TUpnI!j)a2biYK^R)M~A5wUDZhx?ULMX z3x1P&qt=trOY6P2U67L=m=U?F|5#Uj(eCueNTZaHs_ceWiHeET+j+tp3Jt9g(ekqP z2WOvfR{qV+9r+o4J5?qK>7;;^+I7tGv-i)es$X_D=EoKF+S?zsyj^oRFElP}c}JT< zd8SUs-?O?}2YD#ngKbnHgzHBcboxK_2r9l(?eNCl-pEzkJm}fY?WC*jnS?VBE4EpY zO$fEejz6fU;W2Kl>JeQBZBl-%Irg`obSlg*@4QB;Dd1H7^Oi5wvt4d{RZ!8Og?^aE z)k0$1g+V3fd(gdQ3d&q2q-FL*uy#}|bc^=VhFsl0jBgUGJ+-s3U8MK9A!YJJMxpci z5hJ%|{DwV48fZn0{n5l$N_KcSb#NKE4plB`9I6Zt=Z!~-zw0{9tg$L&Ju1F0X)Cy8 zKF;(&lJ>x)Jw(=;p~sF(Sd9VWGwFE2rnyS9!f^DZ8+aCLq zQ};>lcJ1GDLqjm6Hd>|Eabno@P`~Bn(~6^aD_#yoEH(a?Nm1S<;S+hSxI5d16^<1lEM3NPFi zkqPrpL)+ zgnseFikg`gJVBha1&7C4;O6>h=dt~`ND+;Zd?W(4v2JIb7Pt>Td42%M-Ju-XAH#Pns762L}K3 zDhvsRqN0Ni(1UrishD2YvV?4*h2iFj$+&N||Fn$4n|^NSU+o?~jq`0jVQt8T9l{7b zXiwwODFh2V!Q6sqP9S>WH$oOf$N~=d0-bqTlD61!=`&0eAP-F>XN?*|gtOXX{ zQVTWyYo4ZK0GAw!GHf|pz9`D;-bbb*5LBX*{bnz|+)$@&P9|ORM2o?95{;ejvo&r- zq8cBhTN6nn)7~W>54U)%-F_-b?YKdfk5I8MHcuzBD5)!;yv#Z&R&^y=@=>VTIMy#r zX&U<=BsPkdqcMe<_}2+>H%XKyrr5ZR8_KVe>ZqYN z^=^~TFD};;rHJ$U;{~w^hYojl4hRI@SH$^K{YEo=sg)WY87r!*7blQK&qnpDo0`Vn zkl)9u9g=mCh&ZCJS(L4yN3k0kQ zuvg$h2KEEk51T+O0JQ+r0`R>g{jvqM0Mr6d3qUOZwE!?PI7HY@CE|dr sfw?Q;rAv?G4&^^8-z_>&sWXMxvD*gPOU4CBe-*@OtE+wfmVJNyHv)PfH~;_u literal 0 HcmV?d00001 diff --git a/example/UIBubbleTableViewExample.xcodeproj/project.pbxproj b/example/UIBubbleTableViewExample.xcodeproj/project.pbxproj index 5aff5b7..8fc9811 100644 --- a/example/UIBubbleTableViewExample.xcodeproj/project.pbxproj +++ b/example/UIBubbleTableViewExample.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 20489A9519235BF9002CFE85 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 20489A9419235BF9002CFE85 /* Default-568h@2x.png */; }; + 20489A9819236065002CFE85 /* ImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 20489A9719236065002CFE85 /* ImageCache.m */; }; 76687AFD1622039C00707588 /* UIBubbleTypingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 76687AFC1622039C00707588 /* UIBubbleTypingTableViewCell.m */; }; 76687AFF162203AF00707588 /* halloween.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 76687AFE162203AF00707588 /* halloween.jpg */; }; 76D097CC1621F0E000C17816 /* UIBubbleHeaderTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 76D097CB1621F0E000C17816 /* UIBubbleHeaderTableViewCell.m */; }; @@ -37,6 +39,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 20489A9419235BF9002CFE85 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + 20489A9619236065002CFE85 /* ImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageCache.h; path = ../../src/ImageCache.h; sourceTree = ""; }; + 20489A9719236065002CFE85 /* ImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ImageCache.m; path = ../../src/ImageCache.m; sourceTree = ""; }; 76687AFB1622039C00707588 /* UIBubbleTypingTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UIBubbleTypingTableViewCell.h; path = ../../src/UIBubbleTypingTableViewCell.h; sourceTree = ""; }; 76687AFC1622039C00707588 /* UIBubbleTypingTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UIBubbleTypingTableViewCell.m; path = ../../src/UIBubbleTypingTableViewCell.m; sourceTree = ""; }; 76687AFE162203AF00707588 /* halloween.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = halloween.jpg; sourceTree = SOURCE_ROOT; }; @@ -94,6 +99,7 @@ 76ED205315BF096C00E186D3 = { isa = PBXGroup; children = ( + 20489A9419235BF9002CFE85 /* Default-568h@2x.png */, 76ED206815BF096C00E186D3 /* UIBubbleTableViewExample */, 76ED206115BF096C00E186D3 /* Frameworks */, 76ED205F15BF096C00E186D3 /* Products */, @@ -151,6 +157,8 @@ 76ED208315BF09AD00E186D3 /* src */ = { isa = PBXGroup; children = ( + 20489A9619236065002CFE85 /* ImageCache.h */, + 20489A9719236065002CFE85 /* ImageCache.m */, 76ED208915BF0BB100E186D3 /* NSBubbleData.h */, 76ED208A15BF0BB100E186D3 /* NSBubbleData.m */, 76ED208515BF09E300E186D3 /* UIBubbleTableView.h */, @@ -210,7 +218,7 @@ 76ED205515BF096C00E186D3 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0430; + LastUpgradeCheck = 0510; }; buildConfigurationList = 76ED205815BF096C00E186D3 /* Build configuration list for PBXProject "UIBubbleTableViewExample" */; compatibilityVersion = "Xcode 3.2"; @@ -242,6 +250,7 @@ C83B107A15DE43180067DADE /* bubbleMine@2x.png in Resources */, C83B107B15DE43180067DADE /* bubbleSomeone.png in Resources */, C83B107C15DE43180067DADE /* bubbleSomeone@2x.png in Resources */, + 20489A9519235BF9002CFE85 /* Default-568h@2x.png in Resources */, C83B107D15DE43180067DADE /* typingMine.png in Resources */, C83B107E15DE43180067DADE /* typingMine@2x.png in Resources */, C83B107F15DE43180067DADE /* typingSomeone.png in Resources */, @@ -267,6 +276,7 @@ 76ED208B15BF0BB100E186D3 /* NSBubbleData.m in Sources */, 76ED209415BF29EE00E186D3 /* UIBubbleTableViewCell.m in Sources */, 76D097CC1621F0E000C17816 /* UIBubbleHeaderTableViewCell.m in Sources */, + 20489A9819236065002CFE85 /* ImageCache.m in Sources */, 76687AFD1622039C00707588 /* UIBubbleTypingTableViewCell.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -297,7 +307,12 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -309,10 +324,14 @@ ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 5.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; @@ -321,15 +340,23 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 5.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/example/UIBubbleTableViewExample/ViewController.m b/example/UIBubbleTableViewExample/ViewController.m index 5516175..b3a2ff6 100644 --- a/example/UIBubbleTableViewExample/ViewController.m +++ b/example/UIBubbleTableViewExample/ViewController.m @@ -82,7 +82,7 @@ - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interface #pragma mark - UIBubbleTableViewDataSource implementation -- (NSInteger)rowsForBubbleTable:(UIBubbleTableView *)tableView +- (NSUInteger)rowsForBubbleTable:(UIBubbleTableView *)tableView { return [bubbleData count]; } diff --git a/src/ImageCache.h b/src/ImageCache.h new file mode 100644 index 0000000..36b1b04 --- /dev/null +++ b/src/ImageCache.h @@ -0,0 +1,24 @@ +// +// ImageCache.h +// InstrumentsTutorial +// +// Created by Matt Galloway on 06/09/2012. +// Copyright (c) 2012 Swipe Stack Ltd. All rights reserved. +// Modified by Lubos Ilcik on 14/05/2014. +// + +#import + +typedef void(^ImageCacheDownloadCompletionHandler)(UIImage *image); + +@interface ImageCache : NSObject + ++ (id)sharedInstance; + +- (UIImage*)imageForKey:(NSString*)key; +- (void)setImage:(UIImage*)image forKey:(NSString*)key; +- (void)downloadImageAtURL:(NSURL*)url completionHandler:(ImageCacheDownloadCompletionHandler)completion; +- (UIImage*)imageNamed:(NSString*)name; +- (UIImage *)imageNamed:(NSString*)name stretchableWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight; + +@end diff --git a/src/ImageCache.m b/src/ImageCache.m new file mode 100644 index 0000000..3385d92 --- /dev/null +++ b/src/ImageCache.m @@ -0,0 +1,84 @@ +// +// ImageCache.m +// InstrumentsTutorial +// +// Created by Matt Galloway on 06/09/2012. +// Copyright (c) 2012 Swipe Stack Ltd. All rights reserved. +// Modified by Lubos Ilcik on 14/05/2014. +// + +#import "ImageCache.h" + +@interface ImageCache () { + NSMutableDictionary *_cache; +} +@end + +@implementation ImageCache + ++ (id)sharedInstance { + static ImageCache *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[[self class] alloc] init]; + }); + return sharedInstance; +} + +- (id)init { + if ((self = [super init])) { + _cache = [NSMutableDictionary new]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(memoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + } + return self; +} + +- (UIImage*)imageForKey:(NSString*)key { + return [_cache objectForKey:key]; +} + +- (void)setImage:(UIImage*)image forKey:(NSString*)key { + [_cache setObject:image forKey:key]; +} + +- (void)downloadImageAtURL:(NSURL*)url completionHandler:(ImageCacheDownloadCompletionHandler)completion { + UIImage *cachedImage = [self imageForKey:[url absoluteString]]; + if (cachedImage) { + completion(cachedImage); + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSData *data = [NSData dataWithContentsOfURL:url]; + UIImage *image = [UIImage imageWithData:data]; + [self setImage:image forKey:[url absoluteString]]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(image); + }); + }); + } +} + +- (UIImage*)imageNamed:(NSString*)name { + UIImage *image; + if (!(image = [self imageForKey:name])) { + image = [UIImage imageNamed:name]; + [self setImage:image forKey:name]; + } + return image; +} + +- (UIImage *)imageNamed:(NSString*)name stretchableWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight { + UIImage *image; + if (!(image = [self imageForKey:name])) { + image = [[UIImage imageNamed:name] stretchableImageWithLeftCapWidth:leftCapWidth topCapHeight:topCapHeight]; + [self setImage:image forKey:name]; + } + return image; +} + +#pragma mark - Private + +- (void)memoryWarning:(NSNotification*)note { + [_cache removeAllObjects]; +} + +@end diff --git a/src/NSBubbleData.m b/src/NSBubbleData.m index 5a39821..be13efd 100644 --- a/src/NSBubbleData.m +++ b/src/NSBubbleData.m @@ -54,7 +54,7 @@ + (id)dataWithText:(NSString *)text date:(NSDate *)date type:(NSBubbleType)type - (id)initWithText:(NSString *)text date:(NSDate *)date type:(NSBubbleType)type { UIFont *font = [UIFont systemFontOfSize:[UIFont systemFontSize]]; - CGSize size = [(text ? text : @"") sizeWithFont:font constrainedToSize:CGSizeMake(220, 9999) lineBreakMode:NSLineBreakByWordWrapping]; + CGSize size = [(text ? text : @"") boundingRectWithSize: CGSizeMake(220, 9999) options: NSStringDrawingUsesLineFragmentOrigin attributes: @{ NSFontAttributeName:font }context: nil].size; UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)]; label.numberOfLines = 0; diff --git a/src/UIBubbleHeaderTableViewCell.m b/src/UIBubbleHeaderTableViewCell.m index c40243c..37c1080 100644 --- a/src/UIBubbleHeaderTableViewCell.m +++ b/src/UIBubbleHeaderTableViewCell.m @@ -49,6 +49,7 @@ - (void)setDate:(NSDate *)value self.label.shadowColor = [UIColor whiteColor]; self.label.textColor = [UIColor darkGrayColor]; self.label.backgroundColor = [UIColor clearColor]; + self.label.autoresizingMask = UIViewAutoresizingFlexibleWidth; [self addSubview:self.label]; } diff --git a/src/UIBubbleTableView.m b/src/UIBubbleTableView.m index 29e4ddb..4ea1f34 100644 --- a/src/UIBubbleTableView.m +++ b/src/UIBubbleTableView.m @@ -95,7 +95,7 @@ - (void)reloadData self.bubbleSection = nil; // Loading new data - int count = 0; + NSUInteger count = 0; #if !__has_feature(objc_arc) self.bubbleSection = [[[NSMutableArray alloc] init] autorelease]; #else @@ -156,7 +156,7 @@ - (void)reloadData - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - int result = [self.bubbleSection count]; + NSInteger result = [self.bubbleSection count]; if (self.typingBubble != NSBubbleTypingTypeNobody) result++; return result; } @@ -169,7 +169,7 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger return [[self.bubbleSection objectAtIndex:section] count] + 1; } -- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // Now typing if (indexPath.section >= [self.bubbleSection count]) diff --git a/src/UIBubbleTableViewCell.m b/src/UIBubbleTableViewCell.m index 3aeaa1d..4a8b728 100644 --- a/src/UIBubbleTableViewCell.m +++ b/src/UIBubbleTableViewCell.m @@ -11,6 +11,7 @@ #import #import "UIBubbleTableViewCell.h" #import "NSBubbleData.h" +#import "ImageCache.h" @interface UIBubbleTableViewCell () @@ -75,14 +76,20 @@ - (void) setupInternalData CGFloat x = (type == BubbleTypeSomeoneElse) ? 0 : self.frame.size.width - width - self.data.insets.left - self.data.insets.right; CGFloat y = 0; + ImageCache *cache = [ImageCache sharedInstance]; + UIImage *image; + // Adjusting the x coordinate for avatar if (self.showAvatar) { [self.avatarImage removeFromSuperview]; + + image = [cache imageNamed:@"missingAvatar.png"]; + #if !__has_feature(objc_arc) - self.avatarImage = [[[UIImageView alloc] initWithImage:(self.data.avatar ? self.data.avatar : [UIImage imageNamed:@"missingAvatar.png"])] autorelease]; + self.avatarImage = [[[UIImageView alloc] initWithImage:(self.data.avatar ? self.data.avatar : image)] autorelease]; #else - self.avatarImage = [[UIImageView alloc] initWithImage:(self.data.avatar ? self.data.avatar : [UIImage imageNamed:@"missingAvatar.png"])]; + self.avatarImage = [[UIImageView alloc] initWithImage:(self.data.avatar ? self.data.avatar : image)]; #endif self.avatarImage.layer.cornerRadius = 9.0; self.avatarImage.layer.masksToBounds = YES; @@ -109,12 +116,12 @@ - (void) setupInternalData if (type == BubbleTypeSomeoneElse) { - self.bubbleImage.image = [[UIImage imageNamed:@"bubbleSomeone.png"] stretchableImageWithLeftCapWidth:21 topCapHeight:14]; - + image = [cache imageNamed:@"bubbleSomeone.png" stretchableWithLeftCapWidth:21 topCapHeight:14]; } else { - self.bubbleImage.image = [[UIImage imageNamed:@"bubbleMine.png"] stretchableImageWithLeftCapWidth:15 topCapHeight:14]; + image = [cache imageNamed:@"bubbleMine.png" stretchableWithLeftCapWidth:21 topCapHeight:14]; } + self.bubbleImage.image = image; self.bubbleImage.frame = CGRectMake(x, y, width + self.data.insets.left + self.data.insets.right, height + self.data.insets.top + self.data.insets.bottom); } diff --git a/src/UIBubbleTableViewDataSource.h b/src/UIBubbleTableViewDataSource.h index 01194e7..7ee03af 100644 --- a/src/UIBubbleTableViewDataSource.h +++ b/src/UIBubbleTableViewDataSource.h @@ -18,7 +18,7 @@ @required -- (NSInteger)rowsForBubbleTable:(UIBubbleTableView *)tableView; +- (NSUInteger)rowsForBubbleTable:(UIBubbleTableView *)tableView; - (NSBubbleData *)bubbleTableView:(UIBubbleTableView *)tableView dataForRow:(NSInteger)row; @end From 931a9d9ed1b49b4749ed9106bea5b16ebe393b86 Mon Sep 17 00:00:00 2001 From: ihla Date: Thu, 15 May 2014 17:57:52 +0200 Subject: [PATCH 2/3] fix to register cell classes for reuse --- src/UIBubbleTableView.m | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/UIBubbleTableView.m b/src/UIBubbleTableView.m index 4ea1f34..0c13cff 100644 --- a/src/UIBubbleTableView.m +++ b/src/UIBubbleTableView.m @@ -13,6 +13,10 @@ #import "UIBubbleHeaderTableViewCell.h" #import "UIBubbleTypingTableViewCell.h" +static NSString * const tblBubbleTypingCell = @"tblBubbleTypingCell"; +static NSString * const tblBubbleHeaderCell = @"tblBubbleHeaderCell"; +static NSString * const tblBubbleCell = @"tblBubbleCell"; + @interface UIBubbleTableView () @property (nonatomic, retain) NSMutableArray *bubbleSection; @@ -44,6 +48,12 @@ - (void)initializator self.snapInterval = 120; self.typingBubble = NSBubbleTypingTypeNobody; + + // cell class must be registered otherwise dequeueReusableCellWithIdentifier: returns nill! + [self registerClass:[UIBubbleTableViewCell class] forCellReuseIdentifier:tblBubbleCell]; + [self registerClass:[UIBubbleHeaderTableViewCell class] forCellReuseIdentifier:tblBubbleHeaderCell]; + [self registerClass:[UIBubbleTypingTableViewCell class] forCellReuseIdentifier:tblBubbleTypingCell]; + } - (id)init @@ -192,8 +202,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // Now typing if (indexPath.section >= [self.bubbleSection count]) { - static NSString *cellId = @"tblBubbleTypingCell"; - UIBubbleTypingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; + UIBubbleTypingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tblBubbleTypingCell]; if (cell == nil) cell = [[UIBubbleTypingTableViewCell alloc] init]; @@ -206,8 +215,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // Header with date and time if (indexPath.row == 0) { - static NSString *cellId = @"tblBubbleHeaderCell"; - UIBubbleHeaderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; + UIBubbleHeaderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tblBubbleHeaderCell]; NSBubbleData *data = [[self.bubbleSection objectAtIndex:indexPath.section] objectAtIndex:0]; if (cell == nil) cell = [[UIBubbleHeaderTableViewCell alloc] init]; @@ -218,8 +226,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } // Standard bubble - static NSString *cellId = @"tblBubbleCell"; - UIBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; + UIBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tblBubbleCell]; NSBubbleData *data = [[self.bubbleSection objectAtIndex:indexPath.section] objectAtIndex:indexPath.row - 1]; if (cell == nil) cell = [[UIBubbleTableViewCell alloc] init]; From d25a0d776bfd4986fef3d55951c37fd67cfaa797 Mon Sep 17 00:00:00 2001 From: ihla Date: Sun, 18 May 2014 20:07:27 +0200 Subject: [PATCH 3/3] refactoring --- src/NSBubbleData.m | 9 +- src/UIBubbleTableView.m | 18 ++-- src/UIBubbleTableViewCell.m | 165 +++++++++++++++++++++--------------- 3 files changed, 115 insertions(+), 77 deletions(-) diff --git a/src/NSBubbleData.m b/src/NSBubbleData.m index be13efd..ca13b79 100644 --- a/src/NSBubbleData.m +++ b/src/NSBubbleData.m @@ -55,8 +55,13 @@ - (id)initWithText:(NSString *)text date:(NSDate *)date type:(NSBubbleType)type { UIFont *font = [UIFont systemFontOfSize:[UIFont systemFontSize]]; CGSize size = [(text ? text : @"") boundingRectWithSize: CGSizeMake(220, 9999) options: NSStringDrawingUsesLineFragmentOrigin attributes: @{ NSFontAttributeName:font }context: nil].size; - - UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)]; + /* + documention mentions + "In iOS 7 and later, this method returns fractional sizes (in the size component of the returned CGRect); to use a returned size to size views, you must use raise its value to the nearest higher integer using the ceil function." + */ + CGFloat height = ceilf(size.height); + CGFloat width = ceilf(size.width); + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, height)]; label.numberOfLines = 0; label.lineBreakMode = NSLineBreakByWordWrapping; label.text = (text ? text : @""); diff --git a/src/UIBubbleTableView.m b/src/UIBubbleTableView.m index 0c13cff..6fff706 100644 --- a/src/UIBubbleTableView.m +++ b/src/UIBubbleTableView.m @@ -13,9 +13,9 @@ #import "UIBubbleHeaderTableViewCell.h" #import "UIBubbleTypingTableViewCell.h" -static NSString * const tblBubbleTypingCell = @"tblBubbleTypingCell"; -static NSString * const tblBubbleHeaderCell = @"tblBubbleHeaderCell"; -static NSString * const tblBubbleCell = @"tblBubbleCell"; +static NSString * const kBubbleTypingCell = @"tblBubbleTypingCell"; +static NSString * const kBubbleHeaderCell = @"tblBubbleHeaderCell"; +static NSString * const kBubbleCell = @"tblBubbleCell"; @interface UIBubbleTableView () @@ -50,9 +50,9 @@ - (void)initializator self.typingBubble = NSBubbleTypingTypeNobody; // cell class must be registered otherwise dequeueReusableCellWithIdentifier: returns nill! - [self registerClass:[UIBubbleTableViewCell class] forCellReuseIdentifier:tblBubbleCell]; - [self registerClass:[UIBubbleHeaderTableViewCell class] forCellReuseIdentifier:tblBubbleHeaderCell]; - [self registerClass:[UIBubbleTypingTableViewCell class] forCellReuseIdentifier:tblBubbleTypingCell]; + [self registerClass:[UIBubbleTableViewCell class] forCellReuseIdentifier:kBubbleCell]; + [self registerClass:[UIBubbleHeaderTableViewCell class] forCellReuseIdentifier:kBubbleHeaderCell]; + [self registerClass:[UIBubbleTypingTableViewCell class] forCellReuseIdentifier:kBubbleTypingCell]; } @@ -202,7 +202,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // Now typing if (indexPath.section >= [self.bubbleSection count]) { - UIBubbleTypingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tblBubbleTypingCell]; + UIBubbleTypingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kBubbleTypingCell]; if (cell == nil) cell = [[UIBubbleTypingTableViewCell alloc] init]; @@ -215,7 +215,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // Header with date and time if (indexPath.row == 0) { - UIBubbleHeaderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tblBubbleHeaderCell]; + UIBubbleHeaderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kBubbleHeaderCell]; NSBubbleData *data = [[self.bubbleSection objectAtIndex:indexPath.section] objectAtIndex:0]; if (cell == nil) cell = [[UIBubbleHeaderTableViewCell alloc] init]; @@ -226,7 +226,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } // Standard bubble - UIBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tblBubbleCell]; + UIBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kBubbleCell]; NSBubbleData *data = [[self.bubbleSection objectAtIndex:indexPath.section] objectAtIndex:indexPath.row - 1]; if (cell == nil) cell = [[UIBubbleTableViewCell alloc] init]; diff --git a/src/UIBubbleTableViewCell.m b/src/UIBubbleTableViewCell.m index 4a8b728..271ccbb 100644 --- a/src/UIBubbleTableViewCell.m +++ b/src/UIBubbleTableViewCell.m @@ -13,13 +13,16 @@ #import "NSBubbleData.h" #import "ImageCache.h" +static NSInteger kCustomViewTag = 1234; + @interface UIBubbleTableViewCell () @property (nonatomic, retain) UIView *customView; @property (nonatomic, retain) UIImageView *bubbleImage; @property (nonatomic, retain) UIImageView *avatarImage; -- (void) setupInternalData; +- (void)adjustViewFrames; +- (void)createCustomViewHierarchy; @end @@ -31,76 +34,103 @@ @implementation UIBubbleTableViewCell @synthesize showAvatar = _showAvatar; @synthesize avatarImage = _avatarImage; -- (void)setFrame:(CGRect)frame -{ - [super setFrame:frame]; - [self setupInternalData]; -} +#pragma mark - Accessors +- (void)setData:(NSBubbleData *)data { #if !__has_feature(objc_arc) -- (void) dealloc -{ - self.data = nil; - self.customView = nil; - self.bubbleImage = nil; - self.avatarImage = nil; - [super dealloc]; -} + [data retain]; + [_data release]; + _data = data; +#else + _data = data; #endif -- (void)setDataInternal:(NSBubbleData *)value -{ - self.data = value; - [self setupInternalData]; + // set content from data + ImageCache *cache = [ImageCache sharedInstance]; + self.avatarImage.image = data.avatar ? data.avatar : [cache imageNamed:@"missingAvatar.png"]; + + UIImage *image; + if (data.type == BubbleTypeSomeoneElse) { + image = [cache imageNamed:@"bubbleSomeone.png" stretchableWithLeftCapWidth:21 topCapHeight:14]; + } + else { + image = [cache imageNamed:@"bubbleMine.png" stretchableWithLeftCapWidth:21 topCapHeight:14]; + } + self.bubbleImage.image = image; + + // update content view + [[self.contentView viewWithTag:kCustomViewTag] removeFromSuperview]; + self.customView = data.view; + self.customView.tag = kCustomViewTag; + [self.contentView addSubview:self.customView]; } -- (void) setupInternalData -{ - self.selectionStyle = UITableViewCellSelectionStyleNone; - - if (!self.bubbleImage) - { -#if !__has_feature(objc_arc) - self.bubbleImage = [[[UIImageView alloc] init] autorelease]; -#else - self.bubbleImage = [[UIImageView alloc] init]; -#endif - [self addSubview:self.bubbleImage]; +- (UIView *)customView { + if (!_customView) { + _customView = [[UIView alloc] initWithFrame:CGRectZero]; + _customView.tag = kCustomViewTag; } + return _customView; +} + +- (UIImageView *)bubbleImage { + if (!_bubbleImage) { + _bubbleImage = [[UIImageView alloc] initWithFrame:CGRectZero]; + } + return _bubbleImage; +} + +- (UIImageView *)avatarImage { + if (!_avatarImage) { + _avatarImage = [[UIImageView alloc] initWithFrame:CGRectZero]; + _avatarImage.layer.cornerRadius = 9.0; + _avatarImage.layer.masksToBounds = YES; + _avatarImage.layer.borderColor = [UIColor colorWithWhite:0.0 alpha:0.2].CGColor; + _avatarImage.layer.borderWidth = 1.0; + } + return _avatarImage; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + if (self = [super initWithCoder:aDecoder]) { + NSLog(@"%s not implemented!", __PRETTY_FUNCTION__); + //TODO ... + } + return self; +} +- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { + [self createCustomViewHierarchy]; + } + return self; +} +- (void)createCustomViewHierarchy { + self.selectionStyle = UITableViewCellSelectionStyleNone; - NSBubbleType type = self.data.type; - + // create custom view hierarchy with no content + [self.contentView addSubview:self.bubbleImage]; + [self.contentView addSubview:self.avatarImage]; + [self.contentView addSubview:self.customView]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self adjustViewFrames]; +} +- (void)adjustViewFrames { CGFloat width = self.data.view.frame.size.width; CGFloat height = self.data.view.frame.size.height; - + + NSBubbleType type = self.data.type; CGFloat x = (type == BubbleTypeSomeoneElse) ? 0 : self.frame.size.width - width - self.data.insets.left - self.data.insets.right; CGFloat y = 0; - ImageCache *cache = [ImageCache sharedInstance]; - UIImage *image; - // Adjusting the x coordinate for avatar - if (self.showAvatar) - { - [self.avatarImage removeFromSuperview]; - - image = [cache imageNamed:@"missingAvatar.png"]; - -#if !__has_feature(objc_arc) - self.avatarImage = [[[UIImageView alloc] initWithImage:(self.data.avatar ? self.data.avatar : image)] autorelease]; -#else - self.avatarImage = [[UIImageView alloc] initWithImage:(self.data.avatar ? self.data.avatar : image)]; -#endif - self.avatarImage.layer.cornerRadius = 9.0; - self.avatarImage.layer.masksToBounds = YES; - self.avatarImage.layer.borderColor = [UIColor colorWithWhite:0.0 alpha:0.2].CGColor; - self.avatarImage.layer.borderWidth = 1.0; - + if (self.showAvatar) { CGFloat avatarX = (type == BubbleTypeSomeoneElse) ? 2 : self.frame.size.width - 52; CGFloat avatarY = self.frame.size.height - 50; self.avatarImage.frame = CGRectMake(avatarX, avatarY, 50, 50); - [self addSubview:self.avatarImage]; CGFloat delta = self.frame.size.height - (self.data.insets.top + self.data.insets.bottom + self.data.view.frame.size.height); if (delta > 0) y = delta; @@ -108,22 +138,25 @@ - (void) setupInternalData if (type == BubbleTypeSomeoneElse) x += 54; if (type == BubbleTypeMine) x -= 54; } - - [self.customView removeFromSuperview]; - self.customView = self.data.view; + self.customView.frame = CGRectMake(x + self.data.insets.left, y + self.data.insets.top, width, height); - [self.contentView addSubview:self.customView]; - - if (type == BubbleTypeSomeoneElse) - { - image = [cache imageNamed:@"bubbleSomeone.png" stretchableWithLeftCapWidth:21 topCapHeight:14]; - } - else { - image = [cache imageNamed:@"bubbleMine.png" stretchableWithLeftCapWidth:21 topCapHeight:14]; - } - self.bubbleImage.image = image; self.bubbleImage.frame = CGRectMake(x, y, width + self.data.insets.left + self.data.insets.right, height + self.data.insets.top + self.data.insets.bottom); } +#if !__has_feature(objc_arc) +- (void) dealloc +{ + [_data release]; + [_customView release]; + [_bubbleImage release]; + [_avatarImage release]; + _data = nil; + _customView = nil; + _bubbleImage = nil; + _avatarImage = nil; + [super dealloc]; +} +#endif + @end