mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
323 lines
9.9 KiB
Objective-C
323 lines
9.9 KiB
Objective-C
#import "TGPhotoCropRotationView.h"
|
|
|
|
#import "TGPhotoEditorUtils.h"
|
|
#import <pop/POP.h>
|
|
|
|
const CGFloat TGPhotoCropRotationViewMaximumAngle = 45;
|
|
|
|
@interface TGPhotoCropRotationView () <UIGestureRecognizerDelegate>
|
|
{
|
|
UIImageView *_wheelView;
|
|
UIImageView *_needleView;
|
|
|
|
UILongPressGestureRecognizer *_pressGestureRecognizer;
|
|
UIPanGestureRecognizer *_panGestureRecognizer;
|
|
|
|
bool _animating;
|
|
bool _beganInteraction;
|
|
bool _endedInteraction;
|
|
|
|
bool _isTracking;
|
|
}
|
|
@end
|
|
|
|
@implementation TGPhotoCropRotationView
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
{
|
|
self = [super initWithFrame:frame];
|
|
if (self != nil)
|
|
{
|
|
self.clipsToBounds = true;
|
|
|
|
_wheelView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)];
|
|
_wheelView.alpha = 0.9f;
|
|
_wheelView.image = [UIImage imageNamed:@"PhotoEditorRotationWheel"];
|
|
[self addSubview:_wheelView];
|
|
|
|
_needleView = [[UIImageView alloc] initWithFrame:CGRectZero];
|
|
_needleView.alpha = 0.9f;
|
|
_needleView.contentMode = UIViewContentModeCenter;
|
|
_needleView.image = [UIImage imageNamed:@"PhotoEditorRotationNeedle"];
|
|
[self addSubview:_needleView];
|
|
|
|
_pressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePress:)];
|
|
_pressGestureRecognizer.minimumPressDuration = 0.1f;
|
|
[self addGestureRecognizer:_pressGestureRecognizer];
|
|
|
|
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
|
_panGestureRecognizer.delegate = self;
|
|
[self addGestureRecognizer:_panGestureRecognizer];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (bool)isTracking
|
|
{
|
|
return _isTracking;
|
|
}
|
|
|
|
- (void)handlePress:(UILongPressGestureRecognizer *)gestureRecognizer
|
|
{
|
|
switch (gestureRecognizer.state)
|
|
{
|
|
case UIGestureRecognizerStateBegan:
|
|
{
|
|
if (_beganInteraction)
|
|
return;
|
|
|
|
_isTracking = true;
|
|
|
|
if (self.didBeginChanging != nil)
|
|
self.didBeginChanging();
|
|
|
|
_endedInteraction = false;
|
|
_beganInteraction = true;
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateEnded:
|
|
case UIGestureRecognizerStateCancelled:
|
|
{
|
|
_beganInteraction = false;
|
|
|
|
if (_endedInteraction)
|
|
return;
|
|
|
|
_isTracking = false;
|
|
|
|
if (self.didEndChanging != nil)
|
|
self.didEndChanging();
|
|
|
|
_endedInteraction = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer
|
|
{
|
|
switch (gestureRecognizer.state)
|
|
{
|
|
case UIGestureRecognizerStateBegan:
|
|
{
|
|
if (_beganInteraction)
|
|
return;
|
|
|
|
_isTracking = true;
|
|
|
|
if (self.didBeginChanging != nil)
|
|
self.didBeginChanging();
|
|
|
|
_endedInteraction = false;
|
|
_beganInteraction = true;
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateChanged:
|
|
{
|
|
CGFloat translation = 0;
|
|
|
|
switch (self.interfaceOrientation)
|
|
{
|
|
case UIInterfaceOrientationLandscapeLeft:
|
|
translation = [gestureRecognizer translationInView:self].y;
|
|
break;
|
|
case UIInterfaceOrientationLandscapeRight:
|
|
translation = [gestureRecognizer translationInView:self].y * -1;
|
|
break;
|
|
|
|
default:
|
|
translation = [gestureRecognizer translationInView:self].x;
|
|
break;
|
|
}
|
|
|
|
CGFloat angleInDegrees = TGRadiansToDegrees(_angle);
|
|
CGFloat newAngleInDegrees = MIN(TGPhotoCropRotationViewMaximumAngle, MAX(-TGPhotoCropRotationViewMaximumAngle, angleInDegrees - translation / (CGFloat)M_PI / 1.15f));
|
|
|
|
if (ABS(newAngleInDegrees - angleInDegrees) > FLT_EPSILON)
|
|
{
|
|
_angle = TGDegreesToRadians(newAngleInDegrees);
|
|
|
|
if (self.angleChanged != nil)
|
|
self.angleChanged(_angle, false);
|
|
|
|
[self setNeedsLayout];
|
|
}
|
|
|
|
[gestureRecognizer setTranslation:CGPointZero inView:self];
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateEnded:
|
|
case UIGestureRecognizerStateCancelled:
|
|
{
|
|
_beganInteraction = false;
|
|
|
|
if (_endedInteraction)
|
|
return;
|
|
|
|
_isTracking = false;
|
|
|
|
if (self.didEndChanging != nil)
|
|
self.didEndChanging();
|
|
|
|
_endedInteraction = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)__unused gestureRecognizer
|
|
{
|
|
bool shouldBegin = true;
|
|
if (self.shouldBeginChanging != nil)
|
|
shouldBegin = self.shouldBeginChanging();
|
|
|
|
return !_animating && shouldBegin;
|
|
}
|
|
|
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)__unused gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)__unused otherGestureRecognizer
|
|
{
|
|
return true;
|
|
}
|
|
|
|
- (void)resetAnimated:(bool)animated
|
|
{
|
|
if (animated)
|
|
{
|
|
_animating = true;
|
|
|
|
POPSpringAnimation *animation = [POPSpringAnimation animation];
|
|
POPAnimatableProperty *angleProperty = [POPAnimatableProperty propertyWithName:@"org.telegram.rotationAngle" initializer:^(POPMutableAnimatableProperty *prop)
|
|
{
|
|
prop.readBlock = ^(id obj, CGFloat values[])
|
|
{
|
|
values[0] = TGRadiansToDegrees([obj angle]);
|
|
};
|
|
prop.writeBlock = ^(id obj, const CGFloat values[])
|
|
{
|
|
[obj setAngle:TGDegreesToRadians(values[0])];
|
|
};
|
|
}];
|
|
animation.property = angleProperty;
|
|
animation.fromValue = @(TGRadiansToDegrees(self.angle));
|
|
animation.toValue = @(0.0f);
|
|
animation.springSpeed = 5;
|
|
animation.springBounciness = 2;
|
|
animation.completionBlock = ^(__unused POPAnimation *animation, __unused BOOL finished)
|
|
{
|
|
_animating = false;
|
|
};
|
|
|
|
[self pop_addAnimation:animation forKey:@"angle"];
|
|
}
|
|
else
|
|
{
|
|
[self setAngle:0.0f];
|
|
}
|
|
}
|
|
|
|
- (void)setAngle:(CGFloat)angle
|
|
{
|
|
_angle = angle;
|
|
[self setNeedsLayout];
|
|
}
|
|
|
|
- (void)setAngle:(CGFloat)angle animated:(bool)animated
|
|
{
|
|
if (ABS(angle - TGRadiansToDegrees(self.angle)) < FLT_EPSILON)
|
|
return;
|
|
|
|
if (animated)
|
|
{
|
|
_animating = true;
|
|
|
|
if (self.didBeginChanging != nil)
|
|
self.didBeginChanging();
|
|
|
|
POPSpringAnimation *animation = [POPSpringAnimation animation];
|
|
POPAnimatableProperty *angleProperty = [POPAnimatableProperty propertyWithName:@"org.telegram.rotationAngle" initializer:^(POPMutableAnimatableProperty *prop)
|
|
{
|
|
prop.readBlock = ^(id obj, CGFloat values[])
|
|
{
|
|
values[0] = TGRadiansToDegrees([obj angle]);
|
|
};
|
|
prop.writeBlock = ^(id obj, const CGFloat values[])
|
|
{
|
|
TGPhotoCropRotationView *view = (TGPhotoCropRotationView *)obj;
|
|
[view setAngle:TGDegreesToRadians(values[0])];
|
|
if (view.angleChanged != nil)
|
|
view.angleChanged(view->_angle, false);
|
|
};
|
|
}];
|
|
animation.property = angleProperty;
|
|
animation.fromValue = @(TGRadiansToDegrees(self.angle));
|
|
animation.toValue = @(angle);
|
|
animation.springSpeed = 5;
|
|
animation.springBounciness = 2;
|
|
animation.completionBlock = ^(__unused POPAnimation *animation, __unused BOOL finished)
|
|
{
|
|
_animating = false;
|
|
|
|
if (self.didEndChanging != nil)
|
|
self.didEndChanging();
|
|
};
|
|
|
|
[self pop_addAnimation:animation forKey:@"angle"];
|
|
}
|
|
else
|
|
{
|
|
[self setAngle:angle];
|
|
}
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
[UIView performWithoutAnimation:^
|
|
{
|
|
switch (self.interfaceOrientation)
|
|
{
|
|
case UIInterfaceOrientationLandscapeLeft:
|
|
{
|
|
_wheelView.image = [UIImage imageNamed:@"PhotoEditorRotationWheelLeft"];
|
|
_wheelView.center = CGPointMake(52 + 200, self.frame.size.height / 2);
|
|
|
|
_needleView.frame = CGRectMake(43, (self.frame.size.height - 10) / 2, 10, 10);
|
|
_needleView.transform = CGAffineTransformMakeRotation((CGFloat)M_PI_2);
|
|
}
|
|
break;
|
|
|
|
case UIInterfaceOrientationLandscapeRight:
|
|
{
|
|
_wheelView.image = [UIImage imageNamed:@"PhotoEditorRotationWheelRight"];
|
|
_wheelView.center = CGPointMake(-152, self.frame.size.height / 2);
|
|
|
|
_needleView.frame = CGRectMake(self.frame.size.width - 53, (self.frame.size.height - 10) / 2, 10, 10);
|
|
_needleView.transform = CGAffineTransformMakeRotation(-(CGFloat)M_PI_2);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
_wheelView.image = [UIImage imageNamed:@"PhotoEditorRotationWheel"];
|
|
_wheelView.center = CGPointMake(self.frame.size.width / 2, -152);
|
|
|
|
_needleView.frame = CGRectMake((self.frame.size.width - 10) / 2, 47, 10, 10);
|
|
_needleView.transform = CGAffineTransformIdentity;
|
|
}
|
|
break;
|
|
}
|
|
}];
|
|
|
|
_wheelView.transform = CGAffineTransformMakeRotation(_angle);
|
|
}
|
|
|
|
@end
|