mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
336 lines
11 KiB
Objective-C
336 lines
11 KiB
Objective-C
/*
|
|
* This is the source code of Telegram for iOS v. 1.1
|
|
* It is licensed under GNU GPL v. 2 or later.
|
|
* You should have received a copy of the license in this archive (see LICENSE).
|
|
*
|
|
* Copyright Peter Iakovlev, 2013.
|
|
*/
|
|
|
|
#import "TGImageView.h"
|
|
|
|
#import "TGImageManager.h"
|
|
#import <MTProtoKit/MTTime.h>
|
|
|
|
#import "UIImage+TG.h"
|
|
|
|
NSString *TGImageViewOptionKeepCurrentImageAsPlaceholder = @"TGImageViewOptionKeepCurrentImageAsPlaceholder";
|
|
NSString *TGImageViewOptionEmbeddedImage = @"TGImageViewOptionEmbeddedImage";
|
|
NSString *TGImageViewOptionSynchronous = @"TGImageViewOptionSynchronous";
|
|
|
|
@interface TGImageView ()
|
|
{
|
|
id _loadToken;
|
|
volatile int _version;
|
|
SMetaDisposable *_disposable;
|
|
|
|
UIImageView *_transitionOverlayView;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGImageView
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
{
|
|
self = [super initWithFrame:frame];
|
|
if (self != nil)
|
|
{
|
|
_disposable = [[SMetaDisposable alloc] init];
|
|
_legacyAutomaticProgress = true;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_disposable dispose];
|
|
if (_loadToken != nil)
|
|
[[TGImageManager instance] cancelTaskWithId:_loadToken];
|
|
}
|
|
|
|
- (void)setExpectExtendedEdges:(bool)expectExtendedEdges
|
|
{
|
|
if (_expectExtendedEdges != expectExtendedEdges)
|
|
{
|
|
_expectExtendedEdges = expectExtendedEdges;
|
|
|
|
if (_expectExtendedEdges && _extendedInsetsImageView == nil)
|
|
{
|
|
self.image = nil;
|
|
_extendedInsetsImageView = [[UIImageView alloc] init];
|
|
[self addSubview:_extendedInsetsImageView];
|
|
}
|
|
else if (!_expectExtendedEdges && _extendedInsetsImageView != nil)
|
|
{
|
|
_extendedInsetsImageView.image = nil;
|
|
[_extendedInsetsImageView removeFromSuperview];
|
|
_extendedInsetsImageView = nil;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)loadUri:(NSString *)uri withOptions:(NSDictionary *)__unused options
|
|
{
|
|
[_disposable setDisposable:nil];
|
|
_version++;
|
|
|
|
UIImage *image = nil;
|
|
|
|
bool beganAsyncTask = false;
|
|
|
|
if (options[TGImageViewOptionEmbeddedImage] != nil)
|
|
image = options[TGImageViewOptionEmbeddedImage];
|
|
else
|
|
{
|
|
__autoreleasing id asyncTaskId = nil;
|
|
__weak TGImageView *weakSelf = self;
|
|
int version = _version;
|
|
MTAbsoluteTime loadStartTime = MTAbsoluteSystemTime();
|
|
bool legacyAutomaticProgress = _legacyAutomaticProgress;
|
|
image = [[TGImageManager instance] loadImageSyncWithUri:uri canWait:[options[TGImageViewOptionSynchronous] boolValue] decode:true acceptPartialData:true asyncTaskId:&asyncTaskId progress:^(float value)
|
|
{
|
|
if (legacyAutomaticProgress)
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
__strong TGImageView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_version == version)
|
|
[strongSelf _updateProgress:value];
|
|
});
|
|
}
|
|
} partialCompletion:^(UIImage *partialImage)
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
__strong TGImageView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_version == version)
|
|
[strongSelf _commitImage:partialImage partial:true loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
|
|
else
|
|
TGLog(@"[TGImageView _commitImage version mismatch]");
|
|
});
|
|
} completion:^(UIImage *image)
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
__strong TGImageView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_version == version)
|
|
{
|
|
if (legacyAutomaticProgress)
|
|
[strongSelf _updateProgress:1.0f];
|
|
[strongSelf _commitImage:image partial:false loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
|
|
}
|
|
else
|
|
TGLog(@"[TGImageView _commitImage version mismatch]");
|
|
});
|
|
}];
|
|
|
|
if (asyncTaskId != nil)
|
|
{
|
|
beganAsyncTask = true;
|
|
_loadToken = asyncTaskId;
|
|
}
|
|
}
|
|
|
|
if (image != nil)
|
|
[self _commitImage:image partial:beganAsyncTask loadTime:0.0];
|
|
else
|
|
{
|
|
if (![options[TGImageViewOptionKeepCurrentImageAsPlaceholder] boolValue])
|
|
{
|
|
UIImage *placeholderImage = [[TGImageManager instance] loadAttributeSyncForUri:uri attribute:@"placeholder"];
|
|
if (placeholderImage != nil)
|
|
[self _commitImage:placeholderImage partial:beganAsyncTask loadTime:0.0];
|
|
else
|
|
[self performTransitionToImage:nil partial:true duration:0.0];
|
|
}
|
|
|
|
MTAbsoluteTime loadStartTime = MTAbsoluteSystemTime();
|
|
|
|
__weak TGImageView *weakSelf = self;
|
|
int version = _version;
|
|
bool legacyAutomaticProgress = _legacyAutomaticProgress;
|
|
_loadToken = [[TGImageManager instance] beginLoadingImageAsyncWithUri:uri decode:true progress:^(float value)
|
|
{
|
|
if (legacyAutomaticProgress)
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
__strong TGImageView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_version == version)
|
|
[strongSelf _updateProgress:value];
|
|
});
|
|
}
|
|
} partialCompletion:^(UIImage *partialImage)
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
__strong TGImageView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_version == version)
|
|
[strongSelf _commitImage:partialImage partial:true loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
|
|
else
|
|
TGLog(@"[TGImageView _commitImage version mismatch]");
|
|
});
|
|
} completion:^(UIImage *image)
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
__strong TGImageView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_version == version)
|
|
{
|
|
if (legacyAutomaticProgress)
|
|
[strongSelf _updateProgress:1.0f];
|
|
[strongSelf _commitImage:image partial:false loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
|
|
}
|
|
else
|
|
TGLog(@"[TGImageView _commitImage version mismatch]");
|
|
});
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)_updateProgress:(float)value
|
|
{
|
|
[self performProgressUpdate:value];
|
|
}
|
|
|
|
- (void)_commitImage:(UIImage *)image partial:(bool)partial loadTime:(NSTimeInterval)loadTime
|
|
{
|
|
if (image == self.image)
|
|
return;
|
|
|
|
NSTimeInterval transitionDuration = 0.0;
|
|
|
|
if (loadTime > DBL_EPSILON)
|
|
transitionDuration = 0.16;
|
|
|
|
[self performTransitionToImage:image partial:partial duration:transitionDuration];
|
|
}
|
|
|
|
- (void)reset
|
|
{
|
|
_version++;
|
|
[_disposable setDisposable:nil];
|
|
|
|
if (_loadToken != nil)
|
|
{
|
|
[[TGImageManager instance] cancelTaskWithId:_loadToken];
|
|
_loadToken = nil;
|
|
}
|
|
|
|
[self _commitImage:nil partial:false loadTime:0.0];
|
|
}
|
|
|
|
- (UIImage *)currentImage
|
|
{
|
|
if (_expectExtendedEdges)
|
|
return _extendedInsetsImageView.image;
|
|
return [super image];
|
|
}
|
|
|
|
- (void)performProgressUpdate:(CGFloat)__unused progress
|
|
{
|
|
}
|
|
|
|
- (void)performTransitionToImage:(UIImage *)image partial:(bool)__unused partial duration:(NSTimeInterval)duration
|
|
{
|
|
if (((_expectExtendedEdges && _extendedInsetsImageView.image != nil) || (!_expectExtendedEdges && self.image != nil)) && duration > DBL_EPSILON)
|
|
{
|
|
self.alpha = 1.0f;
|
|
_extendedInsetsImageView.alpha = 1.0f;
|
|
|
|
if (_transitionOverlayView == nil)
|
|
_transitionOverlayView = [[UIImageView alloc] init];
|
|
|
|
_transitionOverlayView.frame = _extendedInsetsImageView == nil ? self.bounds : _extendedInsetsImageView.frame;
|
|
[self insertSubview:_transitionOverlayView atIndex:0];
|
|
|
|
_transitionOverlayView.image = _extendedInsetsImageView == nil ? self.image : _extendedInsetsImageView.image;
|
|
_transitionOverlayView.backgroundColor = self.backgroundColor;
|
|
_transitionOverlayView.alpha = 1.0;
|
|
|
|
[UIView animateWithDuration:duration animations:^
|
|
{
|
|
_transitionOverlayView.alpha = 0.0;
|
|
} completion:^(__unused BOOL finished)
|
|
{
|
|
_transitionOverlayView.image = nil;
|
|
[_transitionOverlayView removeFromSuperview];
|
|
}];
|
|
|
|
if (_extendedInsetsImageView != nil)
|
|
{
|
|
_extendedInsetsImageView.alpha = 0.0f;
|
|
[UIView animateWithDuration:duration / 2.0f animations:^
|
|
{
|
|
_extendedInsetsImageView.alpha = 1.0f;
|
|
}];
|
|
}
|
|
}
|
|
else if (image != nil && duration > DBL_EPSILON)
|
|
{
|
|
if (_expectExtendedEdges)
|
|
_extendedInsetsImageView.alpha = 0.0f;
|
|
else
|
|
self.alpha = 0.0f;
|
|
[UIView animateWithDuration:duration animations:^
|
|
{
|
|
if (_expectExtendedEdges)
|
|
_extendedInsetsImageView.alpha = 1.0f;
|
|
else
|
|
self.alpha = 1.0f;
|
|
} completion:^(__unused BOOL finished)
|
|
{
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
self.alpha = 1.0f;
|
|
}
|
|
|
|
if (!_expectExtendedEdges)
|
|
{
|
|
self.image = image;
|
|
if (_extendedInsetsImageView != nil)
|
|
{
|
|
[_extendedInsetsImageView removeFromSuperview];
|
|
_extendedInsetsImageView = nil;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UIEdgeInsets insets = [image extendedEdgeInsets];
|
|
_extendedInsetsImageView.image = image;
|
|
_extendedInsetsImageView.frame = CGRectMake(-insets.left, -insets.top, self.bounds.size.width + insets.left + insets.right, self.bounds.size.height + insets.top + insets.bottom);
|
|
}
|
|
}
|
|
|
|
- (void)setSignal:(SSignal *)signal
|
|
{
|
|
_version++;
|
|
int version = _version;
|
|
__weak TGImageView *weakSelf = self;
|
|
|
|
[_disposable setDisposable:[signal startWithNext:^(id next)
|
|
{
|
|
bool synchronous = [NSThread isMainThread];
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
__strong TGImageView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf->_version == version)
|
|
{
|
|
if ([next isKindOfClass:[UIImage class]])
|
|
[strongSelf _commitImage:next partial:[next degraded] && ![next edited] loadTime:synchronous ? 0.0 : 1.0];
|
|
else if ([next respondsToSelector:@selector(floatValue)])
|
|
[strongSelf _updateProgress:[next floatValue]];
|
|
}
|
|
});
|
|
} error:^(id error)
|
|
{
|
|
TGLog(@"TGImageView signal error: %@", error);
|
|
} completed:^
|
|
{
|
|
}]];
|
|
}
|
|
|
|
@end
|