You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m

241 lines
6.3 KiB
Matlab

7 years ago
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSBubbleView.h"
#import <SignalMessaging/UIView+OWS.h>
NS_ASSUME_NONNULL_BEGIN
const CGFloat kOWSMessageCellCornerRadius_Large = 18;
const CGFloat kOWSMessageCellCornerRadius_Small = 2;
7 years ago
7 years ago
@interface OWSBubbleView ()
@property (nonatomic) CAShapeLayer *maskLayer;
@property (nonatomic) CAShapeLayer *shapeLayer;
@property (nonatomic, readonly) NSMutableArray<id<OWSBubbleViewPartner>> *partnerViews;
7 years ago
@end
#pragma mark -
7 years ago
@implementation OWSBubbleView
7 years ago
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
self.layoutMargins = UIEdgeInsetsZero;
7 years ago
self.shapeLayer = [CAShapeLayer new];
[self.layer addSublayer:self.shapeLayer];
self.maskLayer = [CAShapeLayer new];
self.layer.mask = self.maskLayer;
_partnerViews = [NSMutableArray new];
7 years ago
return self;
}
7 years ago
- (void)setFrame:(CGRect)frame
{
7 years ago
// We only need to update our layers if the _size_ of this view
// changes since the contents of the layers are in local coordinates.
BOOL didChangeSize = !CGSizeEqualToSize(self.frame.size, frame.size);
7 years ago
[super setFrame:frame];
if (didChangeSize) {
7 years ago
[self updateLayers];
7 years ago
}
7 years ago
7 years ago
// We always need to inform the "bubble stroke view" (if any) if our
// frame/bounds/center changes. Its contents are not in local coordinates.
[self updatePartnerViews];
7 years ago
}
- (void)setBounds:(CGRect)bounds
{
7 years ago
// We only need to update our layers if the _size_ of this view
// changes since the contents of the layers are in local coordinates.
BOOL didChangeSize = !CGSizeEqualToSize(self.bounds.size, bounds.size);
7 years ago
[super setBounds:bounds];
if (didChangeSize) {
7 years ago
[self updateLayers];
7 years ago
}
7 years ago
7 years ago
// We always need to inform the "bubble stroke view" (if any) if our
// frame/bounds/center changes. Its contents are not in local coordinates.
[self updatePartnerViews];
7 years ago
}
- (void)setCenter:(CGPoint)center
{
[super setCenter:center];
7 years ago
// We always need to inform the "bubble stroke view" (if any) if our
// frame/bounds/center changes. Its contents are not in local coordinates.
[self updatePartnerViews];
7 years ago
}
- (void)setBubbleColor:(nullable UIColor *)bubbleColor
{
_bubbleColor = bubbleColor;
[self updateLayers];
// Prevent the shape layer from animating changes.
[CATransaction begin];
7 years ago
[CATransaction setDisableActions:YES];
7 years ago
self.shapeLayer.fillColor = bubbleColor.CGColor;
[CATransaction commit];
7 years ago
}
- (void)setUseSmallCorners_Top:(BOOL)useSmallCorners_Top
{
_useSmallCorners_Top = useSmallCorners_Top;
[self updateLayers];
}
- (void)setUseSmallCorners_Bottom:(BOOL)useSmallCorners_Bottom
{
_useSmallCorners_Bottom = useSmallCorners_Bottom;
[self updateLayers];
}
7 years ago
- (void)updateLayers
7 years ago
{
if (!self.maskLayer) {
return;
}
if (!self.shapeLayer) {
return;
}
7 years ago
7 years ago
UIBezierPath *bezierPath = [self maskPath];
7 years ago
// Prevent the shape layer from animating changes.
[CATransaction begin];
7 years ago
[CATransaction setDisableActions:YES];
7 years ago
self.shapeLayer.fillColor = self.bubbleColor.CGColor;
self.shapeLayer.path = bezierPath.CGPath;
self.maskLayer.path = bezierPath.CGPath;
[CATransaction commit];
7 years ago
}
7 years ago
- (UIBezierPath *)maskPath
{
return [self.class maskPathForSize:self.bounds.size
useSmallCorners_Top:self.useSmallCorners_Top
useSmallCorners_Bottom:self.useSmallCorners_Bottom];
7 years ago
}
7 years ago
+ (UIBezierPath *)maskPathForSize:(CGSize)size
useSmallCorners_Top:(BOOL)useSmallCorners_Top
useSmallCorners_Bottom:(BOOL)useSmallCorners_Bottom
7 years ago
{
CGRect bounds = CGRectZero;
bounds.size = size;
UIBezierPath *bezierPath = [UIBezierPath new];
CGFloat bubbleLeft = 0.f;
CGFloat bubbleRight = size.width;
CGFloat bubbleTop = 0.f;
CGFloat bubbleBottom = size.height;
CGFloat topRounding = (useSmallCorners_Top ? kOWSMessageCellCornerRadius_Small : kOWSMessageCellCornerRadius_Large);
CGFloat bottomRounding
= (useSmallCorners_Bottom ? kOWSMessageCellCornerRadius_Small : kOWSMessageCellCornerRadius_Large);
7 years ago
const CGFloat topAngle = 3.0f * M_PI_2;
7 years ago
const CGFloat rightAngle = 0.0f;
7 years ago
const CGFloat bottomAngle = M_PI_2;
7 years ago
const CGFloat leftAngle = M_PI;
[bezierPath moveToPoint:CGPointMake(bubbleLeft + topRounding, bubbleTop)];
7 years ago
// top right corner
[bezierPath addArcWithCenter:CGPointMake(bubbleRight - topRounding, bubbleTop + topRounding)
radius:topRounding
startAngle:topAngle
endAngle:rightAngle
clockwise:true];
// bottom right corner
[bezierPath addArcWithCenter:CGPointMake(bubbleRight - bottomRounding, bubbleBottom - bottomRounding)
radius:bottomRounding
startAngle:rightAngle
endAngle:bottomAngle
clockwise:true];
// bottom left corner
[bezierPath addArcWithCenter:CGPointMake(bubbleLeft + bottomRounding, bubbleBottom - bottomRounding)
radius:bottomRounding
startAngle:bottomAngle
endAngle:leftAngle
clockwise:true];
// top left corner
[bezierPath addArcWithCenter:CGPointMake(bubbleLeft + topRounding, bubbleTop + topRounding)
radius:topRounding
startAngle:leftAngle
endAngle:topAngle
clockwise:true];
7 years ago
return bezierPath;
}
#pragma mark - Coordination
- (void)addPartnerView:(id<OWSBubbleViewPartner>)partnerView
{
OWSAssert(self.partnerViews);
[partnerView setBubbleView:self];
[self.partnerViews addObject:partnerView];
}
- (void)clearPartnerViews
{
OWSAssert(self.partnerViews);
[self.partnerViews removeAllObjects];
}
- (void)updatePartnerViews
{
[self layoutIfNeeded];
for (id<OWSBubbleViewPartner> partnerView in self.partnerViews) {
[partnerView updateLayers];
}
}
- (CGFloat)minWidth
{
if (self.useSmallCorners_Top && self.useSmallCorners_Bottom) {
return (kOWSMessageCellCornerRadius_Small * 2);
} else {
return (kOWSMessageCellCornerRadius_Large * 2);
}
}
7 years ago
@end
NS_ASSUME_NONNULL_END