GLEGram 12.5 — Initial public release

Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
This commit is contained in:
Leeksov
2026-04-06 09:48:12 +03:00
commit 4647310322
39685 changed files with 11052678 additions and 0 deletions
@@ -0,0 +1,932 @@
#import <LegacyComponents/TGCameraZoomView.h>
#import <LegacyComponents/TGCameraInterfaceAssets.h>
#import <LegacyComponents/TGModernButton.h>
#import <LegacyComponents/TGImageUtils.h>
#import <LegacyComponents/TGPhotoEditorUtils.h>
#import "LegacyComponentsInternal.h"
@interface TGCameraZoomView ()
{
UIView *_clipView;
UIView *_wrapperView;
UIView *_minusIconView;
UIView *_plusIconView;
UIView *_leftLine;
UIView *_rightLine;
UIImageView *_knobView;
}
@end
@implementation TGCameraZoomView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.userInteractionEnabled = false;
_clipView = [[UIView alloc] init];
_clipView.clipsToBounds = true;
[self addSubview:_clipView];
_wrapperView = [[UIView alloc] initWithFrame:self.bounds];
[_clipView addSubview:_wrapperView];
_leftLine = [[UIView alloc] initWithFrame:CGRectMake(-1000, (12.5f - 1.5f) / 2, 1000, 1.5f)];
_leftLine.backgroundColor = [TGCameraInterfaceAssets normalColor];
[_wrapperView addSubview:_leftLine];
_rightLine = [[UIView alloc] initWithFrame:CGRectMake(12.5f, (12.5 - 1.5f) / 2, 1000, 1.5f)];
_rightLine.backgroundColor = [TGCameraInterfaceAssets normalColor];
[_wrapperView addSubview:_rightLine];
static UIImage *knobImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(12.5f, 12.5f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [TGCameraInterfaceAssets accentColor].CGColor);
CGContextSetLineWidth(context, 1.0f);
CGContextStrokeEllipseInRect(context, CGRectMake(0.75f, 0.75f, 12.5f - 1.5f, 12.5f - 1.5f));
knobImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
_knobView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 12.5f, 12.5f)];
_knobView.image = knobImage;
[_wrapperView addSubview:_knobView];
_minusIconView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 9.5f, 1.5f)];
_minusIconView.backgroundColor = [TGCameraInterfaceAssets normalColor];
_minusIconView.layer.cornerRadius = 1;
[self addSubview:_minusIconView];
_plusIconView = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width - 9.5f, 0, 9.5f, 1.5f)];
_plusIconView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
_plusIconView.backgroundColor = [TGCameraInterfaceAssets normalColor];
_plusIconView.layer.cornerRadius = 1;
[self addSubview:_plusIconView];
CALayer *plusVertLayer = [[CALayer alloc] init];
plusVertLayer.backgroundColor = [TGCameraInterfaceAssets normalColor].CGColor;
plusVertLayer.cornerRadius = 1;
plusVertLayer.frame = CGRectMake((9.5f - 1.5f) / 2, -(9.5f - 1.5f) / 2, 1.5f, 9.5f);
[_plusIconView.layer addSublayer:plusVertLayer];
[self hideAnimated:false];
}
return self;
}
- (void)setZoomLevel:(CGFloat)zoomLevel
{
[self setZoomLevel:zoomLevel displayNeeded:true];
}
- (void)setZoomLevel:(CGFloat)zoomLevel displayNeeded:(bool)displayNeeded
{
_zoomLevel = zoomLevel;
[self setNeedsLayout];
if (displayNeeded)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideAnimated) object:nil];
if (self.alpha < FLT_EPSILON)
[self showAnimated:true];
}
}
- (bool)isActive
{
return (self.alpha > FLT_EPSILON);
}
- (void)showAnimated:(bool)animated
{
if (self.activityChanged != nil)
self.activityChanged(true);
if (animated)
{
[UIView animateWithDuration:0.3f animations:^
{
self.alpha = 1.0f;
}];
}
else
{
self.alpha = 1.0f;
}
}
- (void)hideAnimated:(bool)animated
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideAnimated) object:nil];
if (animated)
{
[UIView animateWithDuration:0.3f animations:^
{
self.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
if (finished)
{
if (self.activityChanged != nil)
self.activityChanged(false);
}
}];
}
else
{
self.alpha = 0.0f;
if (self.activityChanged != nil)
self.activityChanged(false);
}
}
- (void)hideAnimated
{
[self hideAnimated:true];
}
- (void)interactionEnded
{
[self performSelector:@selector(hideAnimated) withObject:nil afterDelay:4.0f];
}
- (void)layoutSubviews
{
_clipView.frame = CGRectMake(22, (self.frame.size.height - 12.5f) / 2, self.frame.size.width - 44, 12.5f);
CGFloat zoomLevel = self.zoomLevel;
zoomLevel = MAX(1.0, zoomLevel);
CGFloat factor = zoomLevel / 8.0;
CGFloat position = (_clipView.frame.size.width - _knobView.frame.size.width) * factor;
if (self.zoomLevel < 1.0f - FLT_EPSILON)
position = CGFloor(position);
_wrapperView.frame = CGRectMake(position, 0, _wrapperView.frame.size.width, _wrapperView.frame.size.height);
}
@end
@interface TGCameraZoomModeItemView: TGModernButton
{
UIImageView *_backgroundView;
UILabel *_label;
}
@end
@implementation TGCameraZoomModeItemView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self != nil) {
_backgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(3, 3, 37, 37)];
_backgroundView.image = TGCircleImage(37, [UIColor colorWithWhite:0.0 alpha:0.4]);
_label = [[UILabel alloc] initWithFrame:self.bounds];
_label.textAlignment = NSTextAlignmentCenter;
_label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0];
[self addSubview:_backgroundView];
[self addSubview:_label];
}
return self;
}
- (void)setValue:(NSString *)value selected:(bool)selected animated:(bool)animated {
CGFloat scale = selected ? 1.0 : 0.7;
CGFloat textScale = selected ? 1.0 : 0.85;
_label.text = value;
_label.textColor = selected ? [TGCameraInterfaceAssets accentColor] : [UIColor whiteColor];
if (animated) {
[UIView animateWithDuration:0.3f animations:^
{
_backgroundView.transform = CGAffineTransformMakeScale(scale, scale);
_label.transform = CGAffineTransformMakeScale(textScale, textScale);
}];
} else {
_backgroundView.transform = CGAffineTransformMakeScale(scale, scale);
_label.transform = CGAffineTransformMakeScale(textScale, textScale);
}
}
@end
@interface TGCameraZoomModeView () <UIGestureRecognizerDelegate>
{
NSArray<NSNumber *> *_zoomLevels;
CGFloat _minZoomLevel;
CGFloat _maxZoomLevel;
UIView *_backgroundView;
NSMutableArray<TGCameraZoomModeItemView *> *_zoomItems;
bool _beganFromPress;
bool _lockedOn;
}
@end
@implementation TGCameraZoomModeView
- (instancetype)initWithFrame:(CGRect)frame zoomLevels:(NSArray<NSNumber *> *)zoomLevels minZoomLevel:(CGFloat)minZoomLevel maxZoomLevel:(CGFloat)maxZoomLevel
{
self = [super initWithFrame:frame];
if (self != nil)
{
_zoomLevels = [zoomLevels copy];
_minZoomLevel = minZoomLevel;
_maxZoomLevel = maxZoomLevel;
_backgroundView = [[UIView alloc] initWithFrame:self.bounds];
_backgroundView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.15];
_backgroundView.layer.cornerRadius = self.bounds.size.height / 2.0;
[self addSubview:_backgroundView];
_zoomItems = [NSMutableArray array];
CGFloat itemWidth = 43.0;
for (NSInteger i = 0; i < zoomLevels.count; i++) {
TGCameraZoomModeItemView *item = [[TGCameraZoomModeItemView alloc] initWithFrame:CGRectMake(i * itemWidth, 0, itemWidth, itemWidth)];
[item addTarget:self action:@selector(itemPressed:) forControlEvents:UIControlEventTouchUpInside];
item.tag = i;
[_zoomItems addObject:item];
[self addSubview:item];
}
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
panGestureRecognizer.delegate = self;
[self addGestureRecognizer:panGestureRecognizer];
UILongPressGestureRecognizer *pressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(pressGesture:)];
pressGestureRecognizer.delegate = self;
[self addGestureRecognizer:pressGestureRecognizer];
NSUInteger defaultIndex = [_zoomLevels indexOfObject:@(1.0)];
if (defaultIndex == NSNotFound) {
defaultIndex = 0;
}
_zoomLevel = [_zoomLevels[defaultIndex] floatValue];
[self setZoomLevel:_zoomLevel animated:NO];
}
return self;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if (gestureRecognizer.view == self && otherGestureRecognizer.view == self) {
return true;
} else {
return false;
}
}
- (void)pressGesture:(UILongPressGestureRecognizer *)gestureRecognizer {
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
_beganFromPress = true;
self.zoomChanged(_zoomLevel, false, false);
break;
case UIGestureRecognizerStateEnded:
self.zoomChanged(_zoomLevel, true, false);
break;
case UIGestureRecognizerStateCancelled:
self.zoomChanged(_zoomLevel, true, false);
break;
default:
break;
}
}
- (void)panGesture:(UIPanGestureRecognizer *)gestureRecognizer {
CGPoint translation = [gestureRecognizer translationInView:self];
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateChanged:
{
if (_lockedOn) {
if (ABS(translation.x) > 8.0) {
_lockedOn = false;
[gestureRecognizer setTranslation:CGPointZero inView:self];
CGFloat delta = translation.x > 0 ? -0.06 : 0.06;
CGFloat newLevel = MAX(_minZoomLevel, MIN(_maxZoomLevel, _zoomLevel + delta));
_zoomLevel = newLevel;
self.zoomChanged(newLevel, false, false);
return;
} else {
return;
}
}
CGFloat previousLevel = _zoomLevel;
CGFloat delta = -translation.x / 60.0;
if (_zoomLevel > 2.0) {
delta *= 3.5;
}
CGFloat newLevel = MAX(_minZoomLevel, MIN(_maxZoomLevel, _zoomLevel + delta));
for (NSNumber *level in _zoomLevels) {
CGFloat zoomValue = [level floatValue];
if (ABS(newLevel - zoomValue) < 0.05 && previousLevel != zoomValue && translation.x < 15.0) {
newLevel = zoomValue;
_lockedOn = true;
[gestureRecognizer setTranslation:CGPointZero inView:self];
break;
}
}
_zoomLevel = newLevel;
self.zoomChanged(newLevel, false, false);
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
if (gestureRecognizer.view != self || !_beganFromPress) {
self.zoomChanged(_zoomLevel, true, false);
}
_beganFromPress = false;
}
break;
default:
break;
}
if (!_lockedOn) {
[gestureRecognizer setTranslation:CGPointZero inView:self];
}
}
- (void)itemPressed:(TGCameraZoomModeItemView *)sender {
NSInteger index = sender.tag;
if (index >= 0 && index < _zoomLevels.count) {
CGFloat newZoomLevel = [_zoomLevels[index] floatValue];
if (_zoomLevel != newZoomLevel) {
[self setZoomLevel:newZoomLevel animated:true];
self.zoomChanged(newZoomLevel, true, true);
}
}
}
- (void)setZoomLevel:(CGFloat)zoomLevel {
[self setZoomLevel:zoomLevel animated:false];
}
- (void)setZoomLevel:(CGFloat)zoomLevel animated:(bool)animated
{
_zoomLevel = zoomLevel;
NSInteger selectedIndex = -1;
CGFloat closestDistance = MAXFLOAT;
CGFloat nextLevelValue = 8.0;
for (NSInteger i = 0; i < _zoomLevels.count; i++) {
CGFloat levelValue = [_zoomLevels[i] floatValue];
CGFloat distance = ABS(levelValue - zoomLevel);
if (distance < 0.025) {
selectedIndex = i;
break;
} else {
if (zoomLevel >= levelValue) {
selectedIndex = i;
closestDistance = distance;
} else {
break;
}
}
}
if (selectedIndex < _zoomLevels.count - 1) {
nextLevelValue = [_zoomLevels[selectedIndex + 1] floatValue];
} else {
nextLevelValue = 16.0;
}
for (NSInteger i = 0; i < _zoomItems.count; i++) {
TGCameraZoomModeItemView *item = _zoomItems[i];
NSNumber *levelNumber = _zoomLevels[i];
CGFloat level = [levelNumber floatValue];
NSString *value;
if (level == (NSInteger)level && level >= 1.0) {
value = [NSString stringWithFormat:@"%d", (int)level];
} else {
value = [NSString stringWithFormat:@"%.1f", level];
value = [value stringByReplacingOccurrencesOfString:@"," withString:@"."];
value = [value stringByReplacingOccurrencesOfString:@".0" withString:@""];
}
bool selected = (i == selectedIndex);
if (i == selectedIndex && closestDistance < MAXFLOAT && closestDistance > 0.025) {
NSString *currentValue = [NSString stringWithFormat:@"%.1f", zoomLevel];
currentValue = [currentValue stringByReplacingOccurrencesOfString:@"," withString:@"."];
currentValue = [currentValue stringByReplacingOccurrencesOfString:@".0" withString:@""];
if ([currentValue isEqualToString:[NSString stringWithFormat:@"%d", (int)nextLevelValue]]) {
currentValue = [NSString stringWithFormat:@"%.1f", nextLevelValue - 0.1];
}
value = currentValue;
}
if (selected) {
value = [NSString stringWithFormat:@"%@×", value];
}
[item setValue:value selected:selected animated:animated];
}
}
- (void)setHidden:(BOOL)hidden
{
self.alpha = hidden ? 0.0f : 1.0f;
super.hidden = hidden;
}
- (void)setHidden:(bool)hidden animated:(bool)animated
{
if (animated)
{
super.hidden = false;
self.userInteractionEnabled = false;
[UIView animateWithDuration:0.25f animations:^
{
self.alpha = hidden ? 0.0f : 1.0f;
} completion:^(BOOL finished)
{
self.userInteractionEnabled = true;
if (finished)
self.hidden = hidden;
}];
}
else
{
self.alpha = hidden ? 0.0f : 1.0f;
super.hidden = hidden;
}
}
- (void)layoutSubviews
{
CGFloat itemWidth = 43.0;
CGFloat totalWidth = itemWidth * _zoomLevels.count;
_backgroundView.frame = CGRectMake(TGScreenPixelFloor((172.0f - totalWidth) / 2.0), 0, totalWidth, 43);
for (NSInteger i = 0; i < _zoomItems.count; i++) {
TGCameraZoomModeItemView *item = _zoomItems[i];
item.frame = CGRectMake(_backgroundView.frame.origin.x + i * itemWidth, 0, itemWidth, 43);
}
}
- (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
_interfaceOrientation = interfaceOrientation;
for (TGCameraZoomModeItemView *item in _zoomItems) {
item.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(interfaceOrientation));
}
}
@end
@interface TGCameraZoomWheelView ()
{
bool _hasUltrawideCamera;
bool _hasTelephotoCamera;
UIView *_containerView;
UIImageView *_backgroundView;
UIImageView *_scaleView;
UIImageView *_maskView;
UIImageView *_arrowView;
UILabel *_valueLabel;
UILabel *_05Label;
UILabel *_1Label;
UILabel *_2Label;
UILabel *_8Label;
UIPanGestureRecognizer *_gestureRecognizer;
UISelectionFeedbackGenerator *_feedbackGenerator;
}
@end
@implementation TGCameraZoomWheelView
- (void)_drawLineInContext:(CGContextRef)context side:(CGFloat)side atAngle:(CGFloat)angle lineLength:(CGFloat)lineLength lineWidth:(CGFloat)lineWidth opaque:(bool)opaque {
CGContextSaveGState(context);
CGContextTranslateCTM(context, side / 2.0, side / 2.0);
CGContextRotateCTM(context, angle);
CGContextTranslateCTM(context, -side / 2.0, -side / 2.0);
CGContextSetLineWidth(context, lineWidth);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:1.0 alpha:opaque ? 1.0 : 0.5].CGColor);
CGContextMoveToPoint(context, side / 2.0, 4.0);
CGContextAddLineToPoint(context, side / 2.0, 4.0 + lineLength);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
- (NSArray *)ultraLines {
return @[
@[@0.5, @-19.6, @3],
@[@0.6, @-14.4, @1],
@[@0.7, @-10.0, @1],
@[@0.8, @-6.3, @1],
@[@0.9, @-3.0, @1]
];
}
- (NSArray *)lines {
return @[
@[@1.0, @0.0, @3],
@[@1.1, @2.7, @1],
@[@1.2, @5.2, @1],
@[@1.3, @7.4, @1],
@[@1.4, @9.6, @1],
@[@1.5, @11.5, @1],
@[@1.6, @13.3, @1],
@[@1.7, @15.0, @1],
@[@1.8, @16.7, @1],
@[@1.9, @18.2, @1],
@[@2.0, @19.6, _hasTelephotoCamera ? @3 : @2],
@[@2.1, @21.0, @1],
@[@2.2, @22.4, @1],
@[@2.3, @23.7, @1],
@[@2.4, @24.8, @1],
@[@2.5, @26.0, @1],
@[@2.6, @27.1, @1],
@[@2.7, @28.2, @1],
@[@2.8, @29.2, @1],
@[@2.9, @30.2, @1],
@[@3.0, @31.1, @2],
@[@3.1, @32.0, @1],
@[@3.2, @32.9, @1],
@[@3.3, @33.8, @1],
@[@3.4, @34.7, @1],
@[@3.5, @35.5, @1],
@[@3.6, @36.34, @1],
@[@3.7, @37.1, @1],
@[@3.8, @37.85, @1],
@[@3.9, @38.55, @1],
@[@4.0, @39.3, @2],
@[@4.1, @40.0, @1],
@[@4.2, @40.77, @1],
@[@4.3, @41.4, @1],
@[@4.4, @42.05, @1],
@[@4.5, @42.63, @1],
@[@4.6, @43.3, @1],
@[@4.7, @43.89, @1],
@[@4.8, @44.42, @1],
@[@4.9, @45.05, @1],
@[@5.0, @45.6, @2],
@[@5.1, @46.17, @1],
@[@5.2, @46.77, @1],
@[@5.3, @47.31, @1],
@[@5.4, @47.78, @1],
@[@5.5, @48.34, @1],
@[@5.6, @48.8, @1],
@[@5.7, @49.31, @1],
@[@5.8, @49.85, @1],
@[@5.9, @50.3, @1],
@[@6.0, @50.8, @2],
@[@6.1, @51.25, @1],
@[@6.2, @51.7, @1],
@[@6.3, @52.18, @1],
@[@6.4, @52.63, @1],
@[@6.5, @53.12, @1],
@[@6.6, @53.49, @1],
@[@6.7, @53.88, @1],
@[@6.8, @54.28, @1],
@[@6.9, @54.71, @1],
@[@7.0, @55.15, @2],
@[@7.1, @55.53, @1],
@[@7.2, @55.91, @1],
@[@7.3, @56.36, @1],
@[@7.4, @56.74, @1],
@[@7.5, @57.09, @1],
@[@7.6, @57.52, @1],
@[@7.7, @57.89, @1],
@[@7.8, @58.19, @1],
@[@7.9, @58.56, @1],
@[@8.0, @58.93, @3],
];
}
- (instancetype)initWithFrame:(CGRect)frame hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera
{
self = [super initWithFrame:frame];
if (self != nil)
{
TGIsRetina();
if (iosMajorVersion() >= 10) {
_feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init];
}
_hasUltrawideCamera = hasUltrawideCamera;
_hasTelephotoCamera = hasTelephotoCamera;
CGFloat side = floor(frame.size.width * 1.1435);
CGFloat length = 17.0;
CGFloat smallWidth = MAX(0.5, 1.0 - TGScreenPixel);
CGFloat mediumWidth = smallWidth;
CGFloat bigWidth = 1.0;
_backgroundView = [[UIImageView alloc] initWithImage:TGCircleImage(side, [UIColor colorWithWhite:0.0 alpha:0.5])];
_backgroundView.frame = CGRectMake(TGScreenPixelFloor((frame.size.width - side) / 2.0), 0.0, side, side);
[self addSubview:_backgroundView];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, side), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
NSArray *ultraLines = [self ultraLines];
NSArray *lines = [self lines];
if (_hasUltrawideCamera) {
for (NSArray *values in ultraLines) {
CGFloat angle = [values[1] floatValue];
CGFloat width = [values[2] intValue];
CGFloat lineWidth = smallWidth;
if (width == 2) {
lineWidth = mediumWidth;
} else if (width == 3) {
lineWidth = bigWidth;
}
[self _drawLineInContext:context side:side atAngle:TGDegreesToRadians(angle) lineLength:length lineWidth:lineWidth opaque:width > 1];
}
}
for (NSArray *values in lines) {
CGFloat angle = [values[1] floatValue];
CGFloat width = [values[2] intValue];
CGFloat lineWidth = smallWidth;
if (width == 2) {
lineWidth = mediumWidth;
} else if (width == 3) {
lineWidth = bigWidth;
}
[self _drawLineInContext:context side:side atAngle:TGDegreesToRadians(angle) lineLength:length lineWidth:lineWidth opaque:width > 1];
}
UIImage *scaleImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25];
UIGraphicsEndImageContext();
_containerView = [[UIView alloc] initWithFrame:CGRectMake(TGScreenPixelFloor((frame.size.width - side) / 2.0), 0.0, side, frame.size.height)];
_containerView.userInteractionEnabled = false;
[self addSubview:_containerView];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, frame.size.height), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0, 0, side, side));
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextMoveToPoint(context, side / 2.0 - 7.0, 0);
CGContextAddLineToPoint(context, side / 2.0 + 7.0, 0);
CGContextAddLineToPoint(context, side / 2.0 + 2.0, 22);
CGContextAddLineToPoint(context, side / 2.0 - 2.0, 22);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextFillRect(context, CGRectMake(side / 2.0 - 1.0, 20, 2.0, 7.0));
CGContextFillEllipseInRect(context, CGRectMake(side / 2.0 - 17.0, 21.0, 34.0, 34.0));
UIImage *maskImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25];
UIGraphicsEndImageContext();
_maskView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, side, frame.size.height)];
_maskView.image = maskImage;
_containerView.maskView = _maskView;
_scaleView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, side, side)];
_scaleView.image = scaleImage;
[_containerView addSubview:_scaleView];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(4, 10), false, 0.0f);
context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [TGCameraInterfaceAssets accentColor].CGColor);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 4, 0);
CGContextAddLineToPoint(context, 2 + TGScreenPixel, 10);
CGContextAddLineToPoint(context, 2 - TGScreenPixel, 10);
CGContextClosePath(context);
CGContextFillPath(context);
UIImage *arrowImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_arrowView = [[UIImageView alloc] initWithFrame:CGRectMake(floor((frame.size.width - 4) / 2.0), 4, 4, 10)];
_arrowView.image = arrowImage;
_arrowView.userInteractionEnabled = false;
[self addSubview:_arrowView];
_valueLabel = [[UILabel alloc] init];
_valueLabel.font = [TGCameraInterfaceAssets boldFontOfSize:13.0];
_valueLabel.textColor = [TGCameraInterfaceAssets accentColor];
_valueLabel.userInteractionEnabled = false;
[self addSubview:_valueLabel];
CGFloat radius = side / 2.0;
if (_hasUltrawideCamera) {
_05Label = [[UILabel alloc] init];
_05Label.text = @"0,5";
_05Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0];
_05Label.textColor = [UIColor whiteColor];
[_05Label sizeToFit];
[_scaleView addSubview:_05Label];
_05Label.center = CGPointMake(radius - sin(TGDegreesToRadians(19.6)) * (radius - 38.0), radius - cos(TGDegreesToRadians(19.6)) * (radius - 38.0));
_05Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(-19.6));
}
_1Label = [[UILabel alloc] init];
_1Label.text = @"1";
_1Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0];
_1Label.textColor = [UIColor whiteColor];
[_1Label sizeToFit];
_1Label.frame = CGRectMake(TGScreenPixelFloor((_scaleView.bounds.size.width - _1Label.frame.size.width) / 2.0), 30.0, _1Label.frame.size.width, _1Label.frame.size.height);
[_scaleView addSubview:_1Label];
if (_hasTelephotoCamera) {
_2Label = [[UILabel alloc] init];
_2Label.text = @"2";
_2Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0];
_2Label.textColor = [UIColor whiteColor];
[_2Label sizeToFit];
[_scaleView addSubview:_2Label];
_2Label.center = CGPointMake(radius - sin(TGDegreesToRadians(-19.6)) * (radius - 38.0), radius - cos(TGDegreesToRadians(-19.6)) * (radius - 38.0));
_2Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(19.6));
}
_8Label = [[UILabel alloc] init];
_8Label.text = @"8";
_8Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0];
_8Label.textColor = [UIColor whiteColor];
[_8Label sizeToFit];
[_scaleView addSubview:_8Label];
_8Label.center = CGPointMake(radius - sin(TGDegreesToRadians(-58.93)) * (radius - 38.0), radius - cos(TGDegreesToRadians(-58.93)) * (radius - 38.0));
_8Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(58.93));
_gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
[self addGestureRecognizer:_gestureRecognizer];
}
return self;
}
- (void)panGesture:(UIPanGestureRecognizer *)gestureRecognizer {
if (self.panGesture != nil) {
self.panGesture(gestureRecognizer);
}
}
- (void)setZoomLevel:(CGFloat)zoomLevel {
[self setZoomLevel:zoomLevel panning:false];
}
- (void)setZoomLevel:(CGFloat)zoomLevel panning:(bool)panning {
zoomLevel = MAX(0.5, zoomLevel);
_zoomLevel = zoomLevel;
NSArray *ultraLines = [self ultraLines];
NSArray *lines = [self lines];
CGFloat finalAngle = 0.0;
NSArray *allLines = [ultraLines arrayByAddingObjectsFromArray:lines];
NSArray *previous = nil;
for (NSArray *values in allLines) {
CGFloat value = [values[0] floatValue];
CGFloat angle = [values[1] floatValue];
if (previous == nil && zoomLevel <= value) {
finalAngle = angle;
break;
}
if (previous != nil && zoomLevel <= value) {
if (zoomLevel == value) {
finalAngle = angle;
break;
} else {
CGFloat previousValue = [previous[0] floatValue];
CGFloat previousAngle = [previous[1] floatValue];
if (zoomLevel > previousValue) {
CGFloat factor = (zoomLevel - previousValue) / (value - previousValue);
finalAngle = previousAngle + (angle - previousAngle) * factor;
break;
}
}
}
previous = values;
}
finalAngle = -TGDegreesToRadians(finalAngle);
_scaleView.transform = CGAffineTransformMakeRotation(finalAngle);
NSString *value = [NSString stringWithFormat:@"%.1f×", zoomLevel];
value = [value stringByReplacingOccurrencesOfString:@"." withString:@","];
value = [value stringByReplacingOccurrencesOfString:@",0×" withString:@"×"];
NSString *previousValue = _valueLabel.text;
_valueLabel.text = value;
[_valueLabel sizeToFit];
if (panning && ![previousValue isEqualToString:value] && ([value isEqualToString:@"0,5×"] || ![value containsString:@","])) {
[_feedbackGenerator selectionChanged];
}
CGRect valueLabelFrame = CGRectMake(TGScreenPixelFloor((self.frame.size.width - _valueLabel.bounds.size.width) / 2.0), 30.0, _valueLabel.bounds.size.width, _valueLabel.bounds.size.height);
_valueLabel.bounds = CGRectMake(0, 0, valueLabelFrame.size.width, valueLabelFrame.size.height);
_valueLabel.center = CGPointMake(valueLabelFrame.origin.x + valueLabelFrame.size.width / 2.0, valueLabelFrame.origin.y + valueLabelFrame.size.height / 2.0);
}
- (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
_interfaceOrientation = interfaceOrientation;
CGFloat delta = 0.0f;
switch (interfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft:
delta = -90.0f;
break;
case UIInterfaceOrientationLandscapeRight:
delta = 90.0f;
break;
case UIInterfaceOrientationPortraitUpsideDown:
delta = 180.0f;
default:
break;
}
_valueLabel.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(delta));
_05Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(-19.6 + delta));
_1Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(delta));
_2Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(19.6 + delta));
_8Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(58.93 + delta));
}
- (void)setHidden:(BOOL)hidden
{
self.alpha = hidden ? 0.0f : 1.0f;
super.hidden = hidden;
}
- (void)setHidden:(bool)hidden animated:(bool)animated
{
if (animated)
{
super.hidden = false;
self.userInteractionEnabled = false;
[UIView animateWithDuration:0.25f animations:^
{
self.alpha = hidden ? 0.0f : 1.0f;
} completion:^(BOOL finished)
{
self.userInteractionEnabled = true;
if (finished)
self.hidden = hidden;
}];
}
else
{
self.alpha = hidden ? 0.0f : 1.0f;
super.hidden = hidden;
}
}
@end