Bubble collapse.

This commit is contained in:
Matthew Chen 2018-03-27 13:25:02 -04:00
parent 75177ef00f
commit 6525ccdb05
1 changed files with 78 additions and 283 deletions

View File

@ -50,7 +50,7 @@ static const CGFloat kBubbleTextVInset = 6.f;
//@property (nonatomic) BOOL isOutgoing;
@property (nonatomic) CAShapeLayer *shapeLayer;
@property (nonatomic) UIColor *bubbleColor;
@property (nonatomic, nullable) UIColor *bubbleColor;
@end
@ -90,7 +90,7 @@ static const CGFloat kBubbleTextVInset = 6.f;
}
}
- (void)setBubbleColor:(UIColor *)bubbleColor
- (void)setBubbleColor:(nullable UIColor *)bubbleColor
{
_bubbleColor = bubbleColor;
@ -121,97 +121,6 @@ static const CGFloat kBubbleTextVInset = 6.f;
self.maskLayer.path = bezierPath.CGPath;
}
//- (void)updateMask
//{
// UIView *_Nullable maskedSubview = self.maskedSubview;
// if (!maskedSubview) {
// return;
// }
// maskedSubview.frame = self.bounds;
// //<<<<<<< HEAD
// // // The JSQ masks are not RTL-safe, so we need to invert the
// // // mask orientation manually.
// // BOOL hasOutgoingMask = self.isOutgoing ^ self.isRTL;
// //
// // // Since the caption has it's own tail, the media bubble just above
// // // it looks better without a tail.
// // if (self.hideTail) {
// // if (hasOutgoingMask) {
// // self.layoutMargins = UIEdgeInsetsMake(0, 0, 2, 8);
// // } else {
// // self.layoutMargins = UIEdgeInsetsMake(0, 8, 2, 0);
// // }
// // maskedSubview.clipsToBounds = YES;
// //
// // // I arrived at this cornerRadius by superimposing the generated corner
// // // over that generated from the JSQMessagesMediaViewBubbleImageMasker
// // maskedSubview.layer.cornerRadius = 17;
// // } else {
// // [JSQMessagesMediaViewBubbleImageMasker applyBubbleImageMaskToMediaView:maskedSubview
// // isOutgoing:hasOutgoingMask];
// // }
// //||||||| merged common ancestors
// // // The JSQ masks are not RTL-safe, so we need to invert the
// // // mask orientation manually.
// // BOOL hasOutgoingMask = self.isOutgoing ^ self.isRTL;
// // [JSQMessagesMediaViewBubbleImageMasker applyBubbleImageMaskToMediaView:maskedSubview
// isOutgoing:hasOutgoingMask];
// //=======
//
//
// UIBezierPath *bezierPath = [BubbleFillView maskPathForSize:self.bounds.size
// isOutgoing:self.isOutgoing
// isRTL:self.isRTL];
// self.maskLayer.path = bezierPath.CGPath;
// maskedSubview.layer.mask = self.maskLayer;
// //>>>>>>> SQUASHED
//}
//- (void)setIsOutgoing:(BOOL)isOutgoing {
// if (_isOutgoing == isOutgoing) {
// return;
// }
// _isOutgoing = isOutgoing;
// [self updateMask];
//}
//
//- (void)setFrame:(CGRect)frame
//{
// BOOL didSizeChange = !CGSizeEqualToSize(self.frame.size, frame.size);
//
// [super setFrame:frame];
//
// if (didSizeChange || !self.shapeLayer) {
// [self updateMask];
// }
//}
//
//- (void)setBounds:(CGRect)bounds
//{
// BOOL didSizeChange = !CGSizeEqualToSize(self.bounds.size, bounds.size);
//
// [super setBounds:bounds];
//
// if (didSizeChange || !self.shapeLayer) {
// [self updateMask];
// }
//}
//
//- (void)updateMask
//{
// if (!self.shapeLayer) {
// self.shapeLayer = [CAShapeLayer new];
// [self.layer addSublayer:self.shapeLayer];
// }
//
// UIBezierPath *bezierPath = [self.class maskPathForSize:self.bounds.size
// isOutgoing:self.isOutgoing
// isRTL:self.isRTL];
//
// self.shapeLayer.fillColor = self.bubbleColor.CGColor;
// self.shapeLayer.path = bezierPath.CGPath;
//}
+ (UIBezierPath *)maskPathForSize:(CGSize)size
isOutgoing:(BOOL)isOutgoing
isRTL:(BOOL)isRTL
@ -311,187 +220,10 @@ static const CGFloat kBubbleTextVInset = 6.f;
return bezierPath;
}
//- (void)setBubbleColor:(UIColor *)bubbleColor {
// _bubbleColor = bubbleColor;
//
// self.shapeLayer.fillColor = bubbleColor.CGColor;
//}
@end
#pragma mark -
//@interface BubbleFillView : UIView
//
////@property (nonatomic) BOOL isOutgoing;
////@property (nonatomic) CAShapeLayer *shapeLayer;
////@property (nonatomic) UIColor *bubbleColor;
//
//@end
//
//#pragma mark -
//
//@implementation BubbleFillView
//
////- (void)setIsOutgoing:(BOOL)isOutgoing {
//// if (_isOutgoing == isOutgoing) {
//// return;
//// }
//// _isOutgoing = isOutgoing;
//// [self updateMask];
////}
////
////- (void)setFrame:(CGRect)frame
////{
//// BOOL didSizeChange = !CGSizeEqualToSize(self.frame.size, frame.size);
////
//// [super setFrame:frame];
////
//// if (didSizeChange || !self.shapeLayer) {
//// [self updateMask];
//// }
////}
////
////- (void)setBounds:(CGRect)bounds
////{
//// BOOL didSizeChange = !CGSizeEqualToSize(self.bounds.size, bounds.size);
////
//// [super setBounds:bounds];
////
//// if (didSizeChange || !self.shapeLayer) {
//// [self updateMask];
//// }
////}
////
////- (void)updateMask
////{
//// if (!self.shapeLayer) {
//// self.shapeLayer = [CAShapeLayer new];
//// [self.layer addSublayer:self.shapeLayer];
//// }
////
//// UIBezierPath *bezierPath = [self.class maskPathForSize:self.bounds.size
//// isOutgoing:self.isOutgoing
//// isRTL:self.isRTL];
////
//// self.shapeLayer.fillColor = self.bubbleColor.CGColor;
//// self.shapeLayer.path = bezierPath.CGPath;
////}
//
////- (void)setBubbleColor:(UIColor *)bubbleColor {
//// _bubbleColor = bubbleColor;
////
//// self.shapeLayer.fillColor = bubbleColor.CGColor;
////}
//
//@end
//
//#pragma mark -
//
//@interface BubbleMaskingView : UIView
//
//@property (nonatomic) BOOL isOutgoing;
//@property (nonatomic) BOOL hideTail;
//@property (nonatomic, nullable, weak) UIView *maskedSubview;
//@property (nonatomic) CAShapeLayer *maskLayer;
//
//@end
//
//#pragma mark -
//
//@implementation BubbleMaskingView
//
//- (void)setMaskedSubview:(UIView * _Nullable)maskedSubview {
// if (_maskedSubview == maskedSubview) {
// return;
// }
// _maskedSubview = maskedSubview;
// [self updateMask];
//}
//
//- (void)setIsOutgoing:(BOOL)isOutgoing {
// if (_isOutgoing == isOutgoing) {
// return;
// }
// _isOutgoing = isOutgoing;
// [self updateMask];
//}
//
//- (void)setFrame:(CGRect)frame
//{
// BOOL didSizeChange = !CGSizeEqualToSize(self.frame.size, frame.size);
//
// [super setFrame:frame];
//
// if (didSizeChange) {
// [self updateMask];
// }
//}
//
//- (void)setBounds:(CGRect)bounds
//{
// BOOL didSizeChange = !CGSizeEqualToSize(self.bounds.size, bounds.size);
//
// [super setBounds:bounds];
//
// if (didSizeChange) {
// [self updateMask];
// }
//}
//
//- (void)updateMask
//{
// UIView *_Nullable maskedSubview = self.maskedSubview;
// if (!maskedSubview) {
// return;
// }
// maskedSubview.frame = self.bounds;
// //<<<<<<< HEAD
// // // The JSQ masks are not RTL-safe, so we need to invert the
// // // mask orientation manually.
// // BOOL hasOutgoingMask = self.isOutgoing ^ self.isRTL;
// //
// // // Since the caption has it's own tail, the media bubble just above
// // // it looks better without a tail.
// // if (self.hideTail) {
// // if (hasOutgoingMask) {
// // self.layoutMargins = UIEdgeInsetsMake(0, 0, 2, 8);
// // } else {
// // self.layoutMargins = UIEdgeInsetsMake(0, 8, 2, 0);
// // }
// // maskedSubview.clipsToBounds = YES;
// //
// // // I arrived at this cornerRadius by superimposing the generated corner
// // // over that generated from the JSQMessagesMediaViewBubbleImageMasker
// // maskedSubview.layer.cornerRadius = 17;
// // } else {
// // [JSQMessagesMediaViewBubbleImageMasker applyBubbleImageMaskToMediaView:maskedSubview
// // isOutgoing:hasOutgoingMask];
// // }
// //||||||| merged common ancestors
// // // The JSQ masks are not RTL-safe, so we need to invert the
// // // mask orientation manually.
// // BOOL hasOutgoingMask = self.isOutgoing ^ self.isRTL;
// // [JSQMessagesMediaViewBubbleImageMasker applyBubbleImageMaskToMediaView:maskedSubview
// isOutgoing:hasOutgoingMask];
// //=======
//
// if (!self.maskLayer) {
// self.maskLayer = [CAShapeLayer new];
// }
//
// UIBezierPath *bezierPath = [OWSBubbleView maskPathForSize:self.bounds.size
// isOutgoing:self.isOutgoing
// isRTL:self.isRTL];
// self.maskLayer.path = bezierPath.CGPath;
// maskedSubview.layer.mask = self.maskLayer;
// //>>>>>>> SQUASHED
//}
//
//@end
#pragma mark -
@interface OWSMessageTextView : UITextView
@property (nonatomic) BOOL shouldIgnoreEvents;
@ -602,7 +334,7 @@ static const CGFloat kBubbleTextVInset = 6.f;
@property (nonatomic) UILabel *footerLabel;
@property (nonatomic, nullable) OWSExpirationTimerView *expirationTimerView;
@property (nonatomic, nullable) UIImageView *lastImageView;
@property (nonatomic, nullable) UIView *lastBodyMediaView;
// Should lazy-load expensive view contents (images, etc.).
// Should do nothing if view is already loaded.
@ -743,6 +475,22 @@ static const CGFloat kBubbleTextVInset = 6.f;
[self.footerView autoPinEdgeToSuperviewEdge:ALEdgeBottom];
[self.footerView autoPinWidthToSuperview];
self.bubbleView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[self.bubbleView addGestureRecognizer:tap];
UILongPressGestureRecognizer *longPress =
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];
[self.bubbleView addGestureRecognizer:longPress];
PanDirectionGestureRecognizer *panGesture = [[PanDirectionGestureRecognizer alloc]
initWithDirection:(self.isRTL ? PanDirectionLeft : PanDirectionRight)target:self
action:@selector(handlePanGesture:)];
[self addGestureRecognizer:panGesture];
// UITapGestureRecognizer *mediaTap =
// [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMediaTapGesture:)];
// [self.mediaMaskingView addGestureRecognizer:mediaTap];
@ -972,7 +720,8 @@ static const CGFloat kBubbleTextVInset = 6.f;
self.bubbleView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message];
// self.bubbleFillView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message];
} else {
OWSFail(@"%@ Unknown interaction type: %@", self.logTag, self.viewItem.interaction.class);
// Media-only messages should have no background color; they will fill the bubble's bounds
// and we don't want artifacts at the edges.
}
//<<<<<<< HEAD
@ -1104,6 +853,7 @@ static const CGFloat kBubbleTextVInset = 6.f;
OWSAssert(self.unloadCellContentBlock);
OWSAssert(!lastSubview);
self.lastBodyMediaView = bodyMediaView;
bodyMediaView.userInteractionEnabled = NO;
if (self.isMediaBeingSent) {
bodyMediaView.layer.opacity = 0.75f;
@ -1750,7 +1500,6 @@ static const CGFloat kBubbleTextVInset = 6.f;
self.unloadCellContentBlock = ^{
stillImageView.image = nil;
};
self.lastImageView = stillImageView;
return stillImageView;
}
@ -1793,7 +1542,6 @@ static const CGFloat kBubbleTextVInset = 6.f;
self.unloadCellContentBlock = ^{
animatedImageView.image = nil;
};
self.lastImageView = animatedImageView;
return animatedImageView;
}
@ -1878,7 +1626,6 @@ static const CGFloat kBubbleTextVInset = 6.f;
self.unloadCellContentBlock = ^{
stillImageView.image = nil;
};
self.lastImageView = stillImageView;
return stillImageView;
}
@ -2252,6 +1999,7 @@ static const CGFloat kBubbleTextVInset = 6.f;
// TODO:
self.bubbleView.hidden = YES;
self.bubbleView.bubbleColor = nil;
//<<<<<<< HEAD
// self.textBubbleImageView.image = nil;
// self.textBubbleImageView.hidden = YES;
@ -2297,8 +2045,8 @@ static const CGFloat kBubbleTextVInset = 6.f;
[self.expirationTimerView removeFromSuperview];
self.expirationTimerView = nil;
[self.lastImageView removeFromSuperview];
self.lastImageView = nil;
[self.lastBodyMediaView removeFromSuperview];
self.lastBodyMediaView = nil;
[self hideMenuControllerIfNecessary];
}
@ -2331,6 +2079,30 @@ static const CGFloat kBubbleTextVInset = 6.f;
#pragma mark - Gesture recognizers
- (void)handleTapGesture:(UITapGestureRecognizer *)sender
{
OWSAssert(self.delegate);
if (sender.state != UIGestureRecognizerStateRecognized) {
DDLogVerbose(@"%@ Ignoring tap on message: %@", self.logTag, self.viewItem.interaction.debugDescription);
return;
}
if (self.lastBodyMediaView) {
// Treat this as a "body media" gesture if:
//
// * There is a "body media" view.
// * The gesture occured within or above the "body media" view.
CGPoint location = [sender locationInView:self.lastBodyMediaView];
if (location.y <= self.lastBodyMediaView.height) {
[self handleMediaTapGesture:sender];
return;
}
}
[self handleTextTapGesture:sender];
}
- (void)handleTextTapGesture:(UITapGestureRecognizer *)sender
{
OWSAssert(self.delegate);
@ -2388,25 +2160,25 @@ static const CGFloat kBubbleTextVInset = 6.f;
}
break;
case OWSMessageCellType_StillImage:
OWSAssert(self.lastImageView);
OWSAssert(self.lastBodyMediaView);
[self.delegate didTapImageViewItem:self.viewItem
attachmentStream:self.attachmentStream
imageView:self.lastImageView];
imageView:self.lastBodyMediaView];
break;
case OWSMessageCellType_AnimatedImage:
OWSAssert(self.lastImageView);
OWSAssert(self.lastBodyMediaView);
[self.delegate didTapImageViewItem:self.viewItem
attachmentStream:self.attachmentStream
imageView:self.lastImageView];
imageView:self.lastBodyMediaView];
break;
case OWSMessageCellType_Audio:
[self.delegate didTapAudioViewItem:self.viewItem attachmentStream:self.attachmentStream];
return;
case OWSMessageCellType_Video:
OWSAssert(self.lastImageView);
OWSAssert(self.lastBodyMediaView);
[self.delegate didTapVideoViewItem:self.viewItem
attachmentStream:self.attachmentStream
imageView:self.lastImageView];
imageView:self.lastBodyMediaView];
return;
case OWSMessageCellType_GenericAttachment:
[AttachmentSharing showShareUIForAttachment:self.attachmentStream];
@ -2421,6 +2193,29 @@ static const CGFloat kBubbleTextVInset = 6.f;
}
}
- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender
{
OWSAssert(self.delegate);
if (sender.state != UIGestureRecognizerStateBegan) {
return;
}
if (self.lastBodyMediaView) {
// Treat this as a "body media" gesture if:
//
// * There is a "body media" view.
// * The gesture occured within or above the "body media" view.
CGPoint location = [sender locationInView:self.lastBodyMediaView];
if (location.y <= self.lastBodyMediaView.height) {
[self handleMediaLongPressGesture:sender];
return;
}
}
[self handleTextLongPressGesture:sender];
}
- (void)handleTextLongPressGesture:(UILongPressGestureRecognizer *)sender
{
OWSAssert(self.delegate);