Swiftgram/submodules/LegacyComponents/Sources/TGLocationPinAnnotationView.m
2020-10-14 01:49:49 +04:00

680 lines
24 KiB
Objective-C

#import "TGLocationPinAnnotationView.h"
#import "LegacyComponentsInternal.h"
#import "TGImageUtils.h"
#import "TGLocationMapViewController.h"
#import "TGUser.h"
#import "TGConversation.h"
#import "TGLocationAnnotation.h"
#import "TGLocationMediaAttachment.h"
#import "TGImageView.h"
#import "TGLetteredAvatarView.h"
#import "TGLocationPulseView.h"
NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
@interface TGLocationPinAnnotationView ()
{
TGLocationPulseView *_pulseView;
UIImageView *_smallView;
UIImageView *_shadowView;
UIImageView *_backgroundView;
TGImageView *_iconView;
UIImageView *_dotView;
TGLetteredAvatarView *_avatarView;
UIImageView *_smallArrowView;
UIImageView *_arrowView;
bool _liveLocation;
SMetaDisposable *_userDisposable;
bool _animating;
bool _observingExpiration;
}
@end
@implementation TGLocationPinAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation
{
self = [super initWithAnnotation:annotation reuseIdentifier:TGLocationPinAnnotationKind];
if (self != nil)
{
_pulseView = [[TGLocationPulseView alloc] init];
[self addSubview:_pulseView];
_shadowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"LocationPinShadow")];
[self addSubview:_shadowView];
_backgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 83.0f, 83.0f)];
[_shadowView addSubview:_backgroundView];
_iconView = [[TGImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 64.0f, 64.0f)];
_iconView.contentMode = UIViewContentModeCenter;
[_backgroundView addSubview:_iconView];
static dispatch_once_t onceToken;
static UIImage *smallDotImage;
static UIImage *largeDotImage;
static UIImage *smallHeadingArrowImage;
static UIImage *largeHeadingArrowImage;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, UIColorRGB(0x008df2).CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
smallDotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1.0f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(2, 2, 12.0, 12.0));
CGContextRestoreGState(context);
CGContextSetFillColorWithColor(context, UIColorRGB(0x008df2).CGColor);
CGContextFillEllipseInRect(context, CGRectMake(4.0f, 4.0f, 8.0f, 8.0f));
largeDotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, CGRectMake(0, 0, 18.0f, 18.0f));
CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
CGContextMoveToPoint(context, 9, 0);
CGContextAddLineToPoint(context, 13, 7);
CGContextAddLineToPoint(context, 5, 7);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextFillEllipseInRect(context, CGRectMake(3.0, 3.0, 12.0, 12.0));
smallHeadingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(CGSizeMake(33.0f, 33.0f), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, CGRectMake(0, 0, 16.0f, 16.0f));
CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
CGContextMoveToPoint(context, 16.5, 0);
CGContextAddLineToPoint(context, 21.5, 6);
CGContextAddLineToPoint(context, 11.5, 6);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextFillEllipseInRect(context, CGRectMake(3.0, 3.0, 27.0, 27.0));
largeHeadingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
_smallView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"LocationSmallCircle")];
_smallView.hidden = true;
[self addSubview:_smallView];
UIImage *dotImage = smallDotImage;
if ([annotation isKindOfClass:[TGLocationAnnotation class]])
{
TGLocationAnnotation *locationAnnotation = (TGLocationAnnotation *)annotation;
if (locationAnnotation.location.period > 0) {
dotImage = largeDotImage;
}
}
_dotView = [[UIImageView alloc] initWithImage:dotImage];
[self addSubview:_dotView];
_smallArrowView = [[UIImageView alloc] initWithImage:smallHeadingArrowImage];
_smallArrowView.hidden = true;
[self addSubview:_smallArrowView];
_arrowView = [[UIImageView alloc] initWithImage:largeHeadingArrowImage];
_arrowView.hidden = true;
[self addSubview:_arrowView];
[self setAnnotation:annotation];
}
return self;
}
- (void)dealloc
{
[self unsubscribeFromExpiration];
}
- (void)prepareForReuse
{
[_pulseView stop];
[_iconView reset];
_smallView.hidden = true;
_backgroundView.hidden = false;
}
- (void)subscribeForExpiration
{
if (_observingExpiration)
return;
_observingExpiration = true;
[self addObserver:self forKeyPath:@"annotation.isExpired" options:NSKeyValueObservingOptionNew context:NULL];
[self addObserver:self forKeyPath:@"annotation.heading" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)unsubscribeFromExpiration
{
if (!_observingExpiration)
return;
_observingExpiration = false;
[self removeObserver:self forKeyPath:@"annotation.isExpired"];
[self removeObserver:self forKeyPath:@"annotation.heading"];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
if (iosMajorVersion() < 7)
animated = false;
[super setSelected:selected animated:animated];
if (!_liveLocation)
return;
if (animated)
{
[self layoutSubviews];
_animating = true;
if (selected)
{
//dispatch_async(dispatch_get_main_queue(), ^
//{
UIView *avatarSnapshot = [_avatarView snapshotViewAfterScreenUpdates:false];
[_smallView addSubview:avatarSnapshot];
avatarSnapshot.transform = _avatarView.transform;
avatarSnapshot.center = CGPointMake(_smallView.frame.size.width / 2.0f, _smallView.frame.size.height / 2.0f);
_avatarView.transform = CGAffineTransformIdentity;
[_backgroundView addSubview:_avatarView];
_avatarView.center = CGPointMake(_backgroundView.frame.size.width / 2.0f, _backgroundView.frame.size.height / 2.0f - 5.0f);
_dotView.alpha = 0.0f;
_shadowView.center = CGPointMake(_shadowView.center.x, _shadowView.center.y + _shadowView.frame.size.height / 2.0f);
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 1.0f);
_shadowView.hidden = false;
_shadowView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
[UIView animateWithDuration:0.35 delay:0.0 usingSpringWithDamping:0.6f initialSpringVelocity:0.5f options:kNilOptions animations:^
{
_smallView.transform = CGAffineTransformMakeScale(0.001f, 0.001f);
_shadowView.transform = CGAffineTransformIdentity;
if (_dotView.hidden)
_smallView.alpha = 0.0f;
} completion:^(BOOL finished)
{
_animating = false;
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
_smallView.hidden = true;
_smallView.transform = CGAffineTransformIdentity;
[avatarSnapshot removeFromSuperview];
[self addSubview:_avatarView];
}];
[UIView animateWithDuration:0.2 animations:^
{
_dotView.alpha = 1.0f;
}];
//});
}
else
{
UIView *avatarSnapshot = [_avatarView snapshotViewAfterScreenUpdates:false];
[_backgroundView addSubview:avatarSnapshot];
avatarSnapshot.transform = _avatarView.transform;
avatarSnapshot.center = CGPointMake(_backgroundView.frame.size.width / 2.0f, _backgroundView.frame.size.height / 2.0f - 5.0f);
_avatarView.transform = CGAffineTransformMakeScale(0.64f, 0.64f);
[_smallView addSubview:_avatarView];
_avatarView.center = CGPointMake(_smallView.frame.size.width / 2.0f, _smallView.frame.size.height / 2.0f);
_smallView.hidden = false;
_smallView.transform = CGAffineTransformMakeScale(0.01f, 0.01f);
_shadowView.center = CGPointMake(_shadowView.center.x, _shadowView.center.y + _shadowView.frame.size.height / 2.0f);
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 1.0f);
[UIView animateWithDuration:0.35 delay:0.0 usingSpringWithDamping:0.6f initialSpringVelocity:0.5f options:kNilOptions animations:^
{
_smallView.transform = CGAffineTransformIdentity;
_shadowView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
if (_dotView.hidden)
_smallView.alpha = 1.0f;
} completion:^(BOOL finished)
{
_animating = false;
_shadowView.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
_shadowView.hidden = true;
_shadowView.transform = CGAffineTransformIdentity;
[avatarSnapshot removeFromSuperview];
[self addSubview:_avatarView];
}];
[UIView animateWithDuration:0.1 animations:^
{
_dotView.alpha = 0.0f;
} completion:nil];
}
}
else
{
_smallView.hidden = selected;
_shadowView.hidden = !selected;
_dotView.alpha = selected ? 1.0f : 0.0f;
_smallView.alpha = 1.0f;
[self layoutSubviews];
}
[self updateHeading];
}
- (void)setPallete:(TGLocationPallete *)pallete
{
if (pallete == nil || _pallete == pallete)
return;
_pallete = pallete;
UIImage *dotImage;
if ([self.annotation isKindOfClass:[TGLocationAnnotation class]])
{
TGLocationAnnotation *locationAnnotation = (TGLocationAnnotation *)self.annotation;
if (locationAnnotation.location.period > 0) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1.0f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(2, 2, 12.0, 12.0));
CGContextRestoreGState(context);
CGContextSetFillColorWithColor(context, pallete.locationColor.CGColor);
CGContextFillEllipseInRect(context, CGRectMake(4.0f, 4.0f, 8.0f, 8.0f));
dotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
}
if (dotImage == nil) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, pallete.locationColor.CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
dotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
_dotView.image = dotImage;
[self setAnnotation:self.annotation];
}
- (void)setAnnotation:(id<MKAnnotation>)annotation
{
[super setAnnotation:annotation];
if ([annotation isKindOfClass:[TGLocationPickerAnnotation class]])
{
_avatarView.hidden = false;
_avatarView.alpha = 1.0f;
_iconView.hidden = true;
_dotView.hidden = true;
_backgroundView.image = TGComponentsImageNamed(@"LocationPinBackground");
_liveLocation = false;
[self setPeer:((TGLocationPickerAnnotation *)annotation).peer];
[self unsubscribeFromExpiration];
}
else if ([annotation isKindOfClass:[TGLocationAnnotation class]])
{
TGLocationAnnotation *locationAnnotation = ((TGLocationAnnotation *)annotation);
TGLocationMediaAttachment *location = locationAnnotation.location;
if (location.period == 0)
{
_dotView.hidden = false;
_avatarView.hidden = true;
_avatarView.alpha = 1.0f;
_iconView.hidden = false;
UIColor *color = _pallete != nil ? _pallete.locationColor : UIColorRGB(0x008df2);
UIColor *pinColor = _pallete != nil ? _pallete.iconColor : [UIColor whiteColor];
if (locationAnnotation.color != nil) {
color = locationAnnotation.color;
pinColor = [UIColor whiteColor];
}
_backgroundView.image = TGTintedImage(TGComponentsImageNamed(@"LocationPinBackground"), color);
if (location.venue.type.length > 0)
{
[_iconView loadUri:[NSString stringWithFormat:@"location-venue-icon://type=%@&width=%d&height=%d&color=%d", location.venue.type, 64, 64, TGColorHexCode(pinColor)] withOptions:nil];
}
else
{
[_iconView reset];
UIImage *image = TGComponentsImageNamed(@"LocationPinIcon");
if (_pallete != nil)
image = TGTintedImage(image, _pallete.iconColor);
_iconView.image = image;
}
_liveLocation = false;
[self unsubscribeFromExpiration];
}
else
{
_avatarView.hidden = false;
_avatarView.alpha = locationAnnotation.isExpired ? 0.5f : 1.0f;
_iconView.hidden = true;
_backgroundView.image = TGComponentsImageNamed(@"LocationPinBackground");
[self setPeer:locationAnnotation.peer];
if (!locationAnnotation.isOwn)
{
if (!locationAnnotation.isExpired)
[_pulseView start];
_dotView.hidden = false;
}
else
{
_dotView.hidden = true;
}
[self subscribeForExpiration];
_liveLocation = true;
if (!self.selected)
{
_shadowView.hidden = true;
_smallView.hidden = false;
}
}
}
}
- (void)updateHeading
{
if ([self.annotation isKindOfClass:[TGLocationAnnotation class]]) {
NSNumber *heading = ((TGLocationAnnotation *)self.annotation).heading;
if (heading != nil) {
_arrowView.hidden = self.isSelected;
_smallArrowView.hidden = !self.isSelected;
_arrowView.transform = CGAffineTransformMakeRotation(heading.floatValue);
_smallArrowView.transform = CGAffineTransformMakeRotation(heading.floatValue);
} else {
_arrowView.hidden = true;
_smallArrowView.hidden = true;
}
} else {
_arrowView.hidden = true;
_smallArrowView.hidden = true;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"annotation.isExpired"])
{
if (((TGLocationAnnotation *)self.annotation).isExpired)
{
[_pulseView stop];
_avatarView.alpha = 0.5f;
}
else
{
[_pulseView start];
_avatarView.alpha = 1.0f;
}
}
else if ([keyPath isEqual:@"annotation.heading"])
{
[self updateHeading];
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)setPeer:(id)peer
{
CGFloat diameter = 55.0f;
static UIImage *placeholder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
//!placeholder
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextSetStrokeColorWithColor(context, UIColorRGB(0xd9d9d9).CGColor);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, diameter - 1.0f, diameter - 1.0f));
placeholder = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
if (_avatarView == nil)
{
_avatarView = [[TGLetteredAvatarView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 55.0f, 55.0f)];
[_avatarView setSingleFontSize:24.0f doubleFontSize:24.0f useBoldFont:false];
[self addSubview:_avatarView];
}
else
{
[_avatarView.superview bringSubviewToFront:_avatarView];
}
bool isUser = [peer isKindOfClass:[TGUser class]];
NSString *avatarUrl = isUser ? ((TGUser *)peer).photoFullUrlSmall : ((TGConversation *)peer).chatPhotoFullSmall;
if (avatarUrl.length != 0)
{
_avatarView.fadeTransitionDuration = 0.3;
if (![avatarUrl isEqualToString:_avatarView.currentUrl])
[_avatarView loadImage:avatarUrl filter:@"circle:55x55" placeholder:placeholder];
}
else
{
if (isUser)
{
[_avatarView loadUserPlaceholderWithSize:CGSizeMake(diameter, diameter) uid:((TGUser *)peer).uid firstName:((TGUser *)peer).firstName lastName:((TGUser *)peer).lastName placeholder:placeholder];
}
else
{
[_avatarView loadGroupPlaceholderWithSize:CGSizeMake(diameter, diameter) conversationId:((TGConversation *)peer).conversationId title:((TGConversation *)peer).chatTitle placeholder:placeholder];
}
}
}
#pragma mark - Layout
- (void)layoutSubviews
{
[super layoutSubviews];
if (_animating)
return;
_dotView.center = CGPointZero;
_smallView.center = CGPointZero;
_arrowView.center = CGPointZero;
_smallArrowView.center = CGPointZero;
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
_backgroundView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f);
_iconView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f - 5.0f);
if (_liveLocation)
{
if (self.selected)
{
_avatarView.center = CGPointMake(TGScreenPixel, -41.0f);
_avatarView.transform = CGAffineTransformIdentity;
}
else
{
_avatarView.center = CGPointZero;
_avatarView.transform = CGAffineTransformMakeScale(0.64f, 0.64f);
}
}
else
{
_avatarView.center = CGPointMake(TGScreenPixel, -41.0f);
_avatarView.transform = CGAffineTransformIdentity;
}
}
- (void)setPinRaised:(bool)raised
{
[self setPinRaised:raised avatar:false animated:false completion:nil];
}
- (void)setPinRaised:(bool)raised avatar:(bool)avatar animated:(bool)animated completion:(void (^)(void))completion
{
_pinRaised = raised;
avatar = false;
[_shadowView.layer removeAllAnimations];
if (iosMajorVersion() < 7)
animated = false;
if (animated)
{
if (raised)
{
[UIView animateWithDuration:0.2 delay:0.0 options:7 << 16 | UIViewAnimationOptionAllowAnimatedContent animations:^
{
_shadowView.center = CGPointMake(TGScreenPixel, -66.0f);
if (avatar)
_avatarView.center = CGPointMake(TGScreenPixel, -71.0f);
} completion:^(BOOL finished) {
if (finished && completion != nil)
completion();
}];
}
else
{
[UIView animateWithDuration:0.2 delay:0.0 usingSpringWithDamping:0.6 initialSpringVelocity:0.0 options:UIViewAnimationOptionAllowAnimatedContent animations:^
{
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
if (avatar)
_avatarView.center = CGPointMake(TGScreenPixel, -41.0f);
} completion:^(BOOL finished)
{
if (finished && completion != nil)
completion();
}];
}
}
else
{
_shadowView.center = CGPointMake(TGScreenPixel, raised ? -66.0f : -36.0f);
if (avatar)
_avatarView.center = CGPointMake(TGScreenPixel, raised ? -71.0 : -41.0f);
if (completion != nil)
completion();
}
}
- (void)setCustomPin:(bool)customPin animated:(bool)animated
{
if (animated)
{
_animating = true;
UIImage *image = TGComponentsImageNamed(@"LocationPinIcon");
if (_pallete != nil)
image = TGTintedImage(image, _pallete.iconColor);
_iconView.image = image;
[_backgroundView addSubview:_avatarView];
_avatarView.center = CGPointMake(_backgroundView.frame.size.width / 2.0f, _backgroundView.frame.size.height / 2.0f - 5.0f);
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
_backgroundView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f);
_iconView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f - 5.0f);
TGDispatchAfter(0.01, dispatch_get_main_queue(), ^
{
[UIView transitionWithView:_backgroundView duration:0.2 options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent animations:^
{
_backgroundView.image = customPin ? TGTintedImage(TGComponentsImageNamed(@"LocationPinBackground"), _pallete != nil ? _pallete.locationColor : UIColorRGB(0x008df2)) : TGComponentsImageNamed(@"LocationPinBackground");
_avatarView.hidden = customPin;
_iconView.hidden = !customPin;
} completion:^(BOOL finished)
{
if (!customPin)
[self addSubview:_avatarView];
_animating = false;
[self setNeedsLayout];
}];
});
[self setNeedsLayout];
}
else
{
}
_dotView.hidden = !customPin;
}
@end
@implementation TGLocationPinWrapperView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *view = [super hitTest:point withEvent:event];
if (view == self)
return nil;
return view;
}
@end