Skip to content
This repository has been archived by the owner on Aug 14, 2019. It is now read-only.

Commit

Permalink
Merge pull request #535 from walsh2000/develop
Browse files Browse the repository at this point in the history
Refactor JSQMessageBubbleImageFactory to allow custom images
  • Loading branch information
jessesquires committed Oct 10, 2014
2 parents a5ad7cf + b696ea4 commit a03344b
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 26 deletions.
7 changes: 4 additions & 3 deletions JSQMessagesDemo/DemoModelData.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@ - (instancetype)init
* Be sure to create your bubble images one time and reuse them for good performance.
*
*/
self.outgoingBubbleImageData = [JSQMessagesBubbleImageFactory outgoingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]];

self.incomingBubbleImageData = [JSQMessagesBubbleImageFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleGreenColor]];
JSQMessagesBubbleImageFactory *factory = [[JSQMessagesBubbleImageFactory alloc] init];
self.outgoingBubbleImageData = [factory outgoingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]];

self.incomingBubbleImageData = [factory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleGreenColor]];

}

Expand Down
57 changes: 54 additions & 3 deletions JSQMessagesTests/FactoryTests/JSQMessagesBubbleImageFactoryTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ - (void)testOutgoingMessageBubbleImageView
CGPoint center = CGPointMake(bubble.size.width / 2.0f, bubble.size.height / 2.0f);
UIEdgeInsets capInsets = UIEdgeInsetsMake(center.y, center.x, center.y, center.x);

JSQMessagesBubbleImage *bubbleImage = [JSQMessagesBubbleImageFactory outgoingMessagesBubbleImageWithColor:[UIColor lightGrayColor]];
JSQMessagesBubbleImageFactory *factory = [[JSQMessagesBubbleImageFactory alloc] init];
JSQMessagesBubbleImage *bubbleImage = [factory outgoingMessagesBubbleImageWithColor:[UIColor lightGrayColor]];
XCTAssertNotNil(bubbleImage, @"Bubble image should not be nil");

XCTAssertNotNil(bubbleImage.messageBubbleImage, "Image should not be nil");
Expand All @@ -64,7 +64,8 @@ - (void)testIncomingMessageBubbleImageView
UIEdgeInsets capInsets = UIEdgeInsetsMake(center.y, center.x, center.y, center.x);


JSQMessagesBubbleImage *bubbleImage = [JSQMessagesBubbleImageFactory incomingMessagesBubbleImageWithColor:[UIColor lightGrayColor]];
JSQMessagesBubbleImageFactory *factory = [[JSQMessagesBubbleImageFactory alloc] init];
JSQMessagesBubbleImage *bubbleImage = [factory incomingMessagesBubbleImageWithColor:[UIColor lightGrayColor]];
XCTAssertNotNil(bubbleImage, @"Bubble image should not be nil");

XCTAssertNotNil(bubbleImage.messageBubbleImage, "Image should not be nil");
Expand All @@ -81,4 +82,54 @@ - (void)testIncomingMessageBubbleImageView
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(bubbleImage.messageBubbleHighlightedImage.capInsets, capInsets), @"HighlightedImage capInsets should be equal to capInsets");
}


- (void)testCustomOutgoingMessageBubbleImageView
{
UIImage *bubble = [UIImage imageNamed:@"clip"];
XCTAssertNotNil(bubble, @"Bubble image should not be nil");

UIEdgeInsets capInsets = UIEdgeInsetsMake(1, 1, 1, 1);
JSQMessagesBubbleImageFactory *factory = [[JSQMessagesBubbleImageFactory alloc] initWithBubbleImage:bubble capInsets:capInsets];
JSQMessagesBubbleImage *bubbleImage = [factory outgoingMessagesBubbleImageWithColor:[UIColor lightGrayColor]];
XCTAssertNotNil(bubbleImage, @"Bubble image should not be nil");

XCTAssertNotNil(bubbleImage.messageBubbleImage, "Image should not be nil");
XCTAssertEqual(bubbleImage.messageBubbleImage.scale, bubble.scale, @"Image scale should equal bubble image scale");
XCTAssertEqual(bubbleImage.messageBubbleImage.imageOrientation, bubble.imageOrientation, @"Image orientation should equal bubble image orientation");
XCTAssertTrue(bubbleImage.messageBubbleImage.resizingMode == UIImageResizingModeStretch, @"Image should be stretchable");
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(bubbleImage.messageBubbleImage.capInsets, capInsets), @"Image capInsets should be equal to capInsets");

