2021-08-05 00:02:35 +02:00

850 lines
26 KiB
Objective-C

#import "TGMenuSheetView.h"
#import "TGMenuSheetItemView.h"
#import "TGMenuSheetController.h"
#import "LegacyComponentsInternal.h"
#import "TGImageUtils.h"
#import "TGColor.h"
NSString *const TGMenuDividerTop = @"top";
NSString *const TGMenuDividerBottom = @"bottom";
const bool TGMenuSheetUseEffectView = false;
const CGFloat TGMenuSheetCornerRadius = 14.5f;
const UIEdgeInsets TGMenuSheetPhoneEdgeInsets = { 10.0f, 10.0f, 10.0f, 10.0f };
const CGFloat TGMenuSheetInterSectionSpacing = 8.0f;
@implementation TGMenuSheetScrollView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.scrollsToTop = false;
self.showsHorizontalScrollIndicator = false;
self.showsVerticalScrollIndicator = false;
}
return self;
}
- (BOOL)touchesShouldCancelInContentView:(UIView *)__unused view
{
return true;
}
@end
@interface TGMenuSheetBackgroundView : UIView
{
UIVisualEffectView *_effectView;
UIImageView *_imageView;
}
@end
@implementation TGMenuSheetBackgroundView
- (instancetype)initWithFrame:(CGRect)frame sizeClass:(UIUserInterfaceSizeClass)sizeClass dark:(bool)dark
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.clipsToBounds = true;
if (dark)
{
if (iosMajorVersion() >= 8)
{
self.layer.cornerRadius = TGMenuSheetCornerRadius;
_effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
_effectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_effectView.frame = self.bounds;
[self addSubview:_effectView];
if (@available(iOS 11.0, *)) {
_effectView.accessibilityIgnoresInvertColors = true;
}
}
else
{
self.backgroundColor = UIColorRGBA(0x181818, 0.9f);
}
}
else
{
if (TGMenuSheetUseEffectView)
{
self.layer.cornerRadius = TGMenuSheetCornerRadius;
_effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]];
_effectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_effectView.frame = self.bounds;
[self addSubview:_effectView];
}
else
{
self.backgroundColor = [UIColor whiteColor];
}
}
[self updateTraitsWithSizeClass:sizeClass];
}
return self;
}
- (void)setMaskEnabled:(bool)enabled
{
if (_effectView != nil)
return;
self.layer.cornerRadius = enabled ? TGMenuSheetCornerRadius : 0.0f;
}
- (void)updateTraitsWithSizeClass:(UIUserInterfaceSizeClass)sizeClass
{
bool hidden = (sizeClass == UIUserInterfaceSizeClassRegular);
_effectView.hidden = hidden;
_imageView.hidden = hidden;
[self setMaskEnabled:!hidden];
}
@end
@interface TGMenuSheetView () <UIScrollViewDelegate>
{
TGMenuSheetBackgroundView *_headerBackgroundView;
TGMenuSheetBackgroundView *_mainBackgroundView;
TGMenuSheetBackgroundView *_footerBackgroundView;
TGMenuSheetScrollView *_scrollView;
NSMutableArray *_itemViews;
NSMutableDictionary *_dividerViews;
UIUserInterfaceSizeClass _sizeClass;
bool _dark;
bool _borderless;
id _panHandlingItemView;
bool _expectsPreciseContentTouch;
id<LegacyComponentsContext> _context;
TGMenuSheetPallete *_pallete;
}
@end
@implementation TGMenuSheetView
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context pallete:(TGMenuSheetPallete *)pallete itemViews:(NSArray *)itemViews sizeClass:(UIUserInterfaceSizeClass)sizeClass dark:(bool)dark borderless:(bool)borderless
{
self = [super initWithFrame:CGRectZero];
if (self != nil)
{
_context = context;
_borderless = borderless;
_dark = dark;
_pallete = pallete;
_itemViews = [[NSMutableArray alloc] init];
_dividerViews = [[NSMutableDictionary alloc] init];
_sizeClass = sizeClass;
self.backgroundColor = [UIColor clearColor];
[self addItemViews:itemViews];
}
return self;
}
- (void)didChangeAbsoluteFrame
{
for (TGMenuSheetItemView *itemView in _itemViews)
{
[itemView didChangeAbsoluteFrame];
}
}
#pragma mark -
- (void)setHandleInternalPan:(void (^)(UIPanGestureRecognizer *))handleInternalPan
{
_handleInternalPan = [handleInternalPan copy];
for (TGMenuSheetItemView *itemView in self.itemViews)
{
itemView.handleInternalPan = handleInternalPan;
}
}
- (void)addItemsView:(TGMenuSheetItemView *)itemView
{
[self addItemView:itemView hasHeader:self.hasHeader hasFooter:self.hasFooter];
}
- (void)addItemView:(TGMenuSheetItemView *)itemView hasHeader:(bool)hasHeader hasFooter:(bool)hasFooter
{
TGMenuSheetItemView *previousItemView = nil;
itemView.sizeClass = _sizeClass;
itemView.tag = _itemViews.count;
itemView.handleInternalPan = [self.handleInternalPan copy];
if (_dark)
[itemView setDark];
switch (itemView.type)
{
case TGMenuSheetItemTypeDefault:
{
if (hasFooter)
[_itemViews insertObject:itemView atIndex:_itemViews.count - 1];
else
[_itemViews addObject:itemView];
if (_mainBackgroundView == nil)
{
_mainBackgroundView = [[TGMenuSheetBackgroundView alloc] initWithFrame:CGRectZero sizeClass:_sizeClass dark:_dark];
[self insertSubview:_mainBackgroundView atIndex:0];
if (_pallete != nil)
_mainBackgroundView.backgroundColor = _pallete.backgroundColor;
_scrollView = [[TGMenuSheetScrollView alloc] initWithFrame:CGRectZero];
if (@available(iOS 11.0, *)) {
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
_scrollView.delegate = self;
[_mainBackgroundView addSubview:_scrollView];
}
[_scrollView addSubview:itemView];
UIView *divider = [self createDividerForItemView:itemView previousItemView:previousItemView];
if (divider != nil)
[_scrollView addSubview:divider];
if (itemView.requiresClearBackground)
{
_mainBackgroundView.backgroundColor = [UIColor clearColor];
_expectsPreciseContentTouch = true;
}
}
break;
case TGMenuSheetItemTypeHeader:
{
if (hasHeader)
return;
[_itemViews insertObject:itemView atIndex:0];
if (_headerBackgroundView == nil)
{
_headerBackgroundView = [[TGMenuSheetBackgroundView alloc] initWithFrame:CGRectZero sizeClass:_sizeClass dark:_dark];
[self insertSubview:_headerBackgroundView atIndex:0];
if (_pallete != nil)
_headerBackgroundView.backgroundColor = _pallete.backgroundColor;
}
[_headerBackgroundView addSubview:itemView];
}
break;
case TGMenuSheetItemTypeFooter:
{
if (hasFooter)
return;
[_itemViews addObject:itemView];
if (_footerBackgroundView == nil)
{
_footerBackgroundView = [[TGMenuSheetBackgroundView alloc] initWithFrame:CGRectZero sizeClass:_sizeClass dark:_dark];
[self insertSubview:_footerBackgroundView atIndex:0];
if (_pallete != nil)
_footerBackgroundView.backgroundColor = _pallete.backgroundColor;
}
[_footerBackgroundView addSubview:itemView];
}
break;
default:
break;
}
__weak TGMenuSheetView *weakSelf = self;
itemView.layoutUpdateBlock = ^
{
__strong TGMenuSheetView *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf layoutSubviews];
if (strongSelf.menuRelayout != nil)
strongSelf.menuRelayout();
};
itemView.highlightUpdateBlock = ^(__unused bool highlighted)
{
};
}
- (void)addItemViews:(NSArray *)itemViews
{
bool hasHeader = self.hasHeader;
bool hasFooter = self.hasFooter;
for (TGMenuSheetItemView *itemView in itemViews)
{
if (_pallete != nil)
[itemView setPallete:_pallete];
[self addItemView:itemView hasHeader:hasHeader hasFooter:hasFooter];
if (itemView.type == TGMenuSheetItemTypeHeader)
hasHeader = true;
else if (itemView.type == TGMenuSheetItemTypeFooter)
hasFooter = true;
}
}
- (void)setItemViews:(NSArray *)itemViews animated:(bool)animated
{
NSMutableArray *itemViewsToDelete = [[NSMutableArray alloc] init];
for (TGMenuSheetItemView *itemView in _itemViews)
{
if (![itemViews containsObject:itemView])
{
[itemViewsToDelete addObject:itemView];
}
}
if (animated)
{
}
else
{
}
}
- (UIView *)createDividerForItemView:(TGMenuSheetItemView *)itemView previousItemView:(TGMenuSheetItemView *)previousItemView
{
if (!itemView.requiresDivider)
return nil;
UIView *topDivider = nil;
if (previousItemView != nil)
topDivider = _dividerViews[@(previousItemView.tag)][TGMenuDividerBottom];
UIView *bottomDivider = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, TGScreenPixel)];
bottomDivider.backgroundColor = _dark ? UIColorRGBA(0xffffff, 0.18f) : TGSeparatorColor();
if (_pallete != nil)
bottomDivider.backgroundColor = _pallete.separatorColor;
NSMutableDictionary *dividers = [[NSMutableDictionary alloc] init];
if (topDivider != nil)
dividers[TGMenuDividerTop] = topDivider;
dividers[TGMenuDividerBottom] = bottomDivider;
_dividerViews[@(itemView.tag)] = dividers;
return bottomDivider;
}
#pragma mark -
- (void)updateTraitsWithSizeClass:(UIUserInterfaceSizeClass)sizeClass
{
_sizeClass = sizeClass;
bool hideNonRegularItems = (_sizeClass == UIUserInterfaceSizeClassRegular);
for (TGMenuSheetItemView *itemView in _itemViews)
{
itemView.sizeClass = sizeClass;
if (itemView.type == TGMenuSheetItemTypeHeader || itemView.type == TGMenuSheetItemTypeFooter)
[itemView setHidden:hideNonRegularItems animated:false];
}
[_headerBackgroundView updateTraitsWithSizeClass:sizeClass];
[_mainBackgroundView updateTraitsWithSizeClass:sizeClass];
[_footerBackgroundView updateTraitsWithSizeClass:sizeClass];
}
#pragma mark -
- (UIEdgeInsets)edgeInsets
{
if (_sizeClass == UIUserInterfaceSizeClassRegular || _borderless)
return UIEdgeInsetsZero;
return TGMenuSheetPhoneEdgeInsets;
}
- (CGFloat)interSectionSpacing
{
return TGMenuSheetInterSectionSpacing;
}
- (CGSize)menuSize
{
return CGSizeMake(self.menuWidth, self.menuHeight);
}
- (CGFloat)menuHeight
{
CGFloat maxHeight = [_context fullscreenBounds].size.height;
if (self.maxHeight > FLT_EPSILON)
maxHeight = MIN(self.maxHeight, maxHeight);
CGFloat edgeInsetLeft = _narrowInLandscape ? self.edgeInsets.left : MAX(self.edgeInsets.left, self.safeAreaInset.left);
CGFloat edgeInsetRight = _narrowInLandscape ? self.edgeInsets.right : MAX(self.edgeInsets.right, self.safeAreaInset.right);
CGFloat width = self.menuWidth - edgeInsetLeft - edgeInsetRight;
return MIN(maxHeight, [self menuHeightForWidth:width]);
}
- (CGFloat)menuHeightForWidth:(CGFloat)width
{
CGFloat height = 0.0f;
CGFloat screenHeight = [_context fullscreenBounds].size.height;
UIEdgeInsets edgeInsets = self.edgeInsets;
bool hasRegularItems = false;
bool hasHeader = false;
bool hasFooter = false;
for (TGMenuSheetItemView *itemView in self.itemViews)
{
bool skip = false;
switch (itemView.type)
{
case TGMenuSheetItemTypeDefault:
hasRegularItems = true;
break;
case TGMenuSheetItemTypeHeader:
if (_sizeClass == UIUserInterfaceSizeClassRegular)
skip = true;
else
hasHeader = true;
break;
case TGMenuSheetItemTypeFooter:
if (_sizeClass == UIUserInterfaceSizeClassRegular)
skip = true;
else
hasFooter = true;
break;
default:
break;
}
if (!skip)
{
height += [itemView preferredHeightForWidth:width screenHeight:screenHeight];
height += itemView.contentHeightCorrection;
}
}
if (hasRegularItems || hasHeader || hasFooter)
height += self.edgeInsets.top + self.edgeInsets.bottom;
if ((hasRegularItems && hasHeader) || (hasRegularItems && hasFooter) || (hasHeader && hasFooter))
height += self.interSectionSpacing;
if (hasHeader && hasFooter && hasRegularItems)
height += self.interSectionSpacing;
if (self.keyboardOffset > 0)
{
height += self.keyboardOffset;
height -= [self.footerItemView preferredHeightForWidth:width screenHeight:screenHeight] + self.interSectionSpacing;
}
if (fabs(height - screenHeight) <= edgeInsets.top)
height = screenHeight;
return height;
}
- (CGFloat)contentHeightCorrection
{
CGFloat height = 0.0f;
for (TGMenuSheetItemView *itemView in self.itemViews)
height += itemView.contentHeightCorrection;
return height;
}
#pragma mark -
- (TGMenuSheetItemView *)headerItemView
{
if (_sizeClass == UIUserInterfaceSizeClassRegular)
return nil;
if ([(TGMenuSheetItemView *)self.itemViews.firstObject type] == TGMenuSheetItemTypeHeader)
return self.itemViews.firstObject;
return nil;
}
- (TGMenuSheetItemView *)footerItemView
{
if (_sizeClass == UIUserInterfaceSizeClassRegular)
return nil;
if ([(TGMenuSheetItemView *)self.itemViews.lastObject type] == TGMenuSheetItemTypeFooter)
return self.itemViews.lastObject;
return nil;
}
- (bool)hasHeader
{
if (_sizeClass == UIUserInterfaceSizeClassRegular)
return nil;
return (self.headerItemView != nil);
}
- (bool)hasFooter
{
if (_sizeClass == UIUserInterfaceSizeClassRegular)
return nil;
return (self.footerItemView != nil);
}
- (NSValue *)mainFrame
{
if (_mainBackgroundView != nil)
return [NSValue valueWithCGRect:_mainBackgroundView.frame];
return nil;
}
- (NSValue *)headerFrame
{
if (_headerBackgroundView != nil)
return [NSValue valueWithCGRect:_headerBackgroundView.frame];
return nil;
}
- (NSValue *)footerFrame
{
if (_footerBackgroundView != nil)
return [NSValue valueWithCGRect:_footerBackgroundView.frame];
return nil;
}
#pragma mark -
- (CGRect)activePanRect
{
if (_panHandlingItemView == nil)
{
for (TGMenuSheetItemView *itemView in _itemViews)
{
if (itemView.handlesPan)
{
_panHandlingItemView = itemView;
break;
}
}
if (_panHandlingItemView == nil)
_panHandlingItemView = [NSNull null];
}
if ([_panHandlingItemView isKindOfClass:[NSNull class]])
{
if (_scrollView.frame.size.height < _scrollView.contentSize.height)
return [self convertRect:_scrollView.frame toView:self.superview.superview];
else
return CGRectNull;
}
TGMenuSheetItemView *itemView = (TGMenuSheetItemView *)_panHandlingItemView;
return [itemView convertRect:itemView.bounds toView:self.superview.superview];
}
- (bool)passPanOffset:(CGFloat)offset
{
if (_scrollView.frame.size.height < _scrollView.contentSize.height)
{
CGFloat bottomContentOffset = (_scrollView.contentSize.height - _scrollView.frame.size.height);
if (bottomContentOffset > 0 && _scrollView.contentOffset.y > bottomContentOffset)
return false;
bool atTop = (_scrollView.contentOffset.y < FLT_EPSILON);
bool atBottom = (_scrollView.contentOffset.y - bottomContentOffset > -FLT_EPSILON);
if (atTop && offset > FLT_EPSILON)
return true;
if (atBottom && offset < 0)
return true;
return false;
}
else if ([_panHandlingItemView isKindOfClass:[NSNull class]])
{
return true;
}
TGMenuSheetItemView *itemView = (TGMenuSheetItemView *)_panHandlingItemView;
return [itemView passPanOffset:offset];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (!_expectsPreciseContentTouch)
return [super pointInside:point withEvent:event];
for (TGMenuSheetItemView *itemView in _itemViews)
{
if ([itemView pointInside:[self convertPoint:point toView:itemView] withEvent:event])
return true;
}
return false;
}
#pragma mark -
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat bottomContentOffset = (scrollView.contentSize.height - scrollView.frame.size.height);
bool atTop = (scrollView.contentOffset.y < FLT_EPSILON);
bool atBottom = (scrollView.contentOffset.y - bottomContentOffset > -FLT_EPSILON);
if ((atTop || atBottom) && _sizeClass == UIUserInterfaceSizeClassCompact)
{
if (scrollView.isTracking && scrollView.bounces && (scrollView.contentOffset.y - bottomContentOffset) < 20.0f)
{
scrollView.bounces = false;
if (atTop)
scrollView.contentOffset = CGPointMake(0, 0);
else if (atBottom)
scrollView.contentOffset = CGPointMake(0, bottomContentOffset);
}
}
else
{
scrollView.bounces = true;
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat bottomContentOffset = (scrollView.contentSize.height - scrollView.frame.size.height);
bool atTop = (scrollView.contentOffset.y < FLT_EPSILON);
bool atBottom = (scrollView.contentOffset.y - bottomContentOffset > -FLT_EPSILON);
if ((atTop || atBottom) && scrollView.bounces && !scrollView.isTracking && _sizeClass == UIUserInterfaceSizeClassCompact)
scrollView.bounces = false;
}
#pragma mark -
- (void)menuWillAppearAnimated:(bool)animated
{
for (TGMenuSheetItemView *itemView in self.itemViews)
[itemView menuView:self willAppearAnimated:animated];
}
- (void)menuDidAppearAnimated:(bool)animated
{
for (TGMenuSheetItemView *itemView in self.itemViews)
[itemView menuView:self didAppearAnimated:animated];
}
- (void)menuWillDisappearAnimated:(bool)animated
{
for (TGMenuSheetItemView *itemView in self.itemViews)
[itemView menuView:self willDisappearAnimated:animated];
}
- (void)menuDidDisappearAnimated:(bool)animated
{
for (TGMenuSheetItemView *itemView in self.itemViews)
[itemView menuView:self didDisappearAnimated:animated];
}
- (void)setSafeAreaInset:(UIEdgeInsets)safeAreaInset
{
_safeAreaInset = safeAreaInset;
[self setNeedsLayout];
}
- (void)layoutSubviews
{
CGFloat edgeInsetLeft = _narrowInLandscape ? self.edgeInsets.left : MAX(self.edgeInsets.left, self.safeAreaInset.left);
CGFloat edgeInsetRight = _narrowInLandscape ? self.edgeInsets.right : MAX(self.edgeInsets.right, self.safeAreaInset.right);
CGFloat width = self.menuWidth - edgeInsetLeft - edgeInsetRight;
CGFloat maxHeight = _sizeClass == UIUserInterfaceSizeClassCompact ? [_context fullscreenBounds].size.height : self.frame.size.height;
if (_sizeClass == UIUserInterfaceSizeClassCompact && self.maxHeight > FLT_EPSILON)
maxHeight = MIN(self.maxHeight , maxHeight);
CGFloat screenHeight = maxHeight;
bool fullscreen = fabs(maxHeight - [_context fullscreenBounds].size.height) < FLT_EPSILON;
if (_sizeClass == UIUserInterfaceSizeClassCompact)
{
if (self.headerItemView != nil)
maxHeight -= [self.headerItemView preferredHeightForWidth:width screenHeight:screenHeight] + self.interSectionSpacing;
if (self.keyboardOffset > FLT_EPSILON)
maxHeight -= self.keyboardOffset;
else if (self.footerItemView != nil)
maxHeight -= [self.footerItemView preferredHeightForWidth:width screenHeight:screenHeight] + self.interSectionSpacing;
}
CGFloat contentHeight = 0;
bool hasRegularItems = false;
NSUInteger i = 0;
TGMenuSheetItemView *condensableItemView = nil;
for (TGMenuSheetItemView *itemView in self.itemViews)
{
if (itemView.type == TGMenuSheetItemTypeDefault)
{
hasRegularItems = true;
CGFloat height = [itemView preferredHeightForWidth:width screenHeight:screenHeight];
itemView.screenHeight = screenHeight;
itemView.frame = CGRectMake(0, contentHeight, width, height);
contentHeight += height;
NSUInteger lastItem = (self.footerItemView != nil) ? self.itemViews.count - 2 : self.itemViews.count - 1;
if (itemView.requiresDivider && i != lastItem)
{
UIView *divider = _dividerViews[@(itemView.tag)][TGMenuDividerBottom];
if (divider != nil)
divider.frame = CGRectMake(0, CGRectGetMaxY(itemView.frame) - divider.frame.size.height, width, divider.frame.size.height);
}
if (itemView.condensable)
condensableItemView = itemView;
}
i++;
}
contentHeight += self.contentHeightCorrection;
UIEdgeInsets edgeInsets = self.edgeInsets;
CGSize statusBarSize = [[LegacyComponentsGlobals provider] statusBarFrame].size;
CGFloat statusBarHeight = MIN(statusBarSize.width, statusBarSize.height);
statusBarHeight = MAX(statusBarHeight, 20.0f);
if (_safeAreaInset.top > FLT_EPSILON)
statusBarHeight = _safeAreaInset.top;
if (fullscreen)
{
if (contentHeight > maxHeight - edgeInsets.top - edgeInsets.bottom)
edgeInsets.top = statusBarHeight;
if (fabs(contentHeight - maxHeight + edgeInsets.bottom) <= statusBarHeight)
edgeInsets.top = statusBarHeight;
}
if (_sizeClass == UIUserInterfaceSizeClassRegular)
edgeInsets = UIEdgeInsetsZero;
maxHeight -= edgeInsets.top + edgeInsets.bottom;
if (self.keyboardOffset > FLT_EPSILON && contentHeight > maxHeight && condensableItemView != nil)
{
CGFloat difference = contentHeight - maxHeight;
contentHeight -= difference;
CGRect frame = condensableItemView.frame;
condensableItemView.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height - difference);
if (condensableItemView.requiresDivider)
{
UIView *divider = _dividerViews[@(condensableItemView.tag)][TGMenuDividerBottom];
if (divider != nil)
{
CGRect dividerFrame = divider.frame;
divider.frame = CGRectMake(dividerFrame.origin.x, dividerFrame.origin.y - difference, dividerFrame.size.width, dividerFrame.size.height);
}
}
bool moveNextItems = false;
for (TGMenuSheetItemView *itemView in self.itemViews)
{
if (moveNextItems)
{
CGRect frame = itemView.frame;
itemView.frame = CGRectMake(frame.origin.x, frame.origin.y - difference, frame.size.width, frame.size.height);
if (itemView.requiresDivider)
{
UIView *divider = _dividerViews[@(itemView.tag)][TGMenuDividerBottom];
if (divider != nil)
{
CGRect dividerFrame = divider.frame;
divider.frame = CGRectMake(dividerFrame.origin.x, dividerFrame.origin.y - difference, dividerFrame.size.width, dividerFrame.size.height);
}
}
}
else if (itemView == condensableItemView)
{
moveNextItems = true;
}
}
}
for (TGMenuSheetItemView *itemView in self.itemViews)
[itemView _didLayoutSubviews];
CGFloat topInset = edgeInsets.top;
if (self.headerItemView != nil)
{
_headerBackgroundView.frame = CGRectMake(edgeInsetLeft, topInset, width, [self.headerItemView preferredHeightForWidth:width screenHeight:screenHeight]);
self.headerItemView.frame = _headerBackgroundView.bounds;
topInset = CGRectGetMaxY(_headerBackgroundView.frame) + TGMenuSheetInterSectionSpacing;
}
if (hasRegularItems)
{
CGFloat additionalHeight = _borderless ? 256.0f : 0.0f;
_mainBackgroundView.frame = CGRectMake(edgeInsetLeft, topInset, width, MIN(contentHeight, maxHeight) + additionalHeight);
_scrollView.frame = CGRectMake(0.0f, 0.0f, _mainBackgroundView.frame.size.width, _mainBackgroundView.frame.size.height - additionalHeight);
_scrollView.contentSize = CGSizeMake(width, contentHeight);
}
if (self.footerItemView != nil)
{
CGFloat height = [self.footerItemView preferredHeightForWidth:width screenHeight:screenHeight];
CGFloat top = self.menuHeight - edgeInsets.bottom - height;
if (hasRegularItems && self.keyboardOffset < FLT_EPSILON)
top = CGRectGetMaxY(_mainBackgroundView.frame) + TGMenuSheetInterSectionSpacing;
_footerBackgroundView.frame = CGRectMake(edgeInsetLeft, top, width, height);
self.footerItemView.frame = _footerBackgroundView.bounds;
}
}
@end