2025-06-18 17:24:21 +02:00

933 lines
31 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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