XCTAssertNotNil(bubbleImage.messageBubbleHighlightedImage, @"Highlighted image should not be nil");
XCTAssertEqual(bubbleImage.messageBubbleHighlightedImage.scale, bubble.scale, @"HighlightedImage scale should equal bubble image scale");
XCTAssertEqual(bubbleImage.messageBubbleHighlightedImage.imageOrientation, bubble.imageOrientation, @"HighlightedImage orientation should equal bubble image orientation");
XCTAssertTrue(bubbleImage.messageBubbleHighlightedImage.resizingMode == UIImageResizingModeStretch, @"HighlightedImage should be stretchable");
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(bubbleImage.messageBubbleHighlightedImage.capInsets, capInsets), @"HighlightedImage capInsets should be equal to capInsets");
}


- (void)testCustomIncomingMessageBubbleImageView
{
UIImage *bubble = [UIImage imageNamed:@"clip"];
XCTAssertNotNil(bubble, @"Bubble image should not be nil");

UIEdgeInsets capInsets = UIEdgeInsetsMake(1, 1, 1, 1);
JSQMessagesBubbleImageFactory *factory = [[JSQMessagesBubbleImageFactory alloc] initWithBubbleImage:bubble capInsets:capInsets];
JSQMessagesBubbleImage *bubbleImage = [factory incomingMessagesBubbleImageWithColor:[UIColor lightGrayColor]];
XCTAssertNotNil(bubbleImage, @"Bubble image should not be nil");

XCTAssertNotNil(bubbleImage.messageBubbleImage, "Image should not be nil");
XCTAssertEqual(bubbleImage.messageBubbleImage.scale, bubble.scale, @"Image scale should equal bubble image scale");
XCTAssertEqual(bubbleImage.messageBubbleImage.imageOrientation, UIImageOrientationUpMirrored, @"Image orientation should be flipped");
XCTAssertTrue(bubbleImage.messageBubbleImage.resizingMode == UIImageResizingModeStretch, @"Image should be stretchable");
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(bubbleImage.messageBubbleImage.capInsets, capInsets), @"Image capInsets should be equal to capInsets");

XCTAssertNotNil(bubbleImage.messageBubbleHighlightedImage, @"Highlighted image should not be nil");
XCTAssertEqual(bubbleImage.messageBubbleHighlightedImage.scale, bubble.scale, @"HighlightedImage scale should equal bubble image scale");
XCTAssertEqual(bubbleImage.messageBubbleHighlightedImage.imageOrientation, UIImageOrientationUpMirrored, @"Image orientation should be flipped");
XCTAssertTrue(bubbleImage.messageBubbleHighlightedImage.resizingMode == UIImageResizingModeStretch, @"HighlightedImage should be stretchable");
XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(bubbleImage.messageBubbleHighlightedImage.capInsets, capInsets), @"HighlightedImage capInsets should be equal to capInsets");
}



@end
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@
*/
@interface JSQMessagesBubbleImageFactory : NSObject

/**
* Creates an instance of JSQMessagesBubbleImageFactory with the default images.
*
* @return An initialized `JSQMessagesBubbleImageFactory` object if created successfully, `nil` otherwise.
*/
- (instancetype)init;

/**
* Creates an instance of JSQMessagesBubbleImageFactory.
*
* @param image A template image for all bubble images generated by this factory. The image should represent the outgoing shape, and will be flipped horizontally to make the incoming shape. This value must not be `nil`.
*
* @param capInsets The UIEdgeInsets which define the unstretchable regions of the image.
*
* @return An initialized `JSQMessagesBubbleImageFactory` object if created successfully, `nil` otherwise.
*/
- (instancetype)initWithBubbleImage:(UIImage *)image capInsets:(UIEdgeInsets)capInsets;


