Add body media shadows.
This commit is contained in:
parent
ec81e15582
commit
9cc3a3b7b3
|
@ -229,7 +229,7 @@
|
|||
34DB0BED2011548B007B313F /* OWSDatabaseConverterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DB0BEC2011548B007B313F /* OWSDatabaseConverterTest.m */; };
|
||||
34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBEFFF206BD5A400025978 /* OWSMessageTextView.m */; };
|
||||
34DBF004206BD5A500025978 /* OWSBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBF001206BD5A500025978 /* OWSBubbleView.m */; };
|
||||
34DBF007206C3CB200025978 /* OWSBubbleStrokeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBF006206C3CB200025978 /* OWSBubbleStrokeView.m */; };
|
||||
34DBF007206C3CB200025978 /* OWSBubbleShapeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBF006206C3CB200025978 /* OWSBubbleShapeView.m */; };
|
||||
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; };
|
||||
34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E3EF0C1EFC235B007F6822 /* DebugUIDiskUsage.m */; };
|
||||
34E3EF101EFC2684007F6822 /* DebugUIPage.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E3EF0F1EFC2684007F6822 /* DebugUIPage.m */; };
|
||||
|
@ -891,8 +891,8 @@
|
|||
34DBF000206BD5A400025978 /* OWSMessageTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageTextView.h; sourceTree = "<group>"; };
|
||||
34DBF001206BD5A500025978 /* OWSBubbleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBubbleView.m; sourceTree = "<group>"; };
|
||||
34DBF002206BD5A500025978 /* OWSBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBubbleView.h; sourceTree = "<group>"; };
|
||||
34DBF005206C3CB100025978 /* OWSBubbleStrokeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBubbleStrokeView.h; sourceTree = "<group>"; };
|
||||
34DBF006206C3CB200025978 /* OWSBubbleStrokeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBubbleStrokeView.m; sourceTree = "<group>"; };
|
||||
34DBF005206C3CB100025978 /* OWSBubbleShapeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBubbleShapeView.h; sourceTree = "<group>"; };
|
||||
34DBF006206C3CB200025978 /* OWSBubbleShapeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBubbleShapeView.m; sourceTree = "<group>"; };
|
||||
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = "<group>"; };
|
||||
34E3EF0B1EFC235B007F6822 /* DebugUIDiskUsage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIDiskUsage.h; sourceTree = "<group>"; };
|
||||
34E3EF0C1EFC235B007F6822 /* DebugUIDiskUsage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIDiskUsage.m; sourceTree = "<group>"; };
|
||||
|
@ -1742,8 +1742,8 @@
|
|||
34D1F0971F867BFC0066283D /* ConversationViewCell.m */,
|
||||
34D1F0B81F8800D90066283D /* OWSAudioMessageView.h */,
|
||||
34D1F0B91F8800D90066283D /* OWSAudioMessageView.m */,
|
||||
34DBF005206C3CB100025978 /* OWSBubbleStrokeView.h */,
|
||||
34DBF006206C3CB200025978 /* OWSBubbleStrokeView.m */,
|
||||
34DBF005206C3CB100025978 /* OWSBubbleShapeView.h */,
|
||||
34DBF006206C3CB200025978 /* OWSBubbleShapeView.m */,
|
||||
34DBF002206BD5A500025978 /* OWSBubbleView.h */,
|
||||
34DBF001206BD5A500025978 /* OWSBubbleView.m */,
|
||||
34D1F09A1F867BFC0066283D /* OWSContactOffersCell.h */,
|
||||
|
@ -3186,7 +3186,7 @@
|
|||
34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */,
|
||||
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */,
|
||||
34386A52207D0C01009F5D9C /* HomeViewCell.m in Sources */,
|
||||
34DBF007206C3CB200025978 /* OWSBubbleStrokeView.m in Sources */,
|
||||
34DBF007206C3CB200025978 /* OWSBubbleShapeView.m in Sources */,
|
||||
34D1F0BA1F8800D90066283D /* OWSAudioMessageView.m in Sources */,
|
||||
34D8C02B1ED3685800188D7C /* DebugUIContacts.m in Sources */,
|
||||
45C9DEB81DF4E35A0065CA84 /* WebRTCCallMessageHandler.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSBubbleView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSBubbleView;
|
||||
|
||||
@interface OWSBubbleShapeView : UIView <OWSBubbleViewPartner>
|
||||
|
||||
@property (nonatomic, nullable) UIColor *fillColor;
|
||||
@property (nonatomic, nullable) UIColor *strokeColor;
|
||||
@property (nonatomic) CGFloat strokeThickness;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (OWSBubbleShapeView *)bubbleDrawView;
|
||||
+ (OWSBubbleShapeView *)bubbleShadowView;
|
||||
+ (OWSBubbleShapeView *)bubbleClipView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,198 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSBubbleShapeView.h"
|
||||
#import "OWSBubbleView.h"
|
||||
#import <SignalMessaging/UIView+OWS.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, OWSBubbleShapeViewMode) {
|
||||
// For stroking or filling.
|
||||
OWSBubbleShapeViewMode_Draw,
|
||||
OWSBubbleShapeViewMode_Shadow,
|
||||
OWSBubbleShapeViewMode_Clip,
|
||||
};
|
||||
|
||||
@interface OWSBubbleShapeView ()
|
||||
|
||||
@property (nonatomic) OWSBubbleShapeViewMode mode;
|
||||
|
||||
@property (nonatomic) CAShapeLayer *shapeLayer;
|
||||
@property (nonatomic) CAShapeLayer *maskLayer;
|
||||
|
||||
@property (nonatomic, weak) OWSBubbleView *bubbleView;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSBubbleShapeView
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.mode = OWSBubbleShapeViewMode_Draw;
|
||||
self.opaque = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
|
||||
self.shapeLayer = [CAShapeLayer new];
|
||||
[self.layer addSublayer:self.shapeLayer];
|
||||
|
||||
self.maskLayer = [CAShapeLayer new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (OWSBubbleShapeView *)bubbleDrawView
|
||||
{
|
||||
OWSBubbleShapeView *instance = [OWSBubbleShapeView new];
|
||||
instance.mode = OWSBubbleShapeViewMode_Draw;
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (OWSBubbleShapeView *)bubbleShadowView
|
||||
{
|
||||
OWSBubbleShapeView *instance = [OWSBubbleShapeView new];
|
||||
instance.mode = OWSBubbleShapeViewMode_Shadow;
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (OWSBubbleShapeView *)bubbleClipView
|
||||
{
|
||||
OWSBubbleShapeView *instance = [OWSBubbleShapeView new];
|
||||
instance.mode = OWSBubbleShapeViewMode_Clip;
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (void)setFillColor:(nullable UIColor *)fillColor
|
||||
{
|
||||
_fillColor = fillColor;
|
||||
|
||||
[self updateLayers];
|
||||
}
|
||||
|
||||
- (void)setStrokeColor:(nullable UIColor *)strokeColor
|
||||
{
|
||||
_strokeColor = strokeColor;
|
||||
|
||||
[self updateLayers];
|
||||
}
|
||||
|
||||
- (void)setStrokeThickness:(CGFloat)strokeThickness
|
||||
{
|
||||
_strokeThickness = strokeThickness;
|
||||
|
||||
[self updateLayers];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
BOOL didChange = !CGRectEqualToRect(self.frame, frame);
|
||||
|
||||
[super setFrame:frame];
|
||||
|
||||
if (didChange) {
|
||||
[self updateLayers];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBounds:(CGRect)bounds
|
||||
{
|
||||
BOOL didChange = !CGRectEqualToRect(self.bounds, bounds);
|
||||
|
||||
[super setBounds:bounds];
|
||||
|
||||
if (didChange) {
|
||||
[self updateLayers];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCenter:(CGPoint)center
|
||||
{
|
||||
[super setCenter:center];
|
||||
|
||||
[self updateLayers];
|
||||
}
|
||||
|
||||
- (void)updateLayers
|
||||
{
|
||||
if (!self.shapeLayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.bubbleView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent the layer from animating changes.
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
UIBezierPath *bezierPath = [UIBezierPath new];
|
||||
|
||||
// Add the bubble view's path to the local path.
|
||||
UIBezierPath *bubbleBezierPath = [self.bubbleView maskPath];
|
||||
// We need to convert between coordinate systems using layers, not views.
|
||||
CGPoint bubbleOffset = [self.layer convertPoint:CGPointZero fromLayer:self.bubbleView.layer];
|
||||
CGAffineTransform transform = CGAffineTransformMakeTranslation(bubbleOffset.x, bubbleOffset.y);
|
||||
[bubbleBezierPath applyTransform:transform];
|
||||
[bezierPath appendPath:bubbleBezierPath];
|
||||
|
||||
switch (self.mode) {
|
||||
case OWSBubbleShapeViewMode_Draw: {
|
||||
UIBezierPath *boundsBezierPath = [UIBezierPath bezierPathWithRect:self.bounds];
|
||||
[bezierPath appendPath:boundsBezierPath];
|
||||
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
if (self.strokeColor) {
|
||||
self.shapeLayer.strokeColor = self.strokeColor.CGColor;
|
||||
self.shapeLayer.lineWidth = self.strokeThickness;
|
||||
self.shapeLayer.zPosition = 100.f;
|
||||
} else {
|
||||
self.shapeLayer.strokeColor = nil;
|
||||
self.shapeLayer.lineWidth = 0.f;
|
||||
}
|
||||
if (self.fillColor) {
|
||||
self.shapeLayer.fillColor = self.fillColor.CGColor;
|
||||
} else {
|
||||
self.shapeLayer.fillColor = nil;
|
||||
}
|
||||
|
||||
self.shapeLayer.path = bezierPath.CGPath;
|
||||
|
||||
break;
|
||||
}
|
||||
case OWSBubbleShapeViewMode_Shadow:
|
||||
self.clipsToBounds = NO;
|
||||
|
||||
if (self.fillColor) {
|
||||
self.shapeLayer.fillColor = self.fillColor.CGColor;
|
||||
} else {
|
||||
self.shapeLayer.fillColor = nil;
|
||||
}
|
||||
|
||||
self.shapeLayer.path = bezierPath.CGPath;
|
||||
self.shapeLayer.frame = self.bounds;
|
||||
self.shapeLayer.masksToBounds = YES;
|
||||
|
||||
break;
|
||||
case OWSBubbleShapeViewMode_Clip:
|
||||
self.maskLayer.path = bezierPath.CGPath;
|
||||
self.layer.mask = self.maskLayer;
|
||||
break;
|
||||
}
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSBubbleView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSBubbleView;
|
||||
|
||||
@interface OWSBubbleStrokeView : UIView <OWSBubbleViewPartner>
|
||||
|
||||
@property (nonatomic) UIColor *strokeColor;
|
||||
@property (nonatomic) CGFloat strokeThickness;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,129 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSBubbleStrokeView.h"
|
||||
#import "OWSBubbleView.h"
|
||||
#import <SignalMessaging/UIView+OWS.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSBubbleStrokeView ()
|
||||
|
||||
@property (nonatomic) CAShapeLayer *shapeLayer;
|
||||
|
||||
@property (nonatomic, weak) OWSBubbleView *bubbleView;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSBubbleStrokeView
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.opaque = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
||||
self.shapeLayer = [CAShapeLayer new];
|
||||
[self.layer addSublayer:self.shapeLayer];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setStrokeColor:(UIColor *)strokeColor
|
||||
{
|
||||
_strokeColor = strokeColor;
|
||||
|
||||
[self updateLayers];
|
||||
}
|
||||
|
||||
- (void)setStrokeThickness:(CGFloat)strokeThickness
|
||||
{
|
||||
_strokeThickness = strokeThickness;
|
||||
|
||||
[self updateLayers];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
BOOL didChange = !CGRectEqualToRect(self.frame, frame);
|
||||
|
||||
[super setFrame:frame];
|
||||
|
||||
if (didChange) {
|
||||
[self updateLayers];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBounds:(CGRect)bounds
|
||||
{
|
||||
BOOL didChange = !CGRectEqualToRect(self.bounds, bounds);
|
||||
|
||||
[super setBounds:bounds];
|
||||
|
||||
if (didChange) {
|
||||
[self updateLayers];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCenter:(CGPoint)center
|
||||
{
|
||||
[super setCenter:center];
|
||||
|
||||
[self updateLayers];
|
||||
}
|
||||
|
||||
- (void)updateLayers
|
||||
{
|
||||
if (!self.shapeLayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent the shape layer from animating changes.
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
// Don't fill the shape layer; we just want a stroke around the border.
|
||||
self.shapeLayer.fillColor = [UIColor clearColor].CGColor;
|
||||
|
||||
[CATransaction commit];
|
||||
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
if (!self.bubbleView) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIBezierPath *bezierPath = [UIBezierPath new];
|
||||
|
||||
UIBezierPath *boundsBezierPath = [UIBezierPath bezierPathWithRect:self.bounds];
|
||||
[bezierPath appendPath:boundsBezierPath];
|
||||
|
||||
UIBezierPath *bubbleBezierPath = [self.bubbleView maskPath];
|
||||
// We need to convert between coordinate systems using layers, not views.
|
||||
CGPoint bubbleOffset = [self.layer convertPoint:CGPointZero fromLayer:self.bubbleView.layer];
|
||||
CGAffineTransform transform = CGAffineTransformMakeTranslation(bubbleOffset.x, bubbleOffset.y);
|
||||
[bubbleBezierPath applyTransform:transform];
|
||||
[bezierPath appendPath:bubbleBezierPath];
|
||||
|
||||
// Prevent the shape layer from animating changes.
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
self.shapeLayer.strokeColor = self.strokeColor.CGColor;
|
||||
self.shapeLayer.lineWidth = self.strokeThickness;
|
||||
self.shapeLayer.zPosition = 100.f;
|
||||
self.shapeLayer.path = bezierPath.CGPath;
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -24,6 +24,8 @@ extern const CGFloat kOWSMessageCellCornerRadius;
|
|||
|
||||
@property (nonatomic, nullable) UIColor *bubbleColor;
|
||||
|
||||
@property (nonatomic, nullable, weak) id<OWSBubbleViewPartner> delegate;
|
||||
|
||||
- (UIBezierPath *)maskPath;
|
||||
|
||||
#pragma mark - Coordination
|
||||
|
|
|
@ -160,6 +160,7 @@ const CGFloat kOWSMessageCellCornerRadius = 16;
|
|||
for (id<OWSBubbleViewPartner> partnerView in self.partnerViews) {
|
||||
[partnerView updateLayers];
|
||||
}
|
||||
[self.delegate updateLayers];
|
||||
}
|
||||
|
||||
+ (CGFloat)minWidth
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#import "AttachmentUploadView.h"
|
||||
#import "ConversationViewItem.h"
|
||||
#import "OWSAudioMessageView.h"
|
||||
#import "OWSBubbleStrokeView.h"
|
||||
#import "OWSBubbleShapeView.h"
|
||||
#import "OWSBubbleView.h"
|
||||
#import "OWSContactShareView.h"
|
||||
#import "OWSGenericAttachmentView.h"
|
||||
|
@ -23,6 +23,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@property (nonatomic) OWSBubbleView *bubbleView;
|
||||
|
||||
@property (nonatomic) OWSBubbleShapeView *mediaShadowView;
|
||||
|
||||
@property (nonatomic) OWSBubbleShapeView *mediaClipView;
|
||||
|
||||
@property (nonatomic) UIStackView *stackView;
|
||||
|
||||
@property (nonatomic) UILabel *senderNameLabel;
|
||||
|
@ -75,6 +79,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self addSubview:self.bubbleView];
|
||||
[self.bubbleView autoPinEdgesToSuperviewEdges];
|
||||
|
||||
self.mediaShadowView = [OWSBubbleShapeView bubbleShadowView];
|
||||
self.mediaClipView = [OWSBubbleShapeView bubbleClipView];
|
||||
|
||||
self.stackView = [UIStackView new];
|
||||
self.stackView.axis = UILayoutConstraintAxisVertical;
|
||||
self.stackView.alignment = UIStackViewAlignmentFill;
|
||||
|
@ -278,7 +285,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
UIView *_Nullable bodyMediaView = nil;
|
||||
BOOL bodyMediaViewHasGreedyWidth = NO;
|
||||
BOOL hasThumbnailForBodyMedia = NO;
|
||||
switch (self.cellType) {
|
||||
case OWSMessageCellType_Unknown:
|
||||
case OWSMessageCellType_TextMessage:
|
||||
|
@ -287,41 +294,37 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
case OWSMessageCellType_StillImage:
|
||||
OWSAssert(self.viewItem.attachmentStream);
|
||||
bodyMediaView = [self loadViewForStillImage];
|
||||
hasThumbnailForBodyMedia = YES;
|
||||
break;
|
||||
case OWSMessageCellType_AnimatedImage:
|
||||
OWSAssert(self.viewItem.attachmentStream);
|
||||
bodyMediaView = [self loadViewForAnimatedImage];
|
||||
hasThumbnailForBodyMedia = YES;
|
||||
break;
|
||||
case OWSMessageCellType_Video:
|
||||
OWSAssert(self.viewItem.attachmentStream);
|
||||
bodyMediaView = [self loadViewForVideo];
|
||||
hasThumbnailForBodyMedia = YES;
|
||||
break;
|
||||
case OWSMessageCellType_Audio:
|
||||
OWSAssert(self.viewItem.attachmentStream);
|
||||
bodyMediaView = [self loadViewForAudio];
|
||||
bodyMediaViewHasGreedyWidth = YES;
|
||||
break;
|
||||
case OWSMessageCellType_GenericAttachment:
|
||||
bodyMediaView = [self loadViewForGenericAttachment];
|
||||
bodyMediaViewHasGreedyWidth = YES;
|
||||
break;
|
||||
case OWSMessageCellType_DownloadingAttachment:
|
||||
bodyMediaView = [self loadViewForDownloadingAttachment];
|
||||
bodyMediaViewHasGreedyWidth = YES;
|
||||
break;
|
||||
case OWSMessageCellType_ContactShare:
|
||||
bodyMediaView = [self loadViewForContactShare];
|
||||
bodyMediaViewHasGreedyWidth = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
BOOL shouldFooterOverlayMedia = NO;
|
||||
if (bodyMediaView) {
|
||||
OWSAssert(self.loadCellContentBlock);
|
||||
OWSAssert(self.unloadCellContentBlock);
|
||||
|
||||
shouldFooterOverlayMedia = self.canFooterOverlayMedia;
|
||||
|
||||
// Flush any pending "text" subviews.
|
||||
[self insertAnyTextViewsIntoStackView:textViews];
|
||||
[textViews removeAllObjects];
|
||||
|
@ -334,22 +337,40 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
bodyMediaView.layer.opacity = 0.75f;
|
||||
}
|
||||
|
||||
[self.stackView addArrangedSubview:bodyMediaView];
|
||||
if (hasThumbnailForBodyMedia) {
|
||||
// The "body media" view casts a shadow "downward" onto adjacent views,
|
||||
// so we use a "proxy" view to take its place within the v-stack
|
||||
// view and then insert the body media view above its proxy so that
|
||||
// it floats above the other content of the bubble view.
|
||||
|
||||
BOOL shouldStrokeMediaView = ([bodyMediaView isKindOfClass:[UIImageView class]] ||
|
||||
[bodyMediaView isKindOfClass:[OWSContactShareView class]]);
|
||||
if (shouldStrokeMediaView) {
|
||||
OWSBubbleStrokeView *bubbleStrokeView = [OWSBubbleStrokeView new];
|
||||
bubbleStrokeView.strokeThickness = 1.f;
|
||||
bubbleStrokeView.strokeColor = [UIColor colorWithWhite:0.f alpha:0.1f];
|
||||
UIView *bodyProxyView = [UIView new];
|
||||
[self.stackView addArrangedSubview:bodyProxyView];
|
||||
|
||||
[self.bubbleView addSubview:bubbleStrokeView];
|
||||
[bubbleStrokeView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:bodyMediaView];
|
||||
[bubbleStrokeView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:bodyMediaView];
|
||||
[bubbleStrokeView autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:bodyMediaView];
|
||||
[bubbleStrokeView autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:bodyMediaView];
|
||||
[self addSubview:self.mediaShadowView];
|
||||
[self.mediaShadowView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:bodyProxyView];
|
||||
[self.mediaShadowView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:bodyProxyView];
|
||||
[self.mediaShadowView autoPinEdge:ALEdgeLeading toEdge:ALEdgeLeading ofView:bodyProxyView];
|
||||
[self.mediaShadowView autoPinEdge:ALEdgeTrailing toEdge:ALEdgeTrailing ofView:bodyProxyView];
|
||||
|
||||
[self.bubbleView addPartnerView:bubbleStrokeView];
|
||||
[self.mediaShadowView addSubview:self.mediaClipView];
|
||||
[self.mediaClipView autoPinToSuperviewEdges];
|
||||
|
||||
[self.mediaClipView addSubview:bodyMediaView];
|
||||
[bodyMediaView autoPinToSuperviewEdges];
|
||||
|
||||
[self.bubbleView addPartnerView:self.mediaClipView];
|
||||
[self.bubbleView addPartnerView:self.mediaShadowView];
|
||||
|
||||
// TODO: Constants
|
||||
// TODO: What's the difference between an inner and outer shadow?
|
||||
self.mediaShadowView.fillColor = self.bubbleColor;
|
||||
self.mediaShadowView.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
self.mediaShadowView.layer.shadowOpacity = 0.08f;
|
||||
self.mediaShadowView.layer.shadowOpacity = 1.f;
|
||||
self.mediaShadowView.layer.shadowOffset = CGSizeMake(0.f, 2.f);
|
||||
self.mediaShadowView.layer.shadowRadius = 8.f;
|
||||
} else {
|
||||
[self.stackView addArrangedSubview:bodyMediaView];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -372,6 +393,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
BOOL shouldFooterOverlayMedia = (self.canFooterOverlayMedia && bodyMediaView && !self.hasBodyText);
|
||||
if (self.viewItem.shouldHideFooter) {
|
||||
// Do nothing.
|
||||
} else if (shouldFooterOverlayMedia) {
|
||||
|
@ -424,8 +446,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
break;
|
||||
}
|
||||
if (!hasOnlyBodyMediaView) {
|
||||
TSMessage *message = (TSMessage *)self.viewItem.interaction;
|
||||
self.bubbleView.bubbleColor = [self.bubbleFactory bubbleColorWithMessage:message];
|
||||
self.bubbleView.bubbleColor = self.bubbleColor;
|
||||
} else {
|
||||
// Media-only messages should have no background color; they will fill the bubble's bounds
|
||||
// and we don't want artifacts at the edges.
|
||||
|
@ -433,6 +454,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
- (UIColor *)bubbleColor
|
||||
{
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
TSMessage *message = (TSMessage *)self.viewItem.interaction;
|
||||
return [self.bubbleFactory bubbleColorWithMessage:message];
|
||||
}
|
||||
|
||||
- (BOOL)canFooterOverlayMedia
|
||||
{
|
||||
switch (self.cellType) {
|
||||
|
@ -1089,7 +1118,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
// TODO: Update this to reflect generic attachment, downloading attachments and
|
||||
// contact shares.
|
||||
if (!self.viewItem.shouldHideFooter && !self.canFooterOverlayMedia) {
|
||||
BOOL shouldFooterOverlayMedia = (self.canFooterOverlayMedia && !self.hasBodyText);
|
||||
if (!self.viewItem.shouldHideFooter && !shouldFooterOverlayMedia) {
|
||||
CGSize footerSize = [self.footerView measureWithConversationViewItem:self.viewItem];
|
||||
cellSize.width = MAX(cellSize.width, footerSize.width + self.conversationStyle.textInsetHorizontal * 2);
|
||||
cellSize.height += self.textViewVSpacing + footerSize.height;
|
||||
|
@ -1174,6 +1204,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self.quotedMessageView removeFromSuperview];
|
||||
self.quotedMessageView = nil;
|
||||
|
||||
[self.mediaShadowView removeFromSuperview];
|
||||
[self.mediaClipView removeFromSuperview];
|
||||
|
||||
[self.footerView removeFromSuperview];
|
||||
|
||||
for (UIView *subview in self.stackView.subviews) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class DisplayableText;
|
||||
@class OWSBubbleStrokeView;
|
||||
@class OWSBubbleShapeView;
|
||||
@class OWSQuotedReplyModel;
|
||||
@class TSAttachmentPointer;
|
||||
@class TSQuotedMessage;
|
||||
|
@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@interface OWSQuotedMessageView : UIView
|
||||
|
||||
@property (nonatomic, nullable, readonly) OWSBubbleStrokeView *boundsStrokeView;
|
||||
@property (nonatomic, nullable, readonly) OWSBubbleShapeView *boundsStrokeView;
|
||||
@property (nonatomic, nullable, weak) id<OWSQuotedMessageViewDelegate> delegate;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#import "OWSQuotedMessageView.h"
|
||||
#import "ConversationViewItem.h"
|
||||
#import "Environment.h"
|
||||
#import "OWSBubbleStrokeView.h"
|
||||
#import "OWSBubbleShapeView.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import <SignalMessaging/OWSContactsManager.h>
|
||||
#import <SignalMessaging/SignalMessaging-Swift.h>
|
||||
|
@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic, readonly) OWSQuotedReplyModel *quotedMessage;
|
||||
@property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText;
|
||||
|
||||
@property (nonatomic, nullable) OWSBubbleStrokeView *boundsStrokeView;
|
||||
@property (nonatomic, nullable) OWSBubbleShapeView *boundsStrokeView;
|
||||
@property (nonatomic, readonly) BOOL isForPreview;
|
||||
@property (nonatomic, readonly) BOOL isOutgoing;
|
||||
|
||||
|
@ -119,7 +119,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
self.boundsStrokeView = [OWSBubbleStrokeView new];
|
||||
self.boundsStrokeView = [OWSBubbleShapeView new];
|
||||
self.boundsStrokeView.strokeColor = OWSMessagesBubbleImageFactory.bubbleColorIncoming;
|
||||
self.boundsStrokeView.strokeThickness = 1.f;
|
||||
[self addSubview:self.boundsStrokeView];
|
||||
|
|
Loading…
Reference in New Issue