mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Video Messages Improvements
(cherry picked from commit e614343888ebdb7208399b785815b5dbc3f8ad26)
This commit is contained in:
parent
51b1f7a836
commit
40b7feac42
@ -1,5 +1,12 @@
|
|||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface TGVideoMessageShimmerView : UIView
|
||||||
|
|
||||||
|
- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
@interface TGVideoMessageRingView : UIView
|
@interface TGVideoMessageRingView : UIView
|
||||||
|
|
||||||
@property (nonatomic, strong) UIColor *accentColor;
|
@property (nonatomic, strong) UIColor *accentColor;
|
||||||
|
@ -96,6 +96,7 @@ typedef enum
|
|||||||
UIView *_separatorView;
|
UIView *_separatorView;
|
||||||
|
|
||||||
UIImageView *_placeholderView;
|
UIImageView *_placeholderView;
|
||||||
|
TGVideoMessageShimmerView *_shimmerView;
|
||||||
|
|
||||||
bool _automaticDismiss;
|
bool _automaticDismiss;
|
||||||
NSTimeInterval _startTimestamp;
|
NSTimeInterval _startTimestamp;
|
||||||
@ -329,6 +330,10 @@ typedef enum
|
|||||||
_placeholderView.image = [TGVideoMessageCaptureController startImage];
|
_placeholderView.image = [TGVideoMessageCaptureController startImage];
|
||||||
[_circleView addSubview:_placeholderView];
|
[_circleView addSubview:_placeholderView];
|
||||||
|
|
||||||
|
_shimmerView = [[TGVideoMessageShimmerView alloc] initWithFrame:_circleView.bounds];
|
||||||
|
[_shimmerView updateAbsoluteRect:_circleView.bounds containerSize:_circleView.bounds.size];
|
||||||
|
[_circleView addSubview:_shimmerView];
|
||||||
|
|
||||||
if (iosMajorVersion() >= 11)
|
if (iosMajorVersion() >= 11)
|
||||||
{
|
{
|
||||||
_shadowView.accessibilityIgnoresInvertColors = true;
|
_shadowView.accessibilityIgnoresInvertColors = true;
|
||||||
@ -1182,9 +1187,11 @@ typedef enum
|
|||||||
[UIView animateWithDuration:0.3 delay:delay options:kNilOptions animations:^
|
[UIView animateWithDuration:0.3 delay:delay options:kNilOptions animations:^
|
||||||
{
|
{
|
||||||
_placeholderView.alpha = 0.0f;
|
_placeholderView.alpha = 0.0f;
|
||||||
|
_shimmerView.alpha = 0.0f;
|
||||||
_switchButton.alpha = 1.0f;
|
_switchButton.alpha = 1.0f;
|
||||||
} completion:^(__unused BOOL finished)
|
} completion:^(__unused BOOL finished)
|
||||||
{
|
{
|
||||||
|
_shimmerView.hidden = true;
|
||||||
_placeholderView.hidden = true;
|
_placeholderView.hidden = true;
|
||||||
_placeholderView.alpha = 1.0f;
|
_placeholderView.alpha = 1.0f;
|
||||||
}];
|
}];
|
||||||
|
@ -2,6 +2,168 @@
|
|||||||
|
|
||||||
#import "TGColor.h"
|
#import "TGColor.h"
|
||||||
|
|
||||||
|
#import "LegacyComponentsInternal.h"
|
||||||
|
|
||||||
|
@interface TGVideoMessageShimmerEffectForegroundView : UIView
|
||||||
|
{
|
||||||
|
UIView *_imageContainerView;
|
||||||
|
UIView *_imageView;
|
||||||
|
|
||||||
|
CGFloat _size;
|
||||||
|
bool _hasContainerSize;
|
||||||
|
CGRect _absoluteRect;
|
||||||
|
CGSize _containerSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithSize:(CGFloat)size alpha:(CGFloat)alpha;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation TGVideoMessageShimmerEffectForegroundView
|
||||||
|
|
||||||
|
- (instancetype)initWithSize:(CGFloat)size alpha:(CGFloat)alpha {
|
||||||
|
self = [super initWithFrame:CGRectZero];
|
||||||
|
if (self != nil) {
|
||||||
|
_size = size;
|
||||||
|
|
||||||
|
_imageContainerView = [[UIView alloc] init];
|
||||||
|
_imageView = [[UIView alloc] init];
|
||||||
|
|
||||||
|
self.clipsToBounds = true;
|
||||||
|
|
||||||
|
[_imageContainerView addSubview:_imageView];
|
||||||
|
[self addSubview:_imageContainerView];
|
||||||
|
|
||||||
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, 16), false, 0.0f);
|
||||||
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
|
||||||
|
CGRect bounds = CGRectMake(0, 0, size, 16);
|
||||||
|
CGContextClearRect(context, bounds);
|
||||||
|
CGContextClipToRect(context, bounds);
|
||||||
|
|
||||||
|
UIColor *transparentColor = [UIColor colorWithWhite:1.0 alpha:0.0];
|
||||||
|
UIColor *peakColor = [UIColor colorWithWhite:1.0 alpha:alpha];
|
||||||
|
|
||||||
|
CGColorRef colors[3] = {
|
||||||
|
CGColorRetain(transparentColor.CGColor),
|
||||||
|
CGColorRetain(peakColor.CGColor),
|
||||||
|
CGColorRetain(transparentColor.CGColor)
|
||||||
|
};
|
||||||
|
|
||||||
|
CFArrayRef colorsArray = CFArrayCreate(kCFAllocatorDefault, (const void **)&colors, 3, NULL);
|
||||||
|
CGFloat locations[3] = {0.0f, 0.5, 1.0};
|
||||||
|
|
||||||
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||||
|
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorsArray, (CGFloat const *)&locations);
|
||||||
|
|
||||||
|
CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(size, 0), kNilOptions);
|
||||||
|
|
||||||
|
CFRelease(colorsArray);
|
||||||
|
CFRelease(colors[0]);
|
||||||
|
CFRelease(colors[1]);
|
||||||
|
|
||||||
|
CGColorSpaceRelease(colorSpace);
|
||||||
|
CFRelease(gradient);
|
||||||
|
|
||||||
|
UIImage *image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25];
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
|
_imageView.backgroundColor = [UIColor colorWithPatternImage:image];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize {
|
||||||
|
_hasContainerSize = true;
|
||||||
|
|
||||||
|
CGRect previousAbsoluteRect = _absoluteRect;
|
||||||
|
CGSize previousContainerSize = _containerSize;
|
||||||
|
_absoluteRect = absoluteRect;
|
||||||
|
_containerSize = containerSize;
|
||||||
|
|
||||||
|
if (!CGSizeEqualToSize(previousContainerSize, containerSize)) {
|
||||||
|
[self setupAnimation];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CGRectEqualToRect(previousAbsoluteRect, absoluteRect)) {
|
||||||
|
_imageContainerView.frame = CGRectMake(-absoluteRect.origin.x, -absoluteRect.origin.y, containerSize.width, containerSize.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (void)setupAnimation {
|
||||||
|
if (!_hasContainerSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGFloat gradientHeight = _size;
|
||||||
|
_imageView.frame = CGRectMake(-gradientHeight, 0, gradientHeight, _containerSize.height);
|
||||||
|
|
||||||
|
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
|
||||||
|
animation.fromValue = @(_imageView.center.x);
|
||||||
|
animation.toValue = @(_imageView.center.x + _containerSize.width + gradientHeight);
|
||||||
|
animation.duration = 1.3f;
|
||||||
|
animation.repeatCount = INFINITY;
|
||||||
|
animation.beginTime = 1.0;
|
||||||
|
[_imageView.layer addAnimation:animation forKey:@"position"];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface TGVideoMessageShimmerView ()
|
||||||
|
{
|
||||||
|
TGVideoMessageShimmerEffectForegroundView *_effectView;
|
||||||
|
UIImageView *_imageView;
|
||||||
|
|
||||||
|
UIView *_borderView;
|
||||||
|
UIView *_borderMaskView;
|
||||||
|
TGVideoMessageShimmerEffectForegroundView *_borderEffectView;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation TGVideoMessageShimmerView
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame {
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self != nil) {
|
||||||
|
self.clipsToBounds = true;
|
||||||
|
self.layer.cornerRadius = frame.size.width / 2.0f;
|
||||||
|
if (iosMajorVersion() >= 13) {
|
||||||
|
self.layer.cornerCurve = kCACornerCurveCircular;
|
||||||
|
}
|
||||||
|
|
||||||
|
_effectView = [[TGVideoMessageShimmerEffectForegroundView alloc] initWithSize:320 alpha:0.3];
|
||||||
|
_effectView.layer.compositingFilter = @"screenBlendMode";
|
||||||
|
_effectView.frame = self.bounds;
|
||||||
|
|
||||||
|
_borderView = [[UIView alloc] initWithFrame:self.bounds];
|
||||||
|
_borderMaskView = [[UIView alloc] initWithFrame:self.bounds];
|
||||||
|
_borderMaskView.layer.borderWidth = 1.0;
|
||||||
|
_borderMaskView.layer.borderColor = [UIColor whiteColor].CGColor;
|
||||||
|
_borderMaskView.layer.cornerRadius = frame.size.width / 2.0f;
|
||||||
|
if (iosMajorVersion() >= 13) {
|
||||||
|
_borderMaskView.layer.cornerCurve = kCACornerCurveCircular;
|
||||||
|
}
|
||||||
|
_borderView.maskView = _borderMaskView;
|
||||||
|
|
||||||
|
_borderEffectView = [[TGVideoMessageShimmerEffectForegroundView alloc] initWithSize:400 alpha:0.45];
|
||||||
|
_borderEffectView.layer.compositingFilter = @"screenBlendMode";
|
||||||
|
_borderEffectView.frame = self.bounds;
|
||||||
|
|
||||||
|
[self addSubview:_effectView];
|
||||||
|
[self addSubview:_borderView];
|
||||||
|
[_borderView addSubview:_borderEffectView];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize {
|
||||||
|
[_effectView updateAbsoluteRect:absoluteRect containerSize:containerSize];
|
||||||
|
[_borderEffectView updateAbsoluteRect:absoluteRect containerSize:containerSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@interface TGVideoMessageRingView ()
|
@interface TGVideoMessageRingView ()
|
||||||
{
|
{
|
||||||
CGFloat _value;
|
CGFloat _value;
|
||||||
|
@ -281,7 +281,6 @@ private final class MediaPlayerContext {
|
|||||||
CMTimebaseSetRate(loadedState.controlTimebase.timebase, rate: 0.0)
|
CMTimebaseSetRate(loadedState.controlTimebase.timebase, rate: 0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let currentTimestamp = CMTimeGetSeconds(CMTimebaseGetTime(loadedState.controlTimebase.timebase))
|
|
||||||
var duration: Double = 0.0
|
var duration: Double = 0.0
|
||||||
if let videoTrackFrameBuffer = loadedState.mediaBuffers.videoBuffer {
|
if let videoTrackFrameBuffer = loadedState.mediaBuffers.videoBuffer {
|
||||||
duration = max(duration, CMTimeGetSeconds(videoTrackFrameBuffer.duration))
|
duration = max(duration, CMTimeGetSeconds(videoTrackFrameBuffer.duration))
|
||||||
|
@ -200,8 +200,12 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
self.hapticFeedback.impact(.light)
|
self.hapticFeedback.impact(.light)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.seekTo?(min(0.99, fraction), false)
|
let newProgress = min(0.99, fraction)
|
||||||
self.seekingProgress = CGFloat(fraction)
|
if let seekingProgress = self.seekingProgress, abs(seekingProgress - CGFloat(newProgress)) < 0.005 {
|
||||||
|
} else {
|
||||||
|
self.seekTo?(newProgress, false)
|
||||||
|
self.seekingProgress = CGFloat(fraction)
|
||||||
|
}
|
||||||
case .ended, .cancelled:
|
case .ended, .cancelled:
|
||||||
self.seeking = false
|
self.seeking = false
|
||||||
self.seekTo?(min(0.99, fraction), true)
|
self.seekTo?(min(0.99, fraction), true)
|
||||||
|
@ -720,21 +720,23 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu
|
|||||||
let entity = string.substring(with: match.range(at: 7))
|
let entity = string.substring(with: match.range(at: 7))
|
||||||
let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9))
|
let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9))
|
||||||
|
|
||||||
|
let textInputAttribute: NSAttributedString.Key?
|
||||||
switch entity {
|
switch entity {
|
||||||
case "`":
|
case "`":
|
||||||
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber]))
|
textInputAttribute = ChatTextInputAttributes.monospace
|
||||||
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
|
|
||||||
case "**":
|
case "**":
|
||||||
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.bold: true as NSNumber]))
|
textInputAttribute = ChatTextInputAttributes.bold
|
||||||
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
|
|
||||||
case "__":
|
case "__":
|
||||||
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.italic: true as NSNumber]))
|
textInputAttribute = ChatTextInputAttributes.italic
|
||||||
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
|
|
||||||
case "~~":
|
case "~~":
|
||||||
result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.strikethrough: true as NSNumber]))
|
textInputAttribute = ChatTextInputAttributes.strikethrough
|
||||||
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
|
|
||||||
default:
|
default:
|
||||||
break
|
textInputAttribute = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let textInputAttribute = textInputAttribute {
|
||||||
|
result.append(NSAttributedString(string: substring, attributes: [textInputAttribute: true as NSNumber]))
|
||||||
|
offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
stringOffset -= match.range(at: 7).length * 2
|
stringOffset -= match.range(at: 7).length * 2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user