Add body media shadows.

This commit is contained in:
Matthew Chen 2018-06-27 12:44:06 -04:00
parent ec81e15582
commit 9cc3a3b7b3
10 changed files with 295 additions and 183 deletions

View File

@ -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 */,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -160,6 +160,7 @@ const CGFloat kOWSMessageCellCornerRadius = 16;
for (id<OWSBubbleViewPartner> partnerView in self.partnerViews) {
[partnerView updateLayers];
}
[self.delegate updateLayers];
}
+ (CGFloat)minWidth

View File

@ -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) {

View File

@ -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;

View File

@ -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];