1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGShareSheetView.m
2016-02-25 01:03:51 +01:00

730 lines
28 KiB
Objective-C

#import "TGShareSheetView.h"
#import "TGShareSheetWindow.h"
#import "TGShareSheetItemView.h"
#import "TGImageUtils.h"
#import "TGVerticalSwipeDismissGestureRecognizer.h"
#import "TGAttachmentSheetScrollView.h"
#import "TGShareSheetButtonItemView.h"
#import "TGObserverProxy.h"
#import <Accelerate/Accelerate.h>
static CGFloat blurStaticOffset = 10.0f;
static CGFloat blurDynamicOffset = 5.0f;
@interface TGShareSheetView ()
{
UIView *_backgroundView;
UIView *_containerView;
UIImageView *_containerBackgroundView;
UIImageView *_scrollViewMask;
CGFloat _overscrollHeight;
CGFloat _swipeStart;
CADisplayLink *_displayLink;
NSArray *_blurLayers;
CGPoint _lastPosition;
UIView *_scrollViewContainer;
TGAttachmentSheetScrollView *_scrollView;
NSArray *_separatorViews;
TGVerticalSwipeDismissGestureRecognizer *_swipeRecognizer;
UIImageView *_cancelContainer;
TGShareSheetButtonItemView *_cancelItemView;
CGFloat _keyboardOffset;
id _keyboardWillChangeFrameProxy;
}
@end
@implementation TGShareSheetView
+ (UIImage *)containerBackgroundWithFirst:(bool)first last:(bool)last {
static UIImage *singleImage = nil;
static UIImage *firstImage = nil;
static UIImage *lastImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CGFloat diameter = 30.0f;
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
singleImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)];
UIGraphicsEndImageContext();
}
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextFillRect(context, CGRectMake(0.0f, diameter / 2.0f, diameter, diameter / 2.0f));
firstImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)];
UIGraphicsEndImageContext();
}
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter / 2.0f));
lastImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)];
UIGraphicsEndImageContext();
}
});
if (first && last) {
return singleImage;
} else if (first) {
return firstImage;
} else {
return lastImage;
}
}
+ (UIImage *)selectionBackgroundWithFirst:(bool)first last:(bool)last {
static UIImage *singleImage = nil;
static UIImage *firstImage = nil;
static UIImage *lastImage = nil;
static UIImage *middleImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CGFloat diameter = 30.0f;
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, TGSelectionColor().CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
singleImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)];
UIGraphicsEndImageContext();
}
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, TGSelectionColor().CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextFillRect(context, CGRectMake(0.0f, diameter / 2.0f, diameter, diameter / 2.0f));
firstImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)];
UIGraphicsEndImageContext();
}
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, TGSelectionColor().CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter / 2.0f));
lastImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)];
UIGraphicsEndImageContext();
}
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, TGSelectionColor().CGColor);
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter));
middleImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)];
UIGraphicsEndImageContext();
}
});
if (first && last) {
return singleImage;
} else if (first) {
return firstImage;
} else if (last) {
return lastImage;
} else {
return middleImage;
}
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
_backgroundView = [[UIView alloc] init];
_backgroundView.backgroundColor = UIColorRGBA(0x000000, 0.4f);
[_backgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTapped)]];
[self addSubview:_backgroundView];
_containerView = [[UIView alloc] init];
_containerView.userInteractionEnabled = true;
[self addSubview:_containerView];
_containerBackgroundView = [[UIImageView alloc] initWithImage:[TGShareSheetView containerBackgroundWithFirst:true last:true]];
[_containerView addSubview:_containerBackgroundView];
_scrollViewMask = [[UIImageView alloc] initWithImage:[TGShareSheetView containerBackgroundWithFirst:true last:true]];
__weak TGShareSheetView *weakSelf = self;
_cancelItemView = [[TGShareSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") pressed:^{
__strong TGShareSheetView *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf->_cancel) {
strongSelf->_cancel();
}
}
}];
[_cancelItemView setBold:true];
_cancelContainer = [[UIImageView alloc] initWithImage:[TGShareSheetView containerBackgroundWithFirst:true last:true]];
_cancelContainer.userInteractionEnabled = true;
[_cancelContainer addSubview:_cancelItemView];
[_containerView addSubview:_cancelContainer];
_scrollViewContainer = [[UIView alloc] init];
[_containerView addSubview:_scrollViewContainer];
_scrollView = [[TGAttachmentSheetScrollView alloc] init];
_scrollView.delaysContentTouches = false;
_scrollView.canCancelContentTouches = true;
[_scrollViewContainer addSubview:_scrollView];
_swipeRecognizer = [[TGVerticalSwipeDismissGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
_swipeRecognizer.cancelsTouchesInView = true;
[self addGestureRecognizer:_swipeRecognizer];
_overscrollHeight = 320.0f;
[self updateLayout];
_keyboardWillChangeFrameProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification];
}
return self;
}
- (CGFloat)swipeOffsetForOffset:(CGFloat)offset
{
if (offset < 0.0f)
{
CGFloat c = 0.2f;
CGFloat d = 320.0f;
return (CGFloat)((1.0f - (1.0f / ((offset * c / d) + 1.0))) * d);
}
return offset;
}
- (CGFloat)clampVelocity:(CGFloat)velocity
{
CGFloat value = velocity < 0.0f ? -velocity : velocity;
value = MIN(30.0f, value);
return velocity < 0.0f ? -value : value;
}
- (UIEdgeInsets)insets {
return UIEdgeInsetsMake(8.0f, 10.0f, 10.0f, 10.0f);
}
- (void)animateToDefaultPosition:(CGFloat)__unused velocity
{
UIEdgeInsets insets = [self insets];
CGFloat cancelHeight = insets.top + [_cancelItemView preferredHeightForMaximumHeight:CGFLOAT_MAX] + insets.bottom;
if (iosMajorVersion() >= 7)
{
[UIView animateWithDuration:0.45 delay:0.0 usingSpringWithDamping:0.48f initialSpringVelocity:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^
{
_containerView.frame = CGRectMake(insets.left, self.frame.size.height - _containerView.frame.size.height - MAX(0.0f, _keyboardOffset - cancelHeight), _containerView.frame.size.width, _containerView.frame.size.height);
} completion:nil];
}
else
{
[UIView animateWithDuration:0.2 animations:^
{
_containerView.frame = CGRectMake(insets.left, self.frame.size.height - _containerView.frame.size.height - MAX(0.0f, _keyboardOffset - cancelHeight), _containerView.frame.size.width, _containerView.frame.size.height);
}];
}
}
- (void)swipeGesture:(TGVerticalSwipeDismissGestureRecognizer *)recognizer
{
UIEdgeInsets insets = [self insets];
if (recognizer.state == UIGestureRecognizerStateBegan)
{
if (_containerView.layer.presentationLayer != nil)
_containerView.layer.position = ((CALayer *)_containerView.layer.presentationLayer).position;
_swipeStart = [recognizer locationInView:self].y;
}
else if (recognizer.state == UIGestureRecognizerStateChanged)
{
CGFloat cancelHeight = insets.top + [_cancelItemView preferredHeightForMaximumHeight:CGFLOAT_MAX] + insets.bottom;
CGFloat offset = [recognizer locationInView:self].y - _swipeStart;
CGFloat bandOffset = [self swipeOffsetForOffset:offset];
_containerView.frame = CGRectMake(insets.left, self.frame.size.height - _containerView.frame.size.height - MAX(0.0f, _keyboardOffset - cancelHeight) + bandOffset, _containerView.frame.size.width, _containerView.frame.size.height);
}
else if (recognizer.state == UIGestureRecognizerStateRecognized)
{
CGFloat velocity = [recognizer velocityInView:self].y;
CGFloat offset = [recognizer locationInView:self].y - _swipeStart;
if (offset > (_containerView.frame.size.height - _overscrollHeight) / 3.0f || velocity > 200.0f)
{
[self endEditing:true];
[self animateOutWithVelocity:MAX(0.0f, velocity) forInterchange:false completion:^
{
TGShareSheetWindow *window = self.attachmentSheetWindow;
window.hidden = true;
if (window.dismissalBlock != nil)
window.dismissalBlock();
}];
}
else
[self animateToDefaultPosition:[recognizer velocityInView:self].y];
}
else if (recognizer.state == UIGestureRecognizerStateCancelled)
{
[self animateToDefaultPosition:[recognizer velocityInView:self].y];
}
}
- (void)backgroundTapped
{
[self endEditing:true];
[self animateOut:^
{
TGShareSheetWindow *window = self.attachmentSheetWindow;
window.hidden = true;
if (window.dismissalBlock != nil)
window.dismissalBlock();
}];
}
- (NSArray *)blurryLayer:(CALayer *)layer size:(CGSize)size withBlurLevel:(CGFloat)blur {
if (blur < 0.f || blur > 1.f) {
blur = 0.5f;
}
int boxSize = (int)(blur * 100);
boxSize = boxSize - (boxSize % 2) + 1;
vImage_Buffer inBuffer, outBuffer;
CGFloat scale = 1.0f;
const struct { int width, height; } targetContextSize = { (int)(size.width * scale), (int)(size.height * scale) };
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
UIGraphicsPushContext(targetContext);
CGContextTranslateCTM(targetContext, targetContextSize.width / 2.0f, targetContextSize.height / 2.0f);
CGContextScaleCTM(targetContext, 1.0f, -1.0f);
CGContextTranslateCTM(targetContext, -targetContextSize.width / 2.0f, -targetContextSize.height / 2.0f);
[layer renderInContext:targetContext];
UIGraphicsPopContext();
inBuffer.data = targetMemory;
inBuffer.width = targetContextSize.width;
inBuffer.height = targetContextSize.height;
inBuffer.rowBytes = targetBytesPerRow;
void *pixelBuffer = malloc(targetBytesPerRow * targetContextSize.height);
outBuffer.data = pixelBuffer;
outBuffer.width = targetContextSize.width;
outBuffer.height = targetContextSize.height;
outBuffer.rowBytes = targetBytesPerRow;
TG_TIMESTAMP_DEFINE(convolve)
vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, 1.0f, NULL, kvImageEdgeExtend);
TG_TIMESTAMP_MEASURE(convolve)
memcpy(targetMemory, pixelBuffer, targetBytesPerRow * targetContextSize.height);
free(pixelBuffer);
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
UIImage *image0 = [[UIImage alloc] initWithCGImage:bitmapImage scale:scale orientation:UIImageOrientationUp];
CGImageRelease(bitmapImage);
CGContextRelease(targetContext);
free(targetMemory);
return @[image0];
}
- (NSArray *)containerLayerSnapshots
{
return [self blurryLayer:_containerView.layer size:CGSizeMake(_containerView.frame.size.width, _containerView.frame.size.height - _overscrollHeight) withBlurLevel:0.8f];
}
- (void)beginBlur
{
if (_displayLink == nil)
{
NSMutableArray *blurLayers = [[NSMutableArray alloc] init];
for (UIImage *image in [self containerLayerSnapshots])
{
UIImageView *blurLayer0 = [[UIImageView alloc] initWithImage:image];
blurLayer0.frame = CGRectMake(0.0f, blurStaticOffset + blurDynamicOffset, _containerView.frame.size.width, _containerView.frame.size.height - _overscrollHeight + 0.0f);
[_containerView insertSubview:blurLayer0 atIndex:0];
[blurLayers addObject:blurLayer0];
UIImageView *blurLayer1 = [[UIImageView alloc] initWithImage:image];
blurLayer1.frame = CGRectMake(0.0f, 12.0f, _containerView.frame.size.width, _containerView.frame.size.height - _overscrollHeight + 0.0f);
[_containerView addSubview:blurLayer1];
[blurLayers addObject:blurLayer1];
}
_blurLayers = blurLayers;
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
_lastPosition = _containerView.layer.position;
}
}
- (void)endBlur
{
[_displayLink invalidate];
_displayLink = nil;
for (UIView *view in _blurLayers)
{
[view removeFromSuperview];
}
_blurLayers = nil;
}
- (void)animateIn
{
[self animateInInitial:true];
}
- (void)animateInInitial:(bool)initial
{
[self layoutIfNeeded];
UIEdgeInsets insets = [self insets];
_containerView.frame = CGRectMake(insets.left, self.frame.size.height, self.frame.size.width - insets.left - insets.right, _containerView.frame.size.height);
if (initial)
_backgroundView.alpha = 0.0f;
if (iosMajorVersion() >= 7)
{
//[self beginBlur];
[UIView animateWithDuration:0.12 delay:0.0 options:(7 << 16) | UIViewAnimationOptionAllowUserInteraction animations:^
{
_containerView.frame = CGRectMake(insets.left, self.frame.size.height - _containerView.frame.size.height, self.frame.size.width - insets.left - insets.right, _containerView.frame.size.height);
_backgroundView.alpha = 1.0f;
} completion:^(__unused BOOL finished)
{
[self dispatchDidAppear];
//[self endBlur];
}];
}
else
{
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^
{
_containerView.frame = CGRectMake(insets.left, self.frame.size.height - _containerView.frame.size.height, self.frame.size.width - insets.left - insets.right, _containerView.frame.size.height);
_backgroundView.alpha = 1.0f;
} completion:^(__unused BOOL finished)
{
[self dispatchDidAppear];
}];
}
}
- (void)dispatchDidAppear
{
for (TGShareSheetItemView *itemView in _items)
{
[itemView sheetDidAppear];
}
}
- (void)dispatchWillDisappear
{
for (TGShareSheetItemView *itemView in _items)
{
[itemView sheetWillDisappear];
}
}
- (void)tick:(CADisplayLink *)__unused displayLink
{
CGPoint realPosition = _containerView.layer.presentationLayer == nil ? _containerView.layer.position : ((CALayer *)_containerView.layer.presentationLayer).position;
CGPoint lastPosition = _lastPosition;
CGFloat dy = (CGFloat)(ABS(realPosition.y - lastPosition.y));
CGFloat delta = (CGFloat)(dy);
CGFloat unboundedOpacity = 0.0f;
if (delta > FLT_EPSILON)
{
//unboundedOpacity = (CGFloat)log10(delta) / 3.0f;
unboundedOpacity = delta / 40.0f;
}
CGFloat opacity = (CGFloat)MAX(MIN(unboundedOpacity, 1.0f), 0.0f);
CGFloat offset = blurStaticOffset + blurDynamicOffset * opacity;
CGFloat secondaryOpacity = opacity * 0.06f;
if (_blurLayers.count != 0)
{
((UIView *)_blurLayers[0]).alpha = opacity;
CGRect frame = ((UIView *)_blurLayers[0]).frame;
frame.origin.y = offset;
((UIView *)_blurLayers[0]).frame = frame;
((UIView *)_blurLayers[1]).alpha = secondaryOpacity;
((UIView *)_blurLayers[1]).frame = frame;
}
_lastPosition = realPosition;
}
- (void)animateOut:(void (^)())completion
{
[self animateOutWithVelocity:0.0f forInterchange:false completion:completion];
}
- (void)animateOutForInterchange:(bool)interchange completion:(void (^)())completion
{
[self animateOutWithVelocity:interchange ? 2000.0f : 0.0f forInterchange:interchange completion:completion];
}
- (void)animateOutWithVelocity:(CGFloat)velocity forInterchange:(bool)interchange completion:(void (^)())completion
{
const CGFloat minVelocity = 400.0f;
if (ABS(velocity) < minVelocity)
velocity = (velocity < 0.0f ? -1.0f : 1.0f) * minVelocity;
CGFloat distance = (velocity < FLT_EPSILON ? -1.0f : 1.0f) * (self.frame.size.height - _containerView.frame.origin.y);
NSTimeInterval duration = MIN(0.3, (CGFloat)(fabs(distance)) / velocity);
[self dispatchWillDisappear];
UIEdgeInsets insets = [self insets];
[UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^
{
_containerView.frame = CGRectMake(insets.left, self.frame.size.height, self.frame.size.width - insets.left - insets.right, _containerView.frame.size.height);
if (!interchange)
_backgroundView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
if (completion)
completion();
}];
}
- (void)setItems:(NSArray *)items
{
for (TGShareSheetItemView *itemView in _items)
{
[itemView removeFromSuperview];
}
_items = items;
NSMutableArray *separatorViews = [[NSMutableArray alloc] init];
__weak TGShareSheetView *weakSelf = self;
for (TGShareSheetItemView *itemView in items)
{
itemView.preferredHeightNeedsUpdate = ^(__unused TGShareSheetItemView *itemView) {
__strong TGShareSheetView *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf updateLayout];
}
};
if (itemView != items.lastObject) {
UIView *separatorView = [[UIView alloc] init];
separatorView.backgroundColor = TGSeparatorColor();
[separatorViews addObject:separatorView];
[_scrollView addSubview:separatorView];
}
[_scrollView addSubview:itemView];
}
_separatorViews = separatorViews;
[self updateLayout];
}
- (void)performAnimated:(bool)animated updates:(void (^)(void))updates completion:(void (^)(void))completion
{
[self performAnimated:animated updates:updates stickToBottom:false completion:completion];
}
- (void)performAnimated:(bool)animated updates:(void (^)(void))updates stickToBottom:(bool)stickToBottom completion:(void (^)(void))completion
{
if (updates == nil)
return;
void (^updatesBlock)(void) = ^
{
updates();
[self updateLayout];
if (stickToBottom)
_scrollView.contentOffset = CGPointMake(0, _scrollView.contentSize.height - _scrollView.bounds.size.height);
};
if (animated)
{
[UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) | UIViewAnimationOptionLayoutSubviews animations:updatesBlock
completion:^(__unused BOOL finished)
{
if (completion != nil)
completion();
}];
}
else
{
updatesBlock();
if (completion != nil)
completion();
}
}
- (void)scrollToBottomAnimated:(bool)animated
{
[_scrollView setContentOffset:CGPointMake(0, _scrollView.contentSize.height - _scrollView.bounds.size.height) animated:animated];
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
[self updateLayout];
}
- (void)updateLayout
{
_backgroundView.frame = self.bounds;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
CGFloat minScreenHeight = MIN(screenSize.width, screenSize.height);
CGFloat maxScreenSide = MAX(screenSize.width, screenSize.height);
UIEdgeInsets insets = [self insets];
CGFloat cancelHeight = insets.top + [_cancelItemView preferredHeightForMaximumHeight:CGFLOAT_MAX] + insets.bottom;
CGFloat maxHeight = self.frame.size.width < (maxScreenSide - FLT_EPSILON) ? (maxScreenSide - 20.0f - 44.0f + 4.0f - cancelHeight) : (minScreenHeight - 20.0f - 32.0f + 4.0f - cancelHeight);
if (self.frame.size.width > self.frame.size.height) {
maxHeight += 26.0f;
}
CGFloat maxItemHeight = self.frame.size.width < (maxScreenSide - FLT_EPSILON) ? (maxScreenSide - 20.0f - MAX(cancelHeight, _keyboardOffset)) : (minScreenHeight - 20.0f - MAX(cancelHeight, _keyboardOffset));
maxItemHeight = MAX(80.0f, maxItemHeight);
CGFloat containerWidth = self.frame.size.width - insets.left - insets.right;
CGFloat containerHeight = 0.0f;
CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f;
NSInteger index = -1;
for (TGShareSheetItemView *itemView in _items)
{
index++;
CGFloat itemPreferredHeight = [itemView preferredHeightForMaximumHeight:maxItemHeight];
itemView.frame = CGRectMake(0.0f, containerHeight, containerWidth, itemPreferredHeight);
bool first = itemView == _items.firstObject;
bool last = itemView == _items.lastObject;
[itemView setHighlightedImage:[TGShareSheetView selectionBackgroundWithFirst:first last:last]];
containerHeight += itemPreferredHeight;
if (!last) {
UIView *separatorView = _separatorViews[index];
separatorView.frame = CGRectMake(0.0f, containerHeight, containerWidth, separatorHeight);
containerHeight += separatorHeight;
}
}
_containerBackgroundView.frame = CGRectMake(0.0f, 0.0f, containerWidth, MIN(maxHeight, containerHeight));
_cancelContainer.frame = CGRectMake(0.0f, MIN(maxHeight, containerHeight) + insets.top + MAX(0.0, _keyboardOffset - cancelHeight), containerWidth, [_cancelItemView preferredHeightForMaximumHeight:CGFLOAT_MAX]);
[_cancelItemView setHighlightedImage:[TGShareSheetView selectionBackgroundWithFirst:true last:true]];
_cancelItemView.frame = CGRectMake(0.0f, 0.0f, containerWidth, [_cancelItemView preferredHeightForMaximumHeight:CGFLOAT_MAX]);
_scrollView.contentSize = CGSizeMake(self.frame.size.width, containerHeight);
_containerView.frame = CGRectMake(insets.left, self.frame.size.height - MIN(maxHeight, containerHeight) - cancelHeight - MAX(0.0f, _keyboardOffset - cancelHeight), containerWidth, MIN(maxHeight, containerHeight) + cancelHeight);
_scrollViewContainer.frame = CGRectMake(0.0f, 0.0f, _containerView.frame.size.width, _containerView.frame.size.height - cancelHeight);
_scrollView.frame = _scrollViewContainer.bounds;
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, containerHeight);
_scrollViewMask.frame = _scrollViewContainer.bounds;
bool collapsed = containerHeight > maxHeight;
_swipeRecognizer.enabled = !collapsed;
/*if (collapsed) {
if (_scrollViewContainer.maskView == nil) {
_scrollViewContainer.maskView = _scrollViewMask;
}
} else {
if (_scrollViewContainer.maskView != nil) {
_scrollViewContainer.maskView = nil;
}
}*/
}
- (void)keyboardWillChangeFrame:(NSNotification *)notification {
NSTimeInterval duration = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] == nil ? 0.3 : [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
int curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];
CGRect screenKeyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [self convertRect:screenKeyboardFrame fromView:nil];
CGFloat keyboardHeight = (keyboardFrame.size.height <= FLT_EPSILON || keyboardFrame.size.width <= FLT_EPSILON) ? 0.0f : (self.frame.size.height - keyboardFrame.origin.y);
keyboardHeight = MAX(keyboardHeight, 0.0f);
if (keyboardHeight > FLT_EPSILON) {
keyboardHeight += [self insets].bottom;
}
bool followKeyboard = false;
for (TGShareSheetItemView *itemView in _items) {
if ([itemView followsKeyboard]) {
followKeyboard = true;
break;
}
}
if (followKeyboard) {
if (duration >= FLT_EPSILON) {
[UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
[self updateKeyboardOffset:keyboardHeight];
} completion:nil];
} else {
[self updateKeyboardOffset:keyboardHeight];
}
}
}
- (void)updateKeyboardOffset:(CGFloat)keyboardOffset {
_keyboardOffset = keyboardOffset;
[self updateLayout];
}
@end