/**
* Creates and returns a `JSQMessagesBubbleImage` object with the specified color for *outgoing* message image bubbles.
* The `messageBubbleImage` property of the `JSQMessagesBubbleImage` is configured with a flat bubble image, masked to the given color.
Expand All @@ -37,7 +56,7 @@
*
* @return An initialized `JSQMessagesBubbleImage` object if created successfully, `nil` otherwise.
*/
+ (JSQMessagesBubbleImage *)outgoingMessagesBubbleImageWithColor:(UIColor *)color;
- (JSQMessagesBubbleImage *)outgoingMessagesBubbleImageWithColor:(UIColor *)color;

/**
* Creates and returns a `JSQMessagesBubbleImage` object with the specified color for *incoming* message image bubbles.
Expand All @@ -48,6 +67,6 @@
*
* @return An initialized `JSQMessagesBubbleImage` object if created successfully, `nil` otherwise.
*/
+ (JSQMessagesBubbleImage *)incomingMessagesBubbleImageWithColor:(UIColor *)color;
- (JSQMessagesBubbleImage *)incomingMessagesBubbleImageWithColor:(UIColor *)color;

@end
52 changes: 36 additions & 16 deletions JSQMessagesViewController/Factories/JSQMessagesBubbleImageFactory.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@

@interface JSQMessagesBubbleImageFactory ()

+ (JSQMessagesBubbleImage *)messagesBubbleImageWithColor:(UIColor *)color flippedForIncoming:(BOOL)flippedForIncoming;
@property (nonatomic, readwrite, strong) UIImage *bubbleImage;
@property (nonatomic, readwrite, assign) UIEdgeInsets capInsets;

- (JSQMessagesBubbleImage *)messagesBubbleImageWithColor:(UIColor *)color flippedForIncoming:(BOOL)flippedForIncoming;

+ (UIImage *)jsq_horizontallyFlippedImageFromImage:(UIImage *)image;

