mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Initial implementation
This commit is contained in:
parent
853febcb28
commit
57ac20c121
1
LockWait.json
Normal file
1
LockWait.json
Normal file
@ -0,0 +1 @@
|
||||
{"v":"5.5.9","fr":60,"ip":0,"op":120,"w":240,"h":360,"nm":"Lock1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[120,282,0],"to":[0,-1.667,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[120,272,0],"to":[0,-0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[120,277,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[120,272,0],"to":[0,0.833,0],"ti":[0,-1.667,0]},{"t":120,"s":[120,282,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,150,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.96862745098,0.96862745098,0.96862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":75,"s":[0]},{"t":120,"s":[10]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[150,118,0],"to":[0,1.667,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[150,128,0],"to":[0,0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[150,123,0],"to":[0,0,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[150,128,0],"to":[0,-0.833,0],"ti":[0,1.667,0]},{"t":120,"s":[150,118,0]}],"ix":2},"a":{"a":0,"k":[30,36,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]}
|
@ -2,6 +2,12 @@
|
||||
|
||||
@class TGModernConversationInputMicButton;
|
||||
|
||||
@protocol TGModernConversationInputMicButtonLock <NSObject>
|
||||
|
||||
- (void)updateLockness:(CGFloat)lockness;
|
||||
|
||||
@end
|
||||
|
||||
@protocol TGModernConversationInputMicButtonDecoration <NSObject>
|
||||
|
||||
- (void)updateLevel:(CGFloat)level;
|
||||
@ -29,11 +35,13 @@
|
||||
- (void)micButtonInteractionLocked;
|
||||
- (void)micButtonInteractionRequestedLockedAction;
|
||||
- (void)micButtonInteractionStopped;
|
||||
- (void)micButtonInteractionUpdateCancelTranslation:(CGFloat)translation;
|
||||
|
||||
- (bool)micButtonShouldLock;
|
||||
|
||||
- (id<TGModernConversationInputMicButtonPresentation>)micButtonPresenter;
|
||||
- (UIView<TGModernConversationInputMicButtonDecoration> *)micButtonDecoration;
|
||||
- (UIView<TGModernConversationInputMicButtonLock> *)micButtonLock;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -124,6 +124,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
CGFloat _currentScale;
|
||||
CGFloat _currentTranslation;
|
||||
CGFloat _targetTranslation;
|
||||
CGFloat _cancelTranslation;
|
||||
|
||||
CFAbsoluteTime _animationStartTime;
|
||||
|
||||
@ -136,6 +137,10 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
|
||||
id<TGModernConversationInputMicButtonPresentation> _presentation;
|
||||
UIView<TGModernConversationInputMicButtonDecoration> *_decoration;
|
||||
UIView<TGModernConversationInputMicButtonLock> *_lock;
|
||||
|
||||
BOOL _xFeedbackOccured;
|
||||
BOOL _yFeedbackOccured;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -267,11 +272,11 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
|
||||
- (UIImage *)panelBackgroundImage
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(40.0f, 40.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGRect rect = CGRectMake(TGScreenPixel / 2.0f, TGScreenPixel / 2.0f, 38.0f - TGScreenPixel, 38.0 - TGScreenPixel);
|
||||
CGFloat radius = 38.0f / 2.0f;
|
||||
CGRect rect = CGRectMake(TGScreenPixel / 2.0f, TGScreenPixel / 2.0f, 40.0f - TGScreenPixel, 40.0 - TGScreenPixel);
|
||||
CGFloat radius = 40.0f / 2.0f;
|
||||
|
||||
CGFloat minx = CGRectGetMinX(rect), midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect);
|
||||
CGFloat miny = CGRectGetMinY(rect), midy = CGRectGetMidY(rect), maxy = CGRectGetMaxY(rect);
|
||||
@ -298,14 +303,14 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
|
||||
- (UIImage *)stopButtonImage
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(40.0f, 40.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetFillColorWithColor(context, (self.pallete != nil ? self.pallete.backgroundColor : UIColorRGB(0xf7f7f7)).CGColor);
|
||||
CGContextSetStrokeColorWithColor(context, (self.pallete != nil ? self.pallete.borderColor : UIColorRGB(0xb2b2b2)).CGColor);
|
||||
CGContextSetLineWidth(context, TGScreenPixel);
|
||||
|
||||
CGRect rect1 = CGRectMake(TGScreenPixel / 2.0f, TGScreenPixel / 2.0f, 38.0f - TGScreenPixel, 38.0 - TGScreenPixel);
|
||||
CGRect rect1 = CGRectMake(TGScreenPixel / 2.0f, TGScreenPixel / 2.0f, 40.0f - TGScreenPixel, 40.0 - TGScreenPixel);
|
||||
CGContextFillEllipseInRect(context, rect1);
|
||||
CGContextStrokeEllipseInRect(context, rect1);
|
||||
|
||||
@ -333,8 +338,10 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
}
|
||||
|
||||
- (void)animateIn {
|
||||
if (!_locked)
|
||||
if (!_locked) {
|
||||
_lockView.lockness = 0.0f;
|
||||
[_lock updateLockness:0.0];
|
||||
}
|
||||
|
||||
_animatedIn = true;
|
||||
_animationStartTime = CACurrentMediaTime();
|
||||
@ -358,14 +365,20 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
};
|
||||
}
|
||||
|
||||
_lockPanelWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 38.0f, 77.0f)];
|
||||
_lockPanelWrapperView.userInteractionEnabled = false;
|
||||
_lockPanelWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 72.0f)];
|
||||
[[_presentation view] addSubview:_lockPanelWrapperView];
|
||||
|
||||
_lockPanelView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 38.0f, 77.0f)];
|
||||
_lockPanelView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 72.0f)];
|
||||
_lockPanelView.userInteractionEnabled = true;
|
||||
_lockPanelView.image = [self panelBackgroundImage];
|
||||
|
||||
[_lockPanelWrapperView addSubview:_lockPanelView];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(micButtonLock)]) {
|
||||
_lock = [_delegate micButtonLock];
|
||||
_lock.center = CGPointMake(CGRectGetMidX(_lockPanelView.bounds), CGRectGetMidY(_lockPanelView.bounds));
|
||||
[_lockPanelView addSubview:_lock];
|
||||
} else {
|
||||
_lockArrowView = [[UIImageView alloc] initWithImage:TGTintedImage(TGComponentsImageNamed(@"VideoRecordArrow"), self.pallete != nil ? self.pallete.lockColor : UIColorRGB(0x9597a0))];
|
||||
_lockArrowView.frame = CGRectMake(floor((_lockPanelView.frame.size.width - _lockArrowView.frame.size.width) / 2.0f), 54.0f, _lockArrowView.frame.size.width, _lockArrowView.frame.size.height);
|
||||
[_lockPanelView addSubview:_lockArrowView];
|
||||
@ -374,23 +387,26 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
_lockView.color = self.pallete.lockColor;
|
||||
_lockView.frame = CGRectMake(floor((_lockPanelView.frame.size.width - _lockView.frame.size.width) / 2.0f), 6.0f, _lockView.frame.size.width, _lockView.frame.size.height);
|
||||
[_lockPanelView addSubview:_lockView];
|
||||
}
|
||||
|
||||
_innerCircleView = [[UIImageView alloc] initWithImage:[self innerCircleImage:self.pallete != nil ? self.pallete.buttonColor : TGAccentColor()]];
|
||||
_innerCircleView.alpha = 0.0f;
|
||||
[[_presentation view] addSubview:_innerCircleView];
|
||||
|
||||
// if ([_delegate respondsToSelector:@selector(micButtonDecoration)]) {
|
||||
// UIView<TGModernConversationInputMicButtonDecoration> *decoration = [_delegate micButtonDecoration];
|
||||
// _decoration = decoration;
|
||||
// [[_presentation view] addSubview:_decoration];
|
||||
// }
|
||||
if ([_delegate respondsToSelector:@selector(micButtonDecoration)]) {
|
||||
UIView<TGModernConversationInputMicButtonDecoration> *decoration = [_delegate micButtonDecoration];
|
||||
_decoration = decoration;
|
||||
[[_presentation view] addSubview:_decoration];
|
||||
}
|
||||
|
||||
if (_decoration == nil) {
|
||||
_outerCircleView = [[UIImageView alloc] initWithImage:[self outerCircleImage:self.pallete != nil ? self.pallete.buttonColor : TGAccentColor()]];
|
||||
_outerCircleView.alpha = 0.0f;
|
||||
_outerCircleView.tag = 0x01f2bca;
|
||||
[[_presentation view] addSubview:_outerCircleView];
|
||||
|
||||
[_outerCircleView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(outerCircleTapGesture:)]];
|
||||
}
|
||||
|
||||
_innerIconView = [[UIImageView alloc] initWithImage:_icon];
|
||||
|
||||
@ -401,7 +417,8 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
|
||||
[[_presentation view] addSubview:_innerIconWrapperView];
|
||||
|
||||
_stopButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 38.0f, 38.0f)];
|
||||
if (_lock == nil) {
|
||||
_stopButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)];
|
||||
_stopButton.accessibilityLabel = TGLocalized(@"VoiceOver.Recording.StopAndPreview");
|
||||
_stopButton.adjustsImageWhenHighlighted = false;
|
||||
_stopButton.exclusiveTouch = true;
|
||||
@ -411,6 +428,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
[_stopButton addTarget:self action:@selector(stopPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[[_presentation view] addSubview:_stopButton];
|
||||
}
|
||||
}
|
||||
|
||||
[_presentation setUserInteractionEnabled:_blocking];
|
||||
[_presentation present];
|
||||
@ -505,6 +523,11 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
if (finished || [[[LegacyComponentsGlobals provider] applicationInstance] applicationState] == UIApplicationStateBackground) {
|
||||
[_presentation dismiss];
|
||||
_presentation = nil;
|
||||
|
||||
_cancelTranslation = 0;
|
||||
id<TGModernConversationInputMicButtonDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(micButtonInteractionUpdateCancelTranslation:)])
|
||||
[delegate micButtonInteractionUpdateCancelTranslation:-_cancelTranslation];
|
||||
}
|
||||
|
||||
if (_previousIcon != nil)
|
||||
@ -521,6 +544,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
|
||||
- (void)animateLock {
|
||||
_lockView.lockness = 1.0f;
|
||||
[_lock updateLockness:1.0];
|
||||
|
||||
UIView *snapshotView = [_innerIconView snapshotViewAfterScreenUpdates:false];
|
||||
snapshotView.frame = _innerIconView.frame;
|
||||
@ -544,12 +568,14 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
snapshotView.alpha = 0.0f;
|
||||
_innerIconView.alpha = 1.0f;
|
||||
|
||||
_lockPanelView.frame = CGRectMake(_lockPanelView.frame.origin.x, 39.0f, _lockPanelView.frame.size.width, 77.0f - 39.0f);
|
||||
_lockPanelView.frame = CGRectMake(_lockPanelView.frame.origin.x, 40.0f, _lockPanelView.frame.size.width, 72.0f - 32.0f);
|
||||
_lockView.transform = CGAffineTransformMakeTranslation(0.0f, -11.0f);
|
||||
_lock.transform = CGAffineTransformMakeTranslation(0.0f, -16.0f);
|
||||
_lockArrowView.transform = CGAffineTransformMakeTranslation(0.0f, -39.0f);
|
||||
_lockArrowView.alpha = 0.0f;
|
||||
}];
|
||||
|
||||
if (_lock == nil) {
|
||||
TGDispatchAfter(0.45, dispatch_get_main_queue(), ^
|
||||
{
|
||||
[UIView animateWithDuration:0.2 delay:0.0 options:7 << 16 animations:^
|
||||
@ -558,12 +584,13 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
} completion:^(__unused BOOL finished)
|
||||
{
|
||||
_lockPanelWrapperView.alpha = 0.0f;
|
||||
_lockPanelView.frame = CGRectMake(_lockPanelView.frame.origin.x, 0.0f, _lockPanelView.frame.size.width, 77.0f);
|
||||
_lockPanelView.frame = CGRectMake(_lockPanelView.frame.origin.x, 0.0f, _lockPanelView.frame.size.width, 72.0f);
|
||||
_lockView.transform = CGAffineTransformIdentity;
|
||||
_lockArrowView.transform = CGAffineTransformIdentity;
|
||||
_lockArrowView.alpha = 1.0f;
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
_stopButton.userInteractionEnabled = true;
|
||||
[UIView animateWithDuration:0.25 delay:0.56 options:kNilOptions animations:^
|
||||
@ -661,27 +688,31 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
|
||||
if (CACurrentMediaTime() > _animationStartTime + 0.50) {
|
||||
CGFloat scale = MAX(0.4f, MIN(1.0f, 1.0f - value.x));
|
||||
if (scale > 0.8f) {
|
||||
scale = 1.0f;
|
||||
} else {
|
||||
scale /= 0.8f;
|
||||
}
|
||||
|
||||
_currentScale = scale;
|
||||
|
||||
_targetTranslation = distanceY;
|
||||
_cancelTranslation = distanceX;
|
||||
CGFloat targetLockness = _locked ? 1.0f : MIN(1.0f, fabs(_targetTranslation) / 105.0f);
|
||||
[_lock updateLockness:targetLockness];
|
||||
_lockView.lockness = targetLockness;
|
||||
_lockView.transform = CGAffineTransformMakeTranslation(0.0f, -11.0f * targetLockness);
|
||||
_lock.transform = CGAffineTransformMakeTranslation(0.0f, -16.0f * targetLockness);
|
||||
|
||||
_lockPanelView.frame = CGRectMake(_lockPanelView.frame.origin.x, 39.0f * targetLockness, _lockPanelView.frame.size.width, 77.0f - 39.0f * targetLockness);
|
||||
_lockPanelView.frame = CGRectMake(_lockPanelView.frame.origin.x,
|
||||
40.0f * targetLockness,
|
||||
_lockPanelView.frame.size.width,
|
||||
72.0f - 32.0f * targetLockness);
|
||||
|
||||
_lockArrowView.alpha = MAX(0.0f, 1.0f - targetLockness * 1.6f);
|
||||
_lockArrowView.transform = CGAffineTransformMakeTranslation(0.0f, -39.0f * targetLockness);
|
||||
}
|
||||
|
||||
if (distanceX < -100.0f)
|
||||
{
|
||||
id<TGModernConversationInputMicButtonDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(micButtonInteractionUpdateCancelTranslation:)])
|
||||
[delegate micButtonInteractionUpdateCancelTranslation:-_cancelTranslation];
|
||||
|
||||
if (distanceX < -150.0f) {
|
||||
id<TGModernConversationInputMicButtonDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(micButtonInteractionCancelled:)])
|
||||
[delegate micButtonInteractionCancelled:velocity];
|
||||
@ -689,16 +720,30 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
_targetTranslation = 0.0f;
|
||||
|
||||
return false;
|
||||
} else if (distanceX < -100.0 && !_xFeedbackOccured) {
|
||||
if (iosMajorVersion() >= 10) {
|
||||
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
|
||||
[generator impactOccurred];
|
||||
}
|
||||
_xFeedbackOccured = true;
|
||||
} else if (distanceX > -100.0) {
|
||||
_xFeedbackOccured = false;
|
||||
}
|
||||
|
||||
if (distanceY < -110.0f)
|
||||
{
|
||||
if (distanceY < -110.0f) {
|
||||
[self _commitLocked];
|
||||
|
||||
return false;
|
||||
} else if (distanceY < -60.0 && !_yFeedbackOccured) {
|
||||
if (iosMajorVersion() >= 10) {
|
||||
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
|
||||
[generator impactOccurred];
|
||||
}
|
||||
_yFeedbackOccured = true;
|
||||
} else if (distanceY > -60.0) {
|
||||
_yFeedbackOccured = false;
|
||||
}
|
||||
|
||||
id<TGModernConversationInputMicButtonDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(micButtonInteractionUpdate:)])
|
||||
[delegate micButtonInteractionUpdate:value];
|
||||
|
||||
@ -722,6 +767,9 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
}
|
||||
|
||||
[super cancelTrackingWithEvent:event];
|
||||
|
||||
_yFeedbackOccured = false;
|
||||
_xFeedbackOccured = false;
|
||||
}
|
||||
|
||||
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
|
||||
@ -730,14 +778,22 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
{
|
||||
_targetTranslation = 0.0f;
|
||||
|
||||
CGFloat distanceX = MIN(0.0f, [touch locationInView:self].x - _touchLocation.x);
|
||||
CGFloat distanceY = MIN(0.0f, [touch locationInView:self].y - _touchLocation.y);
|
||||
|
||||
if (fabs(distanceX) > fabs(distanceY))
|
||||
distanceY = 0.0f;
|
||||
else
|
||||
distanceX = 0.0f;
|
||||
|
||||
CGPoint velocity = _lastVelocity;
|
||||
id<TGModernConversationInputMicButtonDelegate> delegate = _delegate;
|
||||
if (velocity.x < -400.0f)
|
||||
if (velocity.x < -400.0f || distanceX < -100.0)
|
||||
{
|
||||
if ([delegate respondsToSelector:@selector(micButtonInteractionCancelled:)])
|
||||
[delegate micButtonInteractionCancelled:_lastVelocity];
|
||||
}
|
||||
else if (velocity.y < -400.0f)
|
||||
else if (velocity.y < -400.0f || distanceY < -60)
|
||||
{
|
||||
[self _commitLocked];
|
||||
}
|
||||
@ -748,6 +804,9 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
}
|
||||
|
||||
[super endTrackingWithTouch:touch withEvent:event];
|
||||
|
||||
_yFeedbackOccured = false;
|
||||
_yFeedbackOccured = false;
|
||||
}
|
||||
|
||||
- (void)_commitLocked
|
||||
@ -788,18 +847,20 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
_currentTranslation = MIN(0.0, _currentTranslation * 0.7f + _targetTranslation * 0.3f);
|
||||
|
||||
CGFloat outerScale = outerCircleMinScale + _currentLevel * (1.0f - outerCircleMinScale);
|
||||
CGAffineTransform translation = CGAffineTransformMakeTranslation(0.0f, _currentTranslation);
|
||||
CGAffineTransform translation = CGAffineTransformMakeTranslation(0, _currentTranslation);
|
||||
CGAffineTransform transform = CGAffineTransformScale(translation, outerScale, outerScale);
|
||||
|
||||
_outerCircleView.transform = transform;
|
||||
|
||||
_innerIconWrapperView.transform = translation;
|
||||
|
||||
if (_lockPanelWrapperView.layer.animationKeys.count == 0)
|
||||
_lockPanelWrapperView.transform = translation;
|
||||
|
||||
transform = CGAffineTransformScale(translation, _currentScale, _currentScale);
|
||||
transform = CGAffineTransformTranslate(transform, _cancelTranslation, 0);
|
||||
|
||||
_innerCircleView.transform = transform;
|
||||
_innerIconWrapperView.transform = transform;
|
||||
_decoration.transform = transform;
|
||||
|
||||
[_decoration tick:_currentLevel];
|
||||
}
|
||||
@ -825,7 +886,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, 38.0f, 38.0f)];
|
||||
self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, 40.0f, 40.0f)];
|
||||
if (self != nil)
|
||||
{
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
1
submodules/TelegramUI/Resources/Animations/Lock.json
Normal file
1
submodules/TelegramUI/Resources/Animations/Lock.json
Normal file
File diff suppressed because one or more lines are too long
1
submodules/TelegramUI/Resources/Animations/LockWait.json
Normal file
1
submodules/TelegramUI/Resources/Animations/LockWait.json
Normal file
@ -0,0 +1 @@
|
||||
{"v":"5.5.9","fr":60,"ip":0,"op":120,"w":240,"h":360,"nm":"Lock1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[120,282,0],"to":[0,-1.667,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[120,272,0],"to":[0,-0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[120,277,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[120,272,0],"to":[0,0.833,0],"ti":[0,-1.667,0]},{"t":120,"s":[120,282,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,150,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.96862745098,0.96862745098,0.96862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":75,"s":[0]},{"t":120,"s":[10]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[150,118,0],"to":[0,1.667,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[150,128,0],"to":[0,0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[150,123,0],"to":[0,0,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[150,128,0],"to":[0,-0.833,0],"ti":[0,1.667,0]},{"t":120,"s":[150,118,0]}],"ix":2},"a":{"a":0,"k":[30,36,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]}
|
1
submodules/TelegramUI/Resources/Animations/voicebin.json
Normal file
1
submodules/TelegramUI/Resources/Animations/voicebin.json
Normal file
File diff suppressed because one or more lines are too long
@ -7231,6 +7231,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
switch updatedAction {
|
||||
case .dismiss:
|
||||
self.chatDisplayNode.updateRecordedMediaDeleted(true)
|
||||
break
|
||||
case .preview:
|
||||
let _ = (audioRecorderValue.takenRecordedData() |> deliverOnMainQueue).start(next: { [weak self] data in
|
||||
@ -7251,6 +7252,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
case .send:
|
||||
self.chatDisplayNode.updateRecordedMediaDeleted(false)
|
||||
let _ = (audioRecorderValue.takenRecordedData()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] data in
|
||||
if let strongSelf = self, let data = data {
|
||||
|
@ -1772,6 +1772,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
return self.textInputPanelNode?.textInputNode
|
||||
}
|
||||
|
||||
func updateRecordedMediaDeleted(_ isDeleted: Bool) {
|
||||
self.textInputPanelNode?.isMediaDeleted = isDeleted
|
||||
}
|
||||
|
||||
func frameForVisibleArea() -> CGRect {
|
||||
let rect = CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom))
|
||||
if let containerNode = self.containerNode {
|
||||
|
@ -166,6 +166,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
||||
var offsetRecordingControls: () -> Void = { }
|
||||
var switchMode: () -> Void = { }
|
||||
var updateLocked: (Bool) -> Void = { _ in }
|
||||
var updateCancelTranslation: () -> Void = { }
|
||||
|
||||
private var modeTimeoutTimer: SwiftSignalKit.Timer?
|
||||
|
||||
@ -174,6 +175,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
||||
private var recordingOverlay: ChatTextInputAudioRecordingOverlay?
|
||||
private var startTouchLocation: CGPoint?
|
||||
private(set) var controlsOffset: CGFloat = 0.0
|
||||
private(set) var cancelTranslation: CGFloat = 0.0
|
||||
|
||||
private var micLevelDisposable: MetaDisposable?
|
||||
|
||||
@ -368,6 +370,11 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
||||
self.offsetRecordingControls()
|
||||
}
|
||||
|
||||
func micButtonInteractionUpdateCancelTranslation(_ translation: CGFloat) {
|
||||
self.cancelTranslation = translation
|
||||
self.updateCancelTranslation()
|
||||
}
|
||||
|
||||
func micButtonInteractionLocked() {
|
||||
self.updateLocked(true)
|
||||
}
|
||||
@ -391,6 +398,30 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
||||
return CombinedWaveView(frame: CGRect(origin: CGPoint(), size: CGSize(width: 640.0, height: 640.0)), color: self.theme.chat.inputPanel.actionControlFillColor)
|
||||
}
|
||||
|
||||
func micButtonLock() -> (UIView & TGModernConversationInputMicButtonLock)! {
|
||||
let lockView = LockView(frame: CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 60.0)), theme: self.theme)
|
||||
lockView.addTarget(self, action: #selector(handleStopTap), for: .touchUpInside)
|
||||
return lockView
|
||||
}
|
||||
|
||||
@objc private func handleStopTap() {
|
||||
micButtonInteractionStopped()
|
||||
}
|
||||
|
||||
override func animateIn() {
|
||||
super.animateIn()
|
||||
|
||||
innerIconView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
innerIconView.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
override func animateOut() {
|
||||
super.animateOut()
|
||||
|
||||
innerIconView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, removeOnCompletion: false)
|
||||
innerIconView.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
private var previousSize = CGSize()
|
||||
func layoutItems() {
|
||||
let size = self.bounds.size
|
||||
|
@ -13,6 +13,7 @@ import AccountContext
|
||||
import TouchDownGesture
|
||||
import ImageTransparency
|
||||
import ActivityIndicator
|
||||
import AnimationUI
|
||||
|
||||
private let accessoryButtonFont = Font.medium(14.0)
|
||||
|
||||
@ -210,7 +211,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
private let searchLayoutClearImageNode: ASImageNode
|
||||
private var searchActivityIndicator: ActivityIndicator?
|
||||
var audioRecordingInfoContainerNode: ASDisplayNode?
|
||||
var audioRecordingDotNode: ASImageNode?
|
||||
var audioRecordingDotNode: AnimationNode?
|
||||
var audioRecordingTimeNode: ChatTextInputAudioRecordingTimeNode?
|
||||
var audioRecordingCancelIndicator: ChatTextInputAudioRecordingCancelIndicator?
|
||||
|
||||
@ -235,6 +236,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
private var keepSendButtonEnabled = false
|
||||
private var extendedSearchLayout = false
|
||||
|
||||
var isMediaDeleted: Bool = false
|
||||
|
||||
private let inputMenu = ChatTextInputMenu()
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
@ -445,6 +448,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.actionButtons.micButton.updateCancelTranslation = { [weak self] in
|
||||
if let strongSelf = self, let presentationInterfaceState = strongSelf.presentationInterfaceState {
|
||||
if let (width, leftInset, rightInset, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.actionButtons.micButton.stopRecording = { [weak self] in
|
||||
if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction {
|
||||
interfaceInteraction.stopMediaRecording()
|
||||
@ -744,10 +754,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
self.searchLayoutClearImageNode.image = PresentationResourcesChat.chatInputTextFieldClearImage(interfaceState.theme)
|
||||
|
||||
if let audioRecordingDotNode = self.audioRecordingDotNode {
|
||||
audioRecordingDotNode.image = PresentationResourcesChat.chatInputPanelMediaRecordingDotImage(interfaceState.theme)
|
||||
}
|
||||
|
||||
self.audioRecordingTimeNode?.updateTheme(theme: interfaceState.theme)
|
||||
self.audioRecordingCancelIndicator?.updateTheme(theme: interfaceState.theme)
|
||||
|
||||
@ -899,9 +905,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.actionButtons.micButton.updateMode(mode: interfaceState.interfaceState.mediaRecordingMode, animated: transition.isAnimated)
|
||||
|
||||
var hideMicButton = false
|
||||
var audioRecordingItemsVerticalOffset: CGFloat = 0.0
|
||||
var audioRecordingItemsAlpha: CGFloat = 1
|
||||
if let mediaRecordingState = interfaceState.inputTextPanelState.mediaRecordingState {
|
||||
audioRecordingItemsVerticalOffset = panelHeight * 2.0
|
||||
audioRecordingItemsAlpha = 0
|
||||
transition.updateAlpha(layer: self.textInputBackgroundNode.layer, alpha: 0.0)
|
||||
if let textInputNode = self.textInputNode {
|
||||
transition.updateAlpha(node: textInputNode, alpha: 0.0)
|
||||
@ -936,7 +942,19 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.insertSubnode(audioRecordingCancelIndicator, at: 0)
|
||||
}
|
||||
|
||||
audioRecordingCancelIndicator.frame = CGRect(origin: CGPoint(x: leftInset + floor((baseWidth - audioRecordingCancelIndicator.bounds.size.width) / 2.0) - self.actionButtons.micButton.controlsOffset, y: panelHeight - minimalHeight + floor((minimalHeight - audioRecordingCancelIndicator.bounds.size.height) / 2.0)), size: audioRecordingCancelIndicator.bounds.size)
|
||||
let cancelTransformThreshold: CGFloat = 8.0
|
||||
|
||||
let indicatorTranslation = max(0.0, self.actionButtons.micButton.cancelTranslation - cancelTransformThreshold)
|
||||
|
||||
audioRecordingCancelIndicator.frame = CGRect(
|
||||
origin: CGPoint(
|
||||
x: leftInset + floor((baseWidth - audioRecordingCancelIndicator.bounds.size.width - indicatorTranslation) / 2.0),
|
||||
y: panelHeight - minimalHeight + floor((minimalHeight - audioRecordingCancelIndicator.bounds.size.height) / 2.0)),
|
||||
size: audioRecordingCancelIndicator.bounds.size)
|
||||
if self.actionButtons.micButton.cancelTranslation > cancelTransformThreshold {
|
||||
let progress = 1 - (self.actionButtons.micButton.cancelTranslation - cancelTransformThreshold) / 80
|
||||
audioRecordingCancelIndicator.alpha = progress
|
||||
}
|
||||
|
||||
if animateCancelSlideIn {
|
||||
let position = audioRecordingCancelIndicator.layer.position
|
||||
@ -945,6 +963,18 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
audioRecordingCancelIndicator.updateIsDisplayingCancel(isLocked, animated: !animateCancelSlideIn)
|
||||
|
||||
if isLocked || self.actionButtons.micButton.cancelTranslation > cancelTransformThreshold {
|
||||
audioRecordingCancelIndicator.layer.removeAnimation(forKey: "slide_juggle")
|
||||
} else if audioRecordingCancelIndicator.layer.animation(forKey: "slide_juggle") == nil {
|
||||
let slideJuggleAnimation = CABasicAnimation(keyPath: "transform")
|
||||
slideJuggleAnimation.toValue = CATransform3DMakeTranslation(-6, 0, 0)
|
||||
slideJuggleAnimation.duration = 1
|
||||
slideJuggleAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
slideJuggleAnimation.autoreverses = true
|
||||
slideJuggleAnimation.repeatCount = Float.infinity
|
||||
audioRecordingCancelIndicator.layer.add(slideJuggleAnimation, forKey: "slide_juggle")
|
||||
}
|
||||
|
||||
var animateTimeSlideIn = false
|
||||
let audioRecordingTimeNode: ChatTextInputAudioRecordingTimeNode
|
||||
if let currentAudioRecordingTimeNode = self.audioRecordingTimeNode {
|
||||
@ -961,32 +991,41 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
let audioRecordingTimeSize = audioRecordingTimeNode.measure(CGSize(width: 200.0, height: 100.0))
|
||||
|
||||
audioRecordingInfoContainerNode.frame = CGRect(origin: CGPoint(x: min(leftInset, audioRecordingCancelIndicator.frame.minX - audioRecordingTimeSize.width - 8.0 - 28.0), y: 0.0), size: CGSize(width: baseWidth, height: panelHeight))
|
||||
let cancelMinX = audioRecordingCancelIndicator.alpha > 0.5 ? audioRecordingCancelIndicator.frame.minX : width
|
||||
|
||||
audioRecordingTimeNode.frame = CGRect(origin: CGPoint(x: 28.0, y: panelHeight - minimalHeight + floor((minimalHeight - audioRecordingTimeSize.height) / 2.0)), size: audioRecordingTimeSize)
|
||||
audioRecordingInfoContainerNode.frame = CGRect(
|
||||
origin: CGPoint(
|
||||
x: min(leftInset, cancelMinX - audioRecordingTimeSize.width - 8.0 - 28.0),
|
||||
y: 0.0
|
||||
),
|
||||
size: CGSize(width: baseWidth, height: panelHeight)
|
||||
)
|
||||
|
||||
audioRecordingTimeNode.frame = CGRect(origin: CGPoint(x: 40.0, y: panelHeight - minimalHeight + floor((minimalHeight - audioRecordingTimeSize.height) / 2.0)), size: audioRecordingTimeSize)
|
||||
if animateTimeSlideIn {
|
||||
let position = audioRecordingTimeNode.layer.position
|
||||
audioRecordingTimeNode.layer.animatePosition(from: CGPoint(x: position.x - 28.0 - audioRecordingTimeSize.width, y: position.y), to: position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
audioRecordingTimeNode.layer.animatePosition(from: CGPoint(x: position.x - 10.0, y: position.y), to: position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
audioRecordingTimeNode.layer.animateAlpha(from: 0, to: 1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
|
||||
audioRecordingTimeNode.audioRecorder = recorder
|
||||
|
||||
var animateDotSlideIn = false
|
||||
let audioRecordingDotNode: ASImageNode
|
||||
var animateDotAppearing = false
|
||||
let audioRecordingDotNode: AnimationNode
|
||||
if let currentAudioRecordingDotNode = self.audioRecordingDotNode {
|
||||
audioRecordingDotNode = currentAudioRecordingDotNode
|
||||
} else {
|
||||
animateDotSlideIn = transition.isAnimated
|
||||
|
||||
audioRecordingDotNode = ASImageNode()
|
||||
audioRecordingDotNode.image = PresentationResourcesChat.chatInputPanelMediaRecordingDotImage(interfaceState.theme)
|
||||
audioRecordingDotNode = AnimationNode(animation: "voicebin")
|
||||
audioRecordingDotNode.speed = 2.0
|
||||
self.audioRecordingDotNode = audioRecordingDotNode
|
||||
audioRecordingInfoContainerNode.addSubnode(audioRecordingDotNode)
|
||||
self.addSubnode(audioRecordingDotNode)
|
||||
}
|
||||
audioRecordingDotNode.frame = CGRect(origin: CGPoint(x: audioRecordingTimeNode.frame.minX - 17.0, y: panelHeight - minimalHeight + floor((minimalHeight - 9.0) / 2.0)), size: CGSize(width: 9.0, height: 9.0))
|
||||
if animateDotSlideIn {
|
||||
let position = audioRecordingDotNode.layer.position
|
||||
audioRecordingDotNode.layer.animatePosition(from: CGPoint(x: position.x - 9.0 - 51.0, y: position.y), to: position, duration: 0.7, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak audioRecordingDotNode] finished in
|
||||
|
||||
animateDotAppearing = transition.isAnimated
|
||||
|
||||
audioRecordingDotNode.frame = CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: panelHeight - 44), size: CGSize(width: 40.0, height: 40))
|
||||
if animateDotAppearing {
|
||||
audioRecordingDotNode.layer.animateAlpha(from: 0, to: 1, duration: 0.25, delay: 0, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak audioRecordingDotNode] finished in
|
||||
if finished {
|
||||
let animation = CAKeyframeAnimation(keyPath: "opacity")
|
||||
animation.values = [1.0 as NSNumber, 1.0 as NSNumber, 0.0 as NSNumber]
|
||||
@ -998,6 +1037,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
audioRecordingDotNode?.layer.add(animation, forKey: "recording")
|
||||
}
|
||||
})
|
||||
|
||||
self.attachmentButton.layer.animateAlpha(from: 1, to: 0, duration: 0.15, delay: 0, removeOnCompletion: false)
|
||||
self.attachmentButton.layer.animateScale(from: 1, to: 0.3, duration: 0.15, delay: 0, removeOnCompletion: false)
|
||||
}
|
||||
case let .video(status, _):
|
||||
switch status {
|
||||
@ -1021,13 +1063,38 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
if let audioRecordingInfoContainerNode = self.audioRecordingInfoContainerNode {
|
||||
self.audioRecordingInfoContainerNode = nil
|
||||
transition.updateFrame(node: audioRecordingInfoContainerNode, frame: CGRect(origin: CGPoint(x: -width, y: 0.0), size: audioRecordingInfoContainerNode.bounds.size), completion: { [weak audioRecordingInfoContainerNode] _ in
|
||||
transition.updateTransformScale(node: audioRecordingInfoContainerNode, scale: 0)
|
||||
transition.updatePosition(node: audioRecordingInfoContainerNode, position: CGPoint(x: audioRecordingInfoContainerNode.position.x - 10, y: audioRecordingInfoContainerNode.position.y))
|
||||
transition.updateAlpha(node: audioRecordingInfoContainerNode, alpha: 0) { [weak audioRecordingInfoContainerNode] _ in
|
||||
audioRecordingInfoContainerNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = self.audioRecordingDotNode {
|
||||
self.audioRecordingDotNode = nil
|
||||
if let audioRecordingDotNode = self.audioRecordingDotNode {
|
||||
let dismissDotNode = { [weak audioRecordingDotNode, weak attachmentButton, weak self] in
|
||||
guard let audioRecordingDotNode = audioRecordingDotNode else { return }
|
||||
|
||||
if audioRecordingDotNode === self?.audioRecordingDotNode {
|
||||
self?.audioRecordingDotNode = nil
|
||||
}
|
||||
|
||||
audioRecordingDotNode.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, delay: 0, removeOnCompletion: false)
|
||||
audioRecordingDotNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, delay: 0, removeOnCompletion: false) { [weak audioRecordingDotNode] _ in
|
||||
audioRecordingDotNode?.removeFromSupernode()
|
||||
}
|
||||
|
||||
attachmentButton?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: 0, removeOnCompletion: false)
|
||||
attachmentButton?.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, delay: 0, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
audioRecordingDotNode.layer.removeAllAnimations()
|
||||
|
||||
if self.isMediaDeleted {
|
||||
audioRecordingDotNode.completion = dismissDotNode
|
||||
audioRecordingDotNode.play()
|
||||
} else {
|
||||
dismissDotNode()
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = self.audioRecordingTimeNode {
|
||||
@ -1037,24 +1104,16 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
if let audioRecordingCancelIndicator = self.audioRecordingCancelIndicator {
|
||||
self.audioRecordingCancelIndicator = nil
|
||||
if transition.isAnimated {
|
||||
if audioRecordingCancelIndicator.isDisplayingCancel {
|
||||
audioRecordingCancelIndicator.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
audioRecordingCancelIndicator.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -22.0), duration: 0.25, removeOnCompletion: false, additive: true, completion: { [weak audioRecordingCancelIndicator] _ in
|
||||
audioRecordingCancelIndicator.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, completion: { [weak audioRecordingCancelIndicator] _ in
|
||||
audioRecordingCancelIndicator?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
let position = audioRecordingCancelIndicator.layer.position
|
||||
audioRecordingCancelIndicator.layer.animatePosition(from: position, to: CGPoint(x: 0.0 - audioRecordingCancelIndicator.bounds.size.width, y: position.y), duration: 0.3, removeOnCompletion: false, completion: { [weak audioRecordingCancelIndicator] _ in
|
||||
audioRecordingCancelIndicator?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
audioRecordingCancelIndicator.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(layer: self.attachmentButton.layer, frame: CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: panelHeight - minimalHeight + audioRecordingItemsVerticalOffset), size: CGSize(width: 40.0, height: minimalHeight)))
|
||||
transition.updateFrame(layer: self.attachmentButton.layer, frame: CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: panelHeight - minimalHeight), size: CGSize(width: 40.0, height: minimalHeight)))
|
||||
transition.updateFrame(node: self.attachmentButtonDisabledNode, frame: self.attachmentButton.frame)
|
||||
|
||||
var composeButtonsOffset: CGFloat = 0.0
|
||||
@ -1113,8 +1172,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.searchLayoutClearImageNode.frame = CGRect(origin: CGPoint(x: floor((searchLayoutClearButtonSize.width - image.size.width) / 2.0), y: floor((searchLayoutClearButtonSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
let textInputFrame = CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top + audioRecordingItemsVerticalOffset, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom)
|
||||
let textInputFrame = CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom)
|
||||
transition.updateFrame(node: self.textInputContainer, frame: textInputFrame)
|
||||
transition.updateAlpha(node: self.textInputContainer, alpha: audioRecordingItemsAlpha)
|
||||
|
||||
if let textInputNode = self.textInputNode {
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top), size: CGSize(width: textInputFrame.size.width - (self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right + accessoryButtonsWidth), height: textInputFrame.size.height - self.textInputViewInternalInsets.top - textInputViewInternalInsets.bottom))
|
||||
@ -1143,7 +1203,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
let _ = placeholderApply()
|
||||
|
||||
contextPlaceholderNode.frame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + audioRecordingItemsVerticalOffset + UIScreenPixel), size: placeholderSize.size)
|
||||
contextPlaceholderNode.frame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + UIScreenPixel), size: placeholderSize.size)
|
||||
contextPlaceholderNode.alpha = audioRecordingItemsAlpha
|
||||
} else if let contextPlaceholderNode = self.contextPlaceholderNode {
|
||||
self.contextPlaceholderNode = nil
|
||||
contextPlaceholderNode.removeFromSupernode()
|
||||
@ -1159,9 +1220,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.slowmodePlaceholderNode = slowmodePlaceholderNode
|
||||
self.insertSubnode(slowmodePlaceholderNode, aboveSubnode: self.textPlaceholderNode)
|
||||
}
|
||||
let placeholderFrame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + audioRecordingItemsVerticalOffset + UIScreenPixel), size: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: 30.0))
|
||||
let placeholderFrame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + UIScreenPixel), size: CGSize(width: width - leftInset - rightInset - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: 30.0))
|
||||
slowmodePlaceholderNode.updateState(slowmodeState)
|
||||
slowmodePlaceholderNode.frame = placeholderFrame
|
||||
slowmodePlaceholderNode.alpha = audioRecordingItemsAlpha
|
||||
slowmodePlaceholderNode.updateLayout(size: placeholderFrame.size)
|
||||
} else if let slowmodePlaceholderNode = self.slowmodePlaceholderNode {
|
||||
self.slowmodePlaceholderNode = nil
|
||||
@ -1181,11 +1243,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.slowmodePlaceholderNode?.isHidden = true
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.textPlaceholderNode, frame: CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + audioRecordingItemsVerticalOffset + UIScreenPixel), size: self.textPlaceholderNode.frame.size))
|
||||
transition.updateFrame(node: self.textPlaceholderNode, frame: CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + self.textInputViewRealInsets.top + UIScreenPixel), size: self.textPlaceholderNode.frame.size))
|
||||
transition.updateAlpha(node: self.textPlaceholderNode, alpha: audioRecordingItemsAlpha)
|
||||
|
||||
transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top + audioRecordingItemsVerticalOffset, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom))
|
||||
transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom))
|
||||
transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha)
|
||||
|
||||
var nextButtonTopRight = CGPoint(x: width - rightInset - textFieldInsets.right - accessoryButtonInset, y: panelHeight - textFieldInsets.bottom - minimalInputHeight + audioRecordingItemsVerticalOffset)
|
||||
var nextButtonTopRight = CGPoint(x: width - rightInset - textFieldInsets.right - accessoryButtonInset, y: panelHeight - textFieldInsets.bottom - minimalInputHeight)
|
||||
for (_, button) in self.accessoryItemButtons.reversed() {
|
||||
let buttonSize = CGSize(width: button.buttonWidth, height: minimalInputHeight)
|
||||
button.updateLayout(size: buttonSize)
|
||||
|
93
submodules/TelegramUI/Sources/LockView.swift
Normal file
93
submodules/TelegramUI/Sources/LockView.swift
Normal file
@ -0,0 +1,93 @@
|
||||
import UIKit
|
||||
import LegacyComponents
|
||||
import AppBundle
|
||||
import Lottie
|
||||
import TelegramPresentationData
|
||||
|
||||
final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
||||
|
||||
private var colorCallbacks = [LOTValueDelegate]()
|
||||
|
||||
private let idleView: LOTAnimationView = {
|
||||
guard let url = getAppBundle().url(forResource: "LockWait", withExtension: "json"),
|
||||
let composition = LOTComposition(filePath: url.path)
|
||||
else { return LOTAnimationView() }
|
||||
|
||||
let view = LOTAnimationView(model: composition, in: getAppBundle())
|
||||
view.autoReverseAnimation = true
|
||||
view.loopAnimation = true
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
return view
|
||||
}()
|
||||
|
||||
private let lockingView: LOTAnimationView = {
|
||||
guard let url = getAppBundle().url(forResource: "Lock", withExtension: "json"),
|
||||
let composition = LOTComposition(filePath: url.path)
|
||||
else { return LOTAnimationView() }
|
||||
|
||||
let view = LOTAnimationView(model: composition, in: getAppBundle())
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
return view
|
||||
}()
|
||||
|
||||
init(frame: CGRect, theme: PresentationTheme) {
|
||||
super.init(frame: frame)
|
||||
|
||||
addSubview(idleView)
|
||||
idleView.frame = bounds
|
||||
|
||||
addSubview(lockingView)
|
||||
lockingView.frame = bounds
|
||||
|
||||
[
|
||||
"Rectangle.Заливка 1": theme.chat.inputPanel.panelBackgroundColor,
|
||||
"Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||
"Path.Path.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
||||
].forEach { key, value in
|
||||
let colorCallback = LOTColorValueCallback(color: value.cgColor)
|
||||
self.colorCallbacks.append(colorCallback)
|
||||
idleView.setValueDelegate(colorCallback, for: LOTKeypath(string: "\(key).Color"))
|
||||
}
|
||||
|
||||
[
|
||||
"Path.Path.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||
"Path.Path.Заливка 1": theme.chat.inputPanel.panelBackgroundColor,
|
||||
"Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||
"Rectangle.Заливка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
||||
].forEach { key, value in
|
||||
let colorCallback = LOTColorValueCallback(color: value.cgColor)
|
||||
self.colorCallbacks.append(colorCallback)
|
||||
lockingView.setValueDelegate(colorCallback, for: LOTKeypath(string: "\(key).Color"))
|
||||
}
|
||||
|
||||
updateLockness(0)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updateLockness(_ lockness: CGFloat) {
|
||||
idleView.isHidden = lockness > 0
|
||||
if lockness > 0 && idleView.isAnimationPlaying {
|
||||
idleView.stop()
|
||||
} else if lockness == 0 && !idleView.isAnimationPlaying {
|
||||
idleView.play()
|
||||
}
|
||||
lockingView.isHidden = !idleView.isHidden
|
||||
|
||||
lockingView.animationProgress = lockness
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let superTest = super.hitTest(point, with: event)
|
||||
if superTest === lockingView {
|
||||
return self
|
||||
}
|
||||
return superTest
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ class CombinedWaveView: UIView, TGModernConversationInputMicButtonDecoration {
|
||||
init(frame: CGRect, color: UIColor) {
|
||||
let n = 12
|
||||
let bounds = CGRect(origin: CGPoint(), size: frame.size)
|
||||
self.bigWaveView = WaveView(frame: bounds, n: n, amplitudeRadius: 40.0, isBig: true, color: color.withAlphaComponent(0.3))
|
||||
self.bigWaveView = WaveView(frame: bounds, n: n, amplitudeRadius: 30.0, isBig: true, color: color.withAlphaComponent(0.3))
|
||||
self.smallWaveView = WaveView(frame: bounds, n: n, amplitudeRadius: 35.0, isBig: false, color: color.withAlphaComponent(0.15))
|
||||
|
||||
super.init(frame: frame)
|
||||
@ -46,7 +46,7 @@ class CombinedWaveView: UIView, TGModernConversationInputMicButtonDecoration {
|
||||
self.bigWaveView.amplitudeWaveDif = 0.02 * Constants.sineWaveSpeed * CGFloat.pi / 180.0
|
||||
|
||||
self.smallWaveView.amplitudeWaveDif = 0.026 * Constants.sineWaveSpeed
|
||||
self.smallWaveView.amplitudeRadius = 20.0 + 20.0 * Constants.smallWaveRadius
|
||||
self.smallWaveView.amplitudeRadius = 10.0 + 20.0 * Constants.smallWaveRadius
|
||||
self.smallWaveView.maxScale = 0.3 * Constants.smallWaveScale
|
||||
self.smallWaveView.scaleSpeed = 0.001 * Constants.smallWaveScaleSpeed
|
||||
self.smallWaveView.fling = Constants.flingDistance
|
||||
@ -60,14 +60,14 @@ class CombinedWaveView: UIView, TGModernConversationInputMicButtonDecoration {
|
||||
}
|
||||
|
||||
func updateLevel(_ level: CGFloat) {
|
||||
let level = level * 0.45
|
||||
let level = level * 0.2
|
||||
self.level = level
|
||||
self.bigWaveView.setLevel(level)
|
||||
self.smallWaveView.setLevel(level)
|
||||
}
|
||||
|
||||
func tick(_ level: CGFloat) {
|
||||
let radius = 56.0 + 30.0 * level * 0.45
|
||||
let radius = 56.0 + 30.0 * level * 0.2
|
||||
self.bigWaveView.tick(circleRadius: radius)
|
||||
self.smallWaveView.tick(circleRadius: radius)
|
||||
}
|
||||
|
1
voicebin.json
Normal file
1
voicebin.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user