mirror of
https://github.com/danog/Telegram.git
synced 2024-12-12 17:37:33 +01:00
276 lines
8.5 KiB
Objective-C
276 lines
8.5 KiB
Objective-C
#import "TGModernFlatteningViewModel.h"
|
|
|
|
#import "TGModernFlatteningView.h"
|
|
|
|
#import "TGImageUtils.h"
|
|
|
|
CGFloat TGModernFlatteningViewModelTilingLimit = 512.0f;
|
|
|
|
@interface TGModernFlatteningViewModel ()
|
|
{
|
|
bool _needsContentUpdate;
|
|
TGModernViewContext *_context;
|
|
|
|
bool _tiledMode;
|
|
TGModernViewStorage *_viewStorage;
|
|
NSMutableArray *_tiledPartViews;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGModernFlatteningViewModel
|
|
|
|
- (instancetype)init
|
|
{
|
|
return [self initWithContext:nil];
|
|
}
|
|
|
|
- (id)initWithContext:(TGModernViewContext *)context
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
_context = context;
|
|
self.disableSubmodelAutomaticBinding = 1;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (Class)viewClass
|
|
{
|
|
return [TGModernFlatteningView class];
|
|
}
|
|
|
|
- (void)setTiledMode:(bool)tiledMode
|
|
{
|
|
if (tiledMode != _tiledMode)
|
|
{
|
|
_tiledMode = tiledMode;
|
|
|
|
if ([self boundView] != nil)
|
|
{
|
|
if (_tiledMode)
|
|
{
|
|
if (_tiledPartViews == nil)
|
|
_tiledPartViews = [[NSMutableArray alloc] init];
|
|
|
|
if (_context == nil || !_context.contentUpdatesDisabled)
|
|
[self boundView].layer.contents = nil;
|
|
}
|
|
else
|
|
{
|
|
if (_context == nil || !_context.contentUpdatesDisabled)
|
|
[self _dropTiledPartViews];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)bindViewToContainer:(UIView *)container viewStorage:(TGModernViewStorage *)viewStorage
|
|
{
|
|
[super bindViewToContainer:container viewStorage:viewStorage];
|
|
|
|
_viewStorage = viewStorage;
|
|
|
|
[self _updateSubmodelContents];
|
|
}
|
|
|
|
- (void)unbindView:(TGModernViewStorage *)viewStorage
|
|
{
|
|
[super unbindView:viewStorage];
|
|
|
|
[self _dropTiledPartViews];
|
|
|
|
_needsContentUpdate = false;
|
|
}
|
|
|
|
- (void)_dropTiledPartViews
|
|
{
|
|
if (_tiledPartViews != nil)
|
|
{
|
|
for (TGModernFlatteningView *tileView in _tiledPartViews)
|
|
{
|
|
[tileView removeFromSuperview];
|
|
[_viewStorage enqueueView:tileView];
|
|
}
|
|
|
|
[_tiledPartViews removeAllObjects];
|
|
}
|
|
}
|
|
|
|
+ (CGContextRef)_createContentContext:(CGSize)size
|
|
{
|
|
CGSize contextSize = size;
|
|
if (TGIsRetina())
|
|
{
|
|
contextSize.width *= 2.0f;
|
|
contextSize.height *= 2.0f;
|
|
}
|
|
|
|
size_t bytesPerRow = 4 * (int)contextSize.width;
|
|
bytesPerRow = (bytesPerRow + 15) & ~15;
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
|
|
|
CGContextRef context = CGBitmapContextCreate(NULL, (int)contextSize.width, (int)contextSize.height, 8, bytesPerRow, colorSpace, bitmapInfo);
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
if (TGIsRetina())
|
|
CGContextScaleCTM(context, 2.0, 2.0f);
|
|
|
|
return context;
|
|
}
|
|
|
|
- (void)animateWithSnapshot
|
|
{
|
|
TGModernFlatteningView *view = (TGModernFlatteningView *)[self boundView];
|
|
if (view != nil && !CGRectIsEmpty(self.frame) && view.layer.contents != nil)
|
|
{
|
|
UIImageView *imageView = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithCGImage:(__bridge CGImageRef)view.layer.contents]];
|
|
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
imageView.frame = self.frame;
|
|
[view.superview addSubview:imageView];
|
|
|
|
imageView.alpha = 1.0f;
|
|
view.alpha = 0.0f;
|
|
[UIView animateWithDuration:0.3 animations:^
|
|
{
|
|
view.alpha = 1.0f;
|
|
imageView.alpha = 0.0f;
|
|
} completion:^(__unused BOOL finished)
|
|
{
|
|
[imageView removeFromSuperview];
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)_updateSubmodelContents
|
|
{
|
|
TGModernFlatteningView *view = (TGModernFlatteningView *)[self boundView];
|
|
if (view != nil && !CGRectIsEmpty(self.frame))
|
|
{
|
|
if (_context != nil && _context.contentUpdatesDisabled)
|
|
_needsContentUpdate = true;
|
|
else if (!_tiledMode)
|
|
{
|
|
[self _updateSubmodelContentsForLayer:view.layer visibleRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
|
|
_needsContentUpdate = false;
|
|
}
|
|
else
|
|
{
|
|
view.layer.contents = nil;
|
|
|
|
[self _dropTiledPartViews];
|
|
_needsContentUpdate = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setNeedsSubmodelContentsUpdate
|
|
{
|
|
_needsContentUpdate = true;
|
|
}
|
|
|
|
- (void)updateSubmodelContentsIfNeeded
|
|
{
|
|
if (_needsContentUpdate)
|
|
[self _updateSubmodelContents];
|
|
}
|
|
|
|
- (void)updateSubmodelContentsForVisibleRect:(CGRect)rect
|
|
{
|
|
if (_tiledMode && (_context == nil || !_context.contentUpdatesDisabled) && [self boundView] != nil)
|
|
{
|
|
[UIView performWithoutAnimation:^
|
|
{
|
|
TGModernFlatteningView *view = (TGModernFlatteningView *)[self boundView];
|
|
|
|
CGSize tiledViewSize = CGSizeMake(view.frame.size.width, TGModernFlatteningViewModelTilingLimit);
|
|
|
|
CGRect frame = self.frame;
|
|
for (CGFloat originY = 0.0f; originY < frame.size.height; originY += tiledViewSize.height)
|
|
{
|
|
NSInteger compareOriginY = (NSInteger)originY;
|
|
int foundTileIndex = -1;
|
|
|
|
if (_tiledPartViews == nil)
|
|
_tiledPartViews = [[NSMutableArray alloc] init];
|
|
|
|
int index = -1;
|
|
for (TGModernFlatteningView *tileView in _tiledPartViews)
|
|
{
|
|
index++;
|
|
NSInteger tileOriginY = (NSInteger)tileView.frame.origin.y;
|
|
if (tileOriginY == compareOriginY)
|
|
{
|
|
foundTileIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
CGRect possibleRect = CGRectMake(rect.origin.x, originY, rect.size.width, tiledViewSize.height);
|
|
if (!CGRectIsNull(CGRectIntersection(rect, possibleRect)))
|
|
{
|
|
if (foundTileIndex == -1)
|
|
{
|
|
static NSString *tileViewIdentifier = @"TGModernFlatteningView";
|
|
|
|
TGModernFlatteningView *tileView = (TGModernFlatteningView *)[_viewStorage dequeueViewWithIdentifier:tileViewIdentifier viewStateIdentifier:nil];
|
|
if (tileView == nil)
|
|
{
|
|
tileView = [[TGModernFlatteningView alloc] init];
|
|
tileView.viewIdentifier = tileViewIdentifier;
|
|
}
|
|
|
|
CGRect tileFrame = CGRectMake(0.0f, originY, tiledViewSize.width, MIN(tiledViewSize.height, frame.size.height - originY));
|
|
|
|
tileView.frame = tileFrame;
|
|
[_tiledPartViews addObject:tileView];
|
|
[view addSubview:tileView];
|
|
|
|
[self _updateSubmodelContentsForLayer:tileView.layer visibleRect:tileFrame];
|
|
}
|
|
}
|
|
else if (foundTileIndex != -1)
|
|
{
|
|
TGModernFlatteningView *tileView = _tiledPartViews[foundTileIndex];
|
|
[tileView removeFromSuperview];
|
|
[_viewStorage enqueueView:tileView];
|
|
[_tiledPartViews removeObjectAtIndex:foundTileIndex];
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)_updateSubmodelContentsForLayer:(CALayer *)layer visibleRect:(CGRect)visibleRect
|
|
{
|
|
CGRect clipRect = CGRectIntersection(visibleRect, CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height));
|
|
if (clipRect.size.height < FLT_EPSILON)
|
|
return;
|
|
|
|
CGSize contextSize = clipRect.size;
|
|
CGContextRef context = [TGModernFlatteningViewModel _createContentContext:contextSize];
|
|
UIGraphicsPushContext(context);
|
|
|
|
CGContextTranslateCTM(context, contextSize.width / 2.0f, contextSize.height / 2.0f);
|
|
CGContextScaleCTM(context, 1.0f, -1.0f);
|
|
CGContextTranslateCTM(context, -contextSize.width / 2.0f, -contextSize.height / 2.0f);
|
|
|
|
if (visibleRect.origin.y > FLT_EPSILON)
|
|
CGContextTranslateCTM(context, 0.0f, -visibleRect.origin.y);
|
|
|
|
[self drawSubmodelsInContext:context];
|
|
|
|
UIGraphicsPopContext();
|
|
|
|
CGImageRef contextImageRef = CGBitmapContextCreateImage(context);
|
|
CGContextRelease(context);
|
|
|
|
layer.contents = (__bridge id)(contextImageRef);
|
|
CGImageRelease(contextImageRef);
|
|
}
|
|
|
|
@end
|