Expand All @@ -36,40 +39,57 @@ + (UIImage *)jsq_stretchableImageFromImage:(UIImage *)image withCapInsets:(UIEdg

@implementation JSQMessagesBubbleImageFactory

@synthesize bubbleImage;
@synthesize capInsets;


#pragma mark - Public

+ (JSQMessagesBubbleImage *)outgoingMessagesBubbleImageWithColor:(UIColor *)color
- (instancetype)initWithBubbleImage:(UIImage *)image capInsets:(UIEdgeInsets)insets {
NSParameterAssert(image != nil);
self = [super init];
if (self) {
self.bubbleImage = image;
self.capInsets = insets;
}
return self;
}

- (instancetype)init {
UIImage *bubble = [UIImage imageNamed:@"bubble_min"];
NSAssert(bubble != nil, @"Unable to load default image in %s. Please make sure default resources are included in your project.",
__PRETTY_FUNCTION__);
CGPoint center = CGPointMake(bubble.size.width / 2.0f, bubble.size.height / 2.0f);
UIEdgeInsets insets = UIEdgeInsetsMake(center.y, center.x, center.y, center.x);
return [self initWithBubbleImage:bubble capInsets:insets];
}

- (JSQMessagesBubbleImage *)outgoingMessagesBubbleImageWithColor:(UIColor *)color
{
NSParameterAssert(color != nil);
return [JSQMessagesBubbleImageFactory messagesBubbleImageWithColor:color flippedForIncoming:NO];
return [self messagesBubbleImageWithColor:color flippedForIncoming:NO];
}

+ (JSQMessagesBubbleImage *)incomingMessagesBubbleImageWithColor:(UIColor *)color
- (JSQMessagesBubbleImage *)incomingMessagesBubbleImageWithColor:(UIColor *)color
{
NSParameterAssert(color != nil);
return [JSQMessagesBubbleImageFactory messagesBubbleImageWithColor:color flippedForIncoming:YES];
return [self messagesBubbleImageWithColor:color flippedForIncoming:YES];
}

#pragma mark - Private

+ (JSQMessagesBubbleImage *)messagesBubbleImageWithColor:(UIColor *)color flippedForIncoming:(BOOL)flippedForIncoming
- (JSQMessagesBubbleImage *)messagesBubbleImageWithColor:(UIColor *)color flippedForIncoming:(BOOL)flippedForIncoming
{
UIImage *bubble = [UIImage imageNamed:@"bubble_min"];

UIImage *normalBubble = [bubble jsq_imageMaskedWithColor:color];
UIImage *highlightedBubble = [bubble jsq_imageMaskedWithColor:[color jsq_colorByDarkeningColorWithValue:0.12f]];
UIImage *normalBubble = [self.bubbleImage jsq_imageMaskedWithColor:color];
UIImage *highlightedBubble = [self.bubbleImage jsq_imageMaskedWithColor:[color jsq_colorByDarkeningColorWithValue:0.12f]];

if (flippedForIncoming) {
normalBubble = [JSQMessagesBubbleImageFactory jsq_horizontallyFlippedImageFromImage:normalBubble];
highlightedBubble = [JSQMessagesBubbleImageFactory jsq_horizontallyFlippedImageFromImage:highlightedBubble];
}

// make image stretchable from center point
CGPoint center = CGPointMake(bubble.size.width / 2.0f, bubble.size.height / 2.0f);
UIEdgeInsets capInsets = UIEdgeInsetsMake(center.y, center.x, center.y, center.x);

normalBubble = [JSQMessagesBubbleImageFactory jsq_stretchableImageFromImage:normalBubble withCapInsets:capInsets];
highlightedBubble = [JSQMessagesBubbleImageFactory jsq_stretchableImageFromImage:highlightedBubble withCapInsets:capInsets];
normalBubble = [JSQMessagesBubbleImageFactory jsq_stretchableImageFromImage:normalBubble withCapInsets:self.capInsets];
highlightedBubble = [JSQMessagesBubbleImageFactory jsq_stretchableImageFromImage:highlightedBubble withCapInsets:self.capInsets];

return [[JSQMessagesBubbleImage alloc] initWithMessageBubbleImage:normalBubble highlightedImage:highlightedBubble];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ - (void)configureWithEllipsisColor:(UIColor *)ellipsisColor
CGFloat bubbleMarginMinimumSpacing = 6.0f;
CGFloat indicatorMarginMinimumSpacing = 26.0f;

JSQMessagesBubbleImageFactory *bubbleImageFactory = [[JSQMessagesBubbleImageFactory alloc] init];
if (shouldDisplayOnLeft) {
self.bubbleImageView.image = [JSQMessagesBubbleImageFactory incomingMessagesBubbleImageWithColor:messageBubbleColor].messageBubbleImage;
self.bubbleImageView.image = [bubbleImageFactory incomingMessagesBubbleImageWithColor:messageBubbleColor].messageBubbleImage;

CGFloat collectionViewWidth = CGRectGetWidth(collectionView.frame);
CGFloat bubbleWidth = CGRectGetWidth(self.bubbleImageView.frame);
Expand All @@ -105,7 +106,7 @@ - (void)configureWithEllipsisColor:(UIColor *)ellipsisColor
self.typingIndicatorImageViewRightHorizontalConstraint.constant = indicatorMarginMaximumSpacing;
}
else {
self.bubbleImageView.image = [JSQMessagesBubbleImageFactory outgoingMessagesBubbleImageWithColor:messageBubbleColor].messageBubbleImage;
self.bubbleImageView.image = [bubbleImageFactory outgoingMessagesBubbleImageWithColor:messageBubbleColor].messageBubbleImage;

self.bubbleImageViewRightHorizontalConstraint.constant = bubbleMarginMinimumSpacing;
self.typingIndicatorImageViewRightHorizontalConstraint.constant = indicatorMarginMinimumSpacing;
Expand Down

0 comments on commit a03344b

Please sign in to comment.