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

1464 lines
53 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 "TGModernGalleryController.h"
#import "TGHacks.h"
#import "TGModernGalleryView.h"
#import "TGAppDelegate.h"
#import "TGModernGalleryItem.h"
#import "TGModernGalleryScrollView.h"
#import "TGModernGalleryItemView.h"
#import "TGModernGalleryTransitionView.h"
#import "TGModernGalleryContainerView.h"
#import "TGModernGalleryInterfaceView.h"
#import "TGModernGalleryDefaultInterfaceView.h"
#import "TGModernGalleryModel.h"
#import "TGAnimationBlockDelegate.h"
#import <pop/POP.h>
#import <objc/runtime.h>
#import "JNWSpringAnimation.h"
#import "TGImageUtils.h"
#import "TGSharedMediaCollectionView.h"
#define TGModernGalleryItemPadding 20.0f
@interface TGModernGalleryController () <UIScrollViewDelegate, TGModernGalleryScrollViewDelegate, TGModernGalleryItemViewDelegate, TGKeyCommandResponder>
{
NSMutableDictionary *_reusableItemViewsByIdentifier;
NSMutableArray *_visibleItemViews;
bool _preloadVisibleItemViews;
TGModernGalleryView *_view;
UIView<TGModernGalleryDefaultHeaderView> *_defaultHeaderView;
UIView<TGModernGalleryDefaultFooterView> *_defaultFooterView;
NSUInteger _lastReportedFocusedIndex;
bool _synchronousBoundsChange;
bool _reloadingItems;
bool _isBeingDismissed;
UIStatusBarStyle _statusBarStyle;
}
@end
@implementation TGModernGalleryController
- (instancetype)init
{
self = [super init];
if (self != nil)
{
self.automaticallyManageScrollViewInsets = false;
self.autoManageStatusBarBackground = false;
_lastReportedFocusedIndex = NSNotFound;
_statusBarStyle = UIStatusBarStyleLightContent;
_animateTransition = true;
_showInterface = true;
_adjustsStatusBarVisibility = true;
}
return self;
}
- (void)dealloc
{
_view.scrollView.delegate = nil;
_view.scrollView.scrollDelegate = nil;
}
- (void)dismiss
{
[super dismiss];
if (_completedTransitionOut)
_completedTransitionOut();
}
- (BOOL)prefersStatusBarHidden
{
if (self.childViewControllers.count > 0)
return [self.childViewControllers.lastObject prefersStatusBarHidden];
return [super prefersStatusBarHidden];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return _statusBarStyle;
}
- (BOOL)shouldAutorotate
{
return [super shouldAutorotate] && (_view == nil || [_view shouldAutorotate]) && ([_model _shouldAutorotate]);
}
- (void)dismissWhenReady
{
/*for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
if (itemView.index == [self currentItemIndex])
{
if ([itemView dismissControllerNowOrSchedule])
[self dismiss];
return;
}
}*/
[self dismiss];
}
- (bool)isFullyOpaque
{
CGFloat alpha = 0.0f;
[_view.backgroundColor getWhite:NULL alpha:&alpha];
return alpha >= 1.0f - FLT_EPSILON;
}
- (void)setModel:(TGModernGalleryModel *)model
{
if (_model != model)
{
_model = model;
__weak TGModernGalleryController *weakSelf = self;
_model.itemsUpdated = ^(id<TGModernGalleryItem> item)
{
__strong TGModernGalleryController *strongSelf = weakSelf;
if (strongSelf != nil && !strongSelf->_isBeingDismissed)
[strongSelf reloadDataAtItem:item synchronously:false];
};
_model.focusOnItem = ^(id<TGModernGalleryItem> item)
{
__strong TGModernGalleryController *strongSelf = weakSelf;
NSUInteger index = [strongSelf.model.items indexOfObject:item];
[strongSelf setCurrentItemIndex:index == NSNotFound ? 0 : index synchronously:false];
};
_model.actionSheetView = ^
{
__strong TGModernGalleryController *strongSelf = weakSelf;
return strongSelf.view;
};
_model.viewControllerForModalPresentation = ^
{
__strong TGModernGalleryController *strongSelf = weakSelf;
return strongSelf;
};
_model.visibleItems = ^NSArray *()
{
__strong TGModernGalleryController *strongSelf = weakSelf;
return [strongSelf visibleItems];
};
_model.dismiss = ^(bool animated, bool asModal)
{
__strong TGModernGalleryController *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf->_isBeingDismissed = true;
if (asModal)
{
strongSelf->_statusBarStyle = UIStatusBarStyleDefault;
strongSelf.view.hidden = true;
if (strongSelf.completedTransitionOut)
strongSelf.completedTransitionOut();
[strongSelf dismissViewControllerAnimated:true completion:^
{
dispatch_async(dispatch_get_main_queue(), ^
{
[strongSelf dismiss];
});
}];
}
else
{
if (animated)
{
if (iosMajorVersion() >= 7)
{
[strongSelf animateStatusBarTransition:0.2];
strongSelf->_statusBarStyle = UIStatusBarStyleDefault;
[strongSelf setNeedsStatusBarAppearanceUpdate];
}
if (strongSelf.adjustsStatusBarVisibility)
{
[UIView animateWithDuration:0.2 animations:^
{
[TGHacks setApplicationStatusBarAlpha:1.0f];
}];
}
[strongSelf->_view simpleTransitionOutWithVelocity:0.0f completion:^
{
[strongSelf dismiss];
}];
}
else
{
if (iosMajorVersion() >= 7)
{
strongSelf->_statusBarStyle = UIStatusBarStyleDefault;
[strongSelf setNeedsStatusBarAppearanceUpdate];
}
[strongSelf dismiss];
}
}
}
};
_model.dismissWhenReady = ^
{
__strong TGModernGalleryController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf dismissWhenReady];
}
};
[self reloadDataAtItem:_model.focusItem synchronously:false];
}
}
- (void)itemViewIsReadyForScheduledDismiss:(TGModernGalleryItemView *)__unused itemView
{
[self dismiss];
}
- (void)itemViewDidRequestInterfaceShowHide:(TGModernGalleryItemView *)__unused itemView
{
[_view showHideInterface];
}
- (TGModernGalleryItemView *)dequeueViewForItem:(id<TGModernGalleryItem>)item
{
if (item == nil || [item viewClass] == nil)
return nil;
NSString *identifier = NSStringFromClass([item viewClass]);
NSMutableArray *views = _reusableItemViewsByIdentifier[identifier];
if (views == nil)
{
views = [[NSMutableArray alloc] init];
_reusableItemViewsByIdentifier[identifier] = views;
}
if (views.count == 0)
{
Class itemClass = [item viewClass];
TGModernGalleryItemView *itemView = [[itemClass alloc] init];
itemView.delegate = self;
itemView.defaultFooterView = _defaultFooterView;
itemView.defaultFooterAccessoryLeftView = [_model createDefaultLeftAccessoryView];
itemView.defaultFooterAccessoryRightView = [_model createDefaultRightAccessoryView];
return itemView;
}
TGModernGalleryItemView *itemView = [views lastObject];
[views removeLastObject];
itemView.delegate = self;
[itemView prepareForReuse];
return itemView;
}
- (void)enqueueView:(TGModernGalleryItemView *)itemView
{
if (itemView == nil)
return;
itemView.delegate = nil;
[itemView prepareForRecycle];
NSString *identifier = NSStringFromClass([itemView class]);
if (identifier != nil)
{
NSMutableArray *views = _reusableItemViewsByIdentifier[identifier];
if (views == nil)
{
views = [[NSMutableArray alloc] init];
_reusableItemViewsByIdentifier[identifier] = views;
}
[views addObject:itemView];
}
}
- (NSArray *)visibleItemViews
{
return _visibleItemViews;
}
- (TGModernGalleryItemView *)itemViewForItem:(id<TGModernGalleryItem>)item
{
for (TGModernGalleryItemView *itemView in self->_visibleItemViews)
{
if ([itemView.item isEqual:item])
{
return itemView;
}
}
return nil;
}
- (void)setShowInterface:(bool)showInterface
{
_showInterface = showInterface;
_view.userInteractionEnabled = showInterface;
_statusBarStyle = _showInterface ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
}
- (void)loadView
{
[super loadView];
object_setClass(self.view, [TGModernGalleryContainerView class]);
self.view.frame = (CGRect){self.view.frame.origin, TGAppDelegateInstance.rootController.applicationBounds.size};
_reusableItemViewsByIdentifier = [[NSMutableDictionary alloc] init];
_visibleItemViews = [[NSMutableArray alloc] init];
UIView<TGModernGalleryInterfaceView> *interfaceView = [_model createInterfaceView];
if (interfaceView == nil)
interfaceView = [[TGModernGalleryDefaultInterfaceView alloc] initWithFrame:CGRectZero];
_view = [[TGModernGalleryView alloc] initWithFrame:self.view.bounds itemPadding:TGModernGalleryItemPadding interfaceView:interfaceView];
_view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:_view];
_defaultHeaderView = [_model createDefaultHeaderView];
if (_defaultHeaderView != nil)
[_view addItemHeaderView:_defaultHeaderView];
_defaultFooterView = [_model createDefaultFooterView];
if (_defaultFooterView != nil)
[_view addItemFooterView:_defaultFooterView];
_view.scrollView.scrollDelegate = self;
_view.scrollView.delegate = self;
_view.userInteractionEnabled = _showInterface;
__weak TGModernGalleryController *weakSelf = self;
_view.transitionOut = ^bool (CGFloat velocity)
{
__strong TGModernGalleryController *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf->_isBeingDismissed = true;
id<TGModernGalleryItem> focusItem = nil;
if ([strongSelf currentItemIndex] < strongSelf.model.items.count)
focusItem = strongSelf.model.items[[strongSelf currentItemIndex]];
TGModernGalleryItemView *currentItemView = nil;
for (TGModernGalleryItemView *itemView in strongSelf->_visibleItemViews)
{
if ([itemView.item isEqual:focusItem])
{
currentItemView = itemView;
break;
}
}
if (strongSelf.hasFadeOutTransition)
{
if (strongSelf.beginTransitionOut)
strongSelf.beginTransitionOut(focusItem, currentItemView);
[strongSelf->_view fadeOutWithDuration:0.3f completion:^
{
[strongSelf dismiss];
}];
}
else
{
UIView *transitionOutToView = nil;
UIView *transitionOutFromView = nil;
CGRect transitionOutFromViewContentRect = CGRectZero;
if (strongSelf.beginTransitionOut && focusItem != nil)
transitionOutToView = strongSelf.beginTransitionOut(focusItem, currentItemView);
if (transitionOutToView != nil && currentItemView != nil)
{
transitionOutFromView = [currentItemView transitionView];
transitionOutFromViewContentRect = [currentItemView transitionViewContentRect];
}
if (transitionOutFromView != nil && transitionOutToView != nil)
{
[strongSelf animateTransitionOutFromView:transitionOutFromView fromViewContentRect:transitionOutFromViewContentRect toView:transitionOutToView velocity:CGPointMake(0.0f, velocity * 3.8f)];
[strongSelf->_view transitionOutWithDuration:0.15];
[strongSelf->_view.interfaceView animateTransitionOutWithDuration:0.15];
}
else
{
if (iosMajorVersion() >= 7)
{
[strongSelf animateStatusBarTransition:0.2];
strongSelf->_statusBarStyle = UIStatusBarStyleDefault;
[strongSelf setNeedsStatusBarAppearanceUpdate];
}
if (strongSelf.adjustsStatusBarVisibility)
{
[UIView animateWithDuration:0.2 animations:^
{
[TGHacks setApplicationStatusBarAlpha:1.0f];
}];
}
[strongSelf->_view simpleTransitionOutWithVelocity:velocity completion:^
{
__strong TGModernGalleryController *strongSelf2 = weakSelf;
[strongSelf2 dismiss];
}];
}
}
}
return true;
};
[self reloadDataAtItem:_model.focusItem synchronously:true];
if (_animateTransition)
{
UIView *transitionInFromView = nil;
UIView *transitionInToView = nil;
CGRect transitionInToViewContentRect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
if (_beginTransitionIn && _model.focusItem != nil)
{
TGModernGalleryItemView *itemView = nil;
for (TGModernGalleryItemView *visibleItemView in self->_visibleItemViews)
{
if ([visibleItemView.item isEqual:self.model.focusItem])
{
itemView = visibleItemView;
break;
}
}
transitionInFromView = _beginTransitionIn(_model.focusItem, itemView);
}
if (transitionInFromView != nil)
{
for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
if ([itemView.item isEqual:_model.focusItem])
{
transitionInToView = [itemView transitionView];
transitionInToViewContentRect = [itemView transitionViewContentRect];
break;
}
}
}
if (transitionInFromView != nil && transitionInToView != nil && transitionInToView.frame.size.width > FLT_EPSILON && transitionInToView.frame.size.height > FLT_EPSILON && transitionInToViewContentRect.size.width > FLT_EPSILON && transitionInToViewContentRect.size.height > FLT_EPSILON)
{
[self animateTransitionInFromView:transitionInFromView toView:transitionInToView toViewContentRect:transitionInToViewContentRect];
}
else if (_finishedTransitionIn && _model.focusItem != nil)
{
TGModernGalleryItemView *itemView = nil;
if (self.finishedTransitionIn && self.model.focusItem != nil)
{
for (TGModernGalleryItemView *visibleItemView in self->_visibleItemViews)
{
if ([visibleItemView.item isEqual:self.model.focusItem])
{
itemView = visibleItemView;
break;
}
}
}
_finishedTransitionIn(_model.focusItem, itemView);
[_model _transitionCompleted];
}
else
[_model _transitionCompleted];
[_view transitionInWithDuration:0.15];
[self animateStatusBarTransition:0.2];
}
else
{
if (_finishedTransitionIn && _model.focusItem != nil)
{
TGModernGalleryItemView *itemView = nil;
if (self.finishedTransitionIn && self.model.focusItem != nil)
{
for (TGModernGalleryItemView *visibleItemView in self->_visibleItemViews)
{
if ([visibleItemView.item isEqual:self.model.focusItem])
{
itemView = visibleItemView;
break;
}
}
}
_finishedTransitionIn(_model.focusItem, itemView);
}
[_model _transitionCompleted];
}
}
- (UIView *)findScrollView:(UIView *)view
{
if (view == nil || [view isKindOfClass:[UIScrollView class]])
return view;
return [self findScrollView:view.superview];
}
- (UIView *)topSuperviewOfView:(UIView *)view
{
if (view.superview == nil)
return view;
return [self topSuperviewOfView:view.superview];
}
- (UIView *)findCommonSuperviewOfView:(UIView *)view andView:(UIView *)andView
{
UIView *leftSuperview = [self topSuperviewOfView:view];
UIView *rightSuperview = [self topSuperviewOfView:andView];
if (leftSuperview != rightSuperview)
return nil;
return leftSuperview;
}
- (UIView *)subviewOfView:(UIView *)view containingView:(UIView *)containingView
{
if (view == containingView)
return view;
for (UIView *subview in view.subviews)
{
if ([self subviewOfView:subview containingView:containingView] != nil)
return subview;
}
return nil;
}
static CGRect adjustFrameForOriginalSubframe(CGRect originalFrame, CGRect originalSubframe, CGRect frame)
{
if (originalSubframe.size.width < FLT_EPSILON || originalSubframe.size.height < FLT_EPSILON)
return frame;
CGFloat widthFactor = frame.size.width / originalSubframe.size.width;
CGFloat heightFactor = frame.size.height / originalSubframe.size.height;
CGRect adjustedFrame = CGRectMake(frame.origin.x - originalSubframe.origin.x * widthFactor, frame.origin.y - originalSubframe.origin.y * heightFactor, originalFrame.size.width * widthFactor, originalFrame.size.height * heightFactor);
return adjustedFrame;
}
- (CGRect)convertFrameOfView:(UIView *)view fromSubframe:(CGRect)fromSubframe toView:(UIView *)toView toSubframe:(CGRect)toSubframe outRotationZ:(CGFloat *)outRotationZ
{
if (view == toView)
return view.bounds;
CGFloat sourceWindowRotation = 0.0f;
CGRect frame = fromSubframe;
UIView *currentView = view;
while (currentView != nil)
{
frame.origin.x += currentView.frame.origin.x - currentView.bounds.origin.x;
frame.origin.y += currentView.frame.origin.y - currentView.bounds.origin.y;
CGFloat rotation = transformRotation(currentView.transform);
if (ABS(rotation) > FLT_EPSILON)
{
CGAffineTransform transform = CGAffineTransformMakeTranslation(currentView.bounds.size.width / 2.0f, currentView.bounds.size.height / 2.0f);
transform = CGAffineTransformRotate(transform, rotation);
transform = CGAffineTransformTranslate(transform, -currentView.bounds.size.width / 2.0f, -currentView.bounds.size.height / 2.0f);
frame = CGRectApplyAffineTransform(frame, transform);
}
//TGLog(@"%f: %@", rotation, currentView);
if ([currentView.superview isKindOfClass:[UIWindow class]])
sourceWindowRotation = rotation;
//frame = CGRectApplyAffineTransform(frame, transform);
currentView = currentView.superview;
}
UIView *subview = [self topSuperviewOfView:toView];
while (subview != nil && subview != toView)
{
frame.origin.x -= subview.frame.origin.x - subview.bounds.origin.x;
frame.origin.y -= subview.frame.origin.y - subview.bounds.origin.y;
if (ABS(sourceWindowRotation) > FLT_EPSILON)
{
CGAffineTransform transform = CGAffineTransformMakeTranslation(subview.bounds.size.width / 2.0f, subview.bounds.size.height / 2.0f);
transform = CGAffineTransformRotate(transform, -sourceWindowRotation);
transform = CGAffineTransformTranslate(transform, -subview.bounds.size.width / 2.0f, -subview.bounds.size.height / 2.0f);
frame = CGRectApplyAffineTransform(frame, transform);
sourceWindowRotation = 0.0f;
}
subview = [self subviewOfView:subview containingView:toView];
}
frame = adjustFrameForOriginalSubframe(toView.frame, toSubframe, frame);
/*frame.origin.x = CGFloor(frame.origin.x);
frame.origin.y = CGFloor(frame.origin.y);
frame.size.width = CGFloor(frame.size.width);
frame.size.height = CGFloor(frame.size.height);*/
if (outRotationZ != NULL)
*outRotationZ = 0.0f;
return frame;
}
static CGFloat transformRotation(CGAffineTransform transform)
{
return (CGFloat)atan2(transform.b, transform.a);
}
- (void)animateView:(UIView *)view frameFrom:(CGRect)fromFrame to:(CGRect)toFrame velocity:(CGPoint)__unused velocity rotationFrom:(CGFloat)fromRotation to:(CGFloat)toRotation animatingIn:(bool)animatingIn completion:(void (^)(bool))completion
{
if (fromFrame.size.width < FLT_EPSILON || fromFrame.size.height < FLT_EPSILON )
{
completion(true);
return;
}
if (ABS(toRotation - fromRotation) > FLT_EPSILON)
{
}
[CATransaction begin];
CGFloat damping = animatingIn ? 25.0f : 30.0f;
CGFloat mass = 0.8f;
CGFloat durationFactor = animatingIn ? 1.8f : 2.0f;
CGPoint fromPosition = CGPointMake(CGRectGetMidX(fromFrame), CGRectGetMidY(fromFrame));
CGPoint toPosition = CGPointMake(CGRectGetMidX(toFrame), CGRectGetMidY(toFrame));
JNWSpringAnimation *positionAnimation = [JNWSpringAnimation animationWithKeyPath:@"position"];
positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPosition];
positionAnimation.toValue = [NSValue valueWithCGPoint:toPosition];
positionAnimation.damping = damping;
positionAnimation.mass = mass;
positionAnimation.removedOnCompletion = true;
positionAnimation.fillMode = kCAFillModeForwards;
positionAnimation.durationFactor = durationFactor;
TGAnimationBlockDelegate *delegate = [[TGAnimationBlockDelegate alloc] initWithLayer:view.layer];
delegate.completion = ^(BOOL finished)
{
if (completion)
completion(finished);
};
positionAnimation.delegate = delegate;
view.layer.position = toPosition;
[view.layer addAnimation:positionAnimation forKey:@"position"];
CGPoint fromScale = CGPointMake(fromFrame.size.width / view.bounds.size.width, fromFrame.size.height/ view.bounds.size.height);
CGPoint toScale = CGPointMake(toFrame.size.width / view.bounds.size.width, toFrame.size.height / view.bounds.size.height);
view.layer.transform = CATransform3DMakeScale(toFrame.size.width / view.bounds.size.width, toFrame.size.height / view.bounds.size.height, 1.0f);
{
JNWSpringAnimation *scaleAnimation = [JNWSpringAnimation animationWithKeyPath:@"transform.scale.x"];
scaleAnimation.fromValue = @(fromScale.x);
scaleAnimation.toValue = @(toScale.x);
scaleAnimation.damping = damping;
scaleAnimation.mass = mass;
scaleAnimation.removedOnCompletion = true;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.durationFactor = durationFactor;
[view.layer addAnimation:scaleAnimation forKey:@"transform.scale.x"];
}
{
JNWSpringAnimation *scaleAnimation = [JNWSpringAnimation animationWithKeyPath:@"transform.scale.y"];
scaleAnimation.fromValue = @(fromScale.y);
scaleAnimation.toValue = @(toScale.y);
scaleAnimation.damping = damping;
scaleAnimation.mass = mass;
scaleAnimation.removedOnCompletion = true;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.durationFactor = durationFactor;
[view.layer addAnimation:scaleAnimation forKey:@"transform.scale.y"];
}
[CATransaction commit];
}
- (void)animateTransitionInFromView:(UIView *)fromView toView:(UIView *)toView toViewContentRect:(CGRect)toViewContentRect
{
UIView *fromScrollView = [self findScrollView:fromView];
UIView *fromContainerView = fromScrollView.superview;
CGFloat fromRotationZ = 0.0f;
CGRect fromFrame = [self convertFrameOfView:fromView fromSubframe:(CGRect){CGPointZero, fromView.frame.size} toView:toView.superview toSubframe:[toView.superview convertRect:toViewContentRect fromView:toView] outRotationZ:&fromRotationZ];
if (![fromScrollView isKindOfClass:[TGSharedMediaCollectionView class]])
fromFrame.origin.y += fromScrollView.frame.origin.y * 2.0f;
fromFrame.origin.x -= toView.superview.frame.origin.x;
fromFrame.origin.y -= toView.superview.frame.origin.y;
CGRect fromContainerFromFrame = [fromContainerView convertRect:fromView.bounds fromView:fromView];
CGRect fromContainerFrame = [self convertFrameOfView:toView fromSubframe:toViewContentRect toView:fromContainerView toSubframe:(CGRect){CGPointZero, fromContainerView.frame.size} outRotationZ:NULL];
if ([fromView conformsToProtocol:@protocol(TGModernGalleryTransitionView)] && [fromView respondsToSelector:@selector(transitionContentRect)])
{
CGRect fromContentRect = [(id<TGModernGalleryTransitionView>)fromView transitionContentRect];
if (fromContentRect.size.width > FLT_EPSILON && fromContentRect.size.height > FLT_EPSILON)
{
fromContainerFrame = adjustFrameForOriginalSubframe(fromView.frame, fromContentRect, fromContainerFrame);
}
}
UIView *fromViewContainerCopy = nil;
if ([fromView conformsToProtocol:@protocol(TGModernGalleryTransitionView)])
{
UIImage *transitionImage = [(id<TGModernGalleryTransitionView>)fromView transitionImage];
if (transitionImage != nil)
fromViewContainerCopy = [[UIImageView alloc] initWithImage:transitionImage];
}
if (fromViewContainerCopy == nil)
fromViewContainerCopy = [fromView snapshotViewAfterScreenUpdates:false];
fromViewContainerCopy.frame = fromContainerFromFrame;
[fromContainerView insertSubview:fromViewContainerCopy aboveSubview:fromScrollView];
__weak TGModernGalleryController *weakSelf = self;
self.view.userInteractionEnabled = false;
[self animateView:toView frameFrom:fromFrame to:toView.frame velocity:CGPointZero rotationFrom:fromRotationZ to:0.0f animatingIn:true completion:^(bool __unused finished)
{
__strong TGModernGalleryController *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf.view.userInteractionEnabled = true;
TGModernGalleryItemView *itemView = nil;
if (strongSelf.finishedTransitionIn && strongSelf.model.focusItem != nil)
{
for (TGModernGalleryItemView *visibleItemView in strongSelf->_visibleItemViews)
{
if ([visibleItemView.item isEqual:strongSelf.model.focusItem])
{
itemView = visibleItemView;
break;
}
}
}
if (strongSelf.finishedTransitionIn)
strongSelf.finishedTransitionIn(strongSelf.model.focusItem, itemView);
strongSelf->_preloadVisibleItemViews = true;
[strongSelf scrollViewBoundsChanged:strongSelf->_view.scrollView.bounds synchronously:false];
[strongSelf.model _transitionCompleted];
}
}];
__weak UIView *weakFromViewContainerCopy = fromViewContainerCopy;
[self animateView:fromViewContainerCopy frameFrom:fromViewContainerCopy.frame to:fromContainerFrame velocity:CGPointZero rotationFrom:0.0f to:0.0f animatingIn:true completion:^(__unused bool finished)
{
__strong UIView *strongFromViewContainerCopy = weakFromViewContainerCopy;
[strongFromViewContainerCopy removeFromSuperview];
}];
toView.alpha = 0.0f;
/*POPBasicAnimation *toViewAlphaAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha];
toViewAlphaAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
toViewAlphaAnimation.duration = 0.15;
toViewAlphaAnimation.fromValue = @(0.0);
toViewAlphaAnimation.toValue = @(1.0);
[toView pop_addAnimation:toViewAlphaAnimation forKey:@"transitionInAlpha"];*/
[UIView animateWithDuration:0.07 animations:^{
toView.alpha = 1.0f;
}];
}
- (void)animateTransitionOutFromView:(UIView *)fromView fromViewContentRect:(CGRect)fromViewContentRect toView:(UIView *)toView velocity:(CGPoint)velocity
{
TGLog(@"begin calc");
TG_TIMESTAMP_DEFINE(out)
UIView *toScrollView = [self findScrollView:toView];
UIView *toContainerView = toScrollView.superview;
CGRect toFrame = [fromView.superview convertRect:[toView convertRect:toView.bounds toView:nil] fromView:nil];
toFrame = adjustFrameForOriginalSubframe(fromView.frame, fromViewContentRect, toFrame);
TG_TIMESTAMP_MEASURE(out)
CGRect toContainerFrame = [toContainerView convertRect:toView.bounds fromView:toView];
CGRect toContainerFromFrame = [toContainerView convertRect:[fromView convertRect:fromViewContentRect toView:nil] fromView:nil];
TG_TIMESTAMP_MEASURE(out)
/*POPBasicAnimation *fromViewAlphaAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha];
fromViewAlphaAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
fromViewAlphaAnimation.duration = 0.15;
fromViewAlphaAnimation.fromValue = @(1.0);
fromViewAlphaAnimation.toValue = @(0.0);
[fromView pop_addAnimation:fromViewAlphaAnimation forKey:@"transitionOutAlpha"];*/
[UIView animateWithDuration:0.1 animations:^{
fromView.alpha = 0.0f;
}];
UIView *toViewCopy = nil;
TG_TIMESTAMP_MEASURE(out)
if ([toView conformsToProtocol:@protocol(TGModernGalleryTransitionView)])
{
UIImage *transitionImage = [(id<TGModernGalleryTransitionView>)toView transitionImage];
if (transitionImage != nil)
{
toViewCopy = [[UIImageView alloc] initWithImage:transitionImage];
}
}
TG_TIMESTAMP_MEASURE(out)
if (toViewCopy == nil)
{
CGFloat toViewAlpha = toView.alpha;
bool toViewHidden = toView.hidden;
CGRect toViewFrame = toView.frame;
toView.alpha = 1.0f;
toView.hidden = false;
toView.frame = CGRectOffset(toViewFrame, 1000.0f, 0.0f);
toViewCopy = [toView snapshotViewAfterScreenUpdates:true];
toView.alpha = toViewAlpha;
toView.hidden = toViewHidden;
toView.frame = toViewFrame;
}
toViewCopy.frame = toContainerFromFrame;
[toContainerView insertSubview:toViewCopy aboveSubview:toScrollView];
TG_TIMESTAMP_MEASURE(out)
__weak TGModernGalleryController *weakSelf = self;
self.view.userInteractionEnabled = false;
[self animateView:fromView frameFrom:fromView.frame to:toFrame velocity:velocity rotationFrom:0.0f to:0.0f animatingIn:false completion:^(__unused bool finished)
{
__strong TGModernGalleryController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf dismiss];
}
}];
TG_TIMESTAMP_MEASURE(out)
__weak UIView *weakToViewCopy = toViewCopy;
[self animateView:toViewCopy frameFrom:toViewCopy.frame to:toContainerFrame velocity:velocity rotationFrom:0.0f to:0.0f animatingIn:false completion:^(__unused bool finished)
{
__strong UIView *strongToViewCopy = weakToViewCopy;
[strongToViewCopy removeFromSuperview];
}];
TG_TIMESTAMP_MEASURE(out)
if (iosMajorVersion() >= 7)
{
[self animateStatusBarTransition:0.2];
self->_statusBarStyle = UIStatusBarStyleDefault;
[self setNeedsStatusBarAppearanceUpdate];
}
TG_TIMESTAMP_MEASURE(out)
if (self.adjustsStatusBarVisibility)
{
[UIView animateWithDuration:0.2 animations:^
{
[TGHacks setApplicationStatusBarAlpha:1.0f];
}];
}
TGLog(@"started animation");
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!_showInterface)
{
_view.interfaceView.alpha = 0.0f;
if (self.adjustsStatusBarVisibility)
[TGHacks setApplicationStatusBarAlpha:0.0f];
}
else if ([_view.interfaceView prefersStatusBarHidden])
{
if (self.adjustsStatusBarVisibility)
[TGHacks setApplicationStatusBarAlpha:0.0f];
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.adjustsStatusBarVisibility)
{
[TGHacks setApplicationStatusBarAlpha:1.0f];
dispatch_async(dispatch_get_main_queue(), ^
{
[TGHacks setApplicationStatusBarAlpha:1.0f];
});
}
}
#pragma mark -
- (void)setCurrentItemIndex:(NSUInteger)index animated:(bool)animated
{
[self setCurrentItemIndex:index direction:TGModernGalleryScrollAnimationDirectionDefault animated:animated];
}
- (void)setCurrentItemIndex:(NSUInteger)index direction:(TGModernGalleryScrollAnimationDirection)direction animated:(bool)animated
{
if (index == [self currentItemIndex])
return;
if (animated)
{
UIView *currentItemView = [self itemViewForItem:[self currentItem]];
UIView *outgoingItemView = [currentItemView snapshotViewAfterScreenUpdates:false];
[_view insertSubview:outgoingItemView belowSubview:_view.interfaceView];
if (direction == TGModernGalleryScrollAnimationDirectionDefault)
direction = (index > [self currentItemIndex]) ? TGModernGalleryScrollAnimationDirectionRight : TGModernGalleryScrollAnimationDirectionLeft;
[self setCurrentItemIndex:index synchronously:false];
UIView *incomingItemView = [self itemViewForItem:[self currentItem]];
CGPoint incomingItemStartPosition = incomingItemView.center;
CGPoint incomingItemTargetPosition = incomingItemView.center;
CGPoint outgoingItemTargetPosition = CGPointMake(0, incomingItemTargetPosition.y);
CGSize referenceSize = TGAppDelegateInstance.rootController.applicationBounds.size;
switch (direction)
{
case TGModernGalleryScrollAnimationDirectionLeft:
incomingItemStartPosition.x += -referenceSize.width - 2 * TGModernGalleryItemPadding;
outgoingItemTargetPosition.x = referenceSize.width * 1.5f + 2 * TGModernGalleryItemPadding;
break;
case TGModernGalleryScrollAnimationDirectionRight:
incomingItemStartPosition.x += referenceSize.width + 2 * TGModernGalleryItemPadding;
outgoingItemTargetPosition.x = -(referenceSize.width / 2) - 2 * TGModernGalleryItemPadding;
break;
default:
break;
}
incomingItemView.center = incomingItemStartPosition;
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseInOut
animations:^
{
incomingItemView.center = incomingItemTargetPosition;
outgoingItemView.center = outgoingItemTargetPosition;
} completion:^(__unused BOOL finished)
{
[outgoingItemView removeFromSuperview];
[self _updateItemViewsCurrent];
}];
}
else
{
[self setCurrentItemIndex:index synchronously:false];
}
}
- (id<TGModernGalleryItem>)currentItem
{
return _model.items.count > 0 ? [_model.items objectAtIndex:[self currentItemIndex]] : nil;
}
- (void)setCurrentItemIndex:(NSUInteger)currentItemIndex synchronously:(bool)synchronously
{
_synchronousBoundsChange = synchronously;
_view.scrollView.bounds = CGRectMake(_view.scrollView.bounds.size.width * currentItemIndex, 0.0f, _view.scrollView.bounds.size.width, _view.scrollView.bounds.size.height);
_synchronousBoundsChange = false;
}
- (NSUInteger)currentItemIndex
{
return _model.items.count == 0 ? 0 : (NSUInteger)[self currentItemFuzzyIndex];
}
- (CGFloat)currentItemFuzzyIndex
{
if (_model.items.count == 0)
return 0.0f;
return CGFloor((_view.scrollView.bounds.origin.x + _view.scrollView.bounds.size.width / 2.0f) / _view.scrollView.bounds.size.width);
}
- (NSArray *)visibleItems
{
NSMutableArray *items = [[NSMutableArray alloc] init];
for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
if (itemView.item != nil)
[items addObject:itemView.item];
}
return items;
}
- (void)reloadDataAtItem:(id<TGModernGalleryItem>)atItem synchronously:(bool)synchronously
{
NSMutableIndexSet *removeIndices = nil;
id<TGModernGalleryItem> focusItem = atItem;
if (focusItem == nil)
{
for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
if (itemView.index == [self currentItemIndex])
{
focusItem = itemView.item;
break;
}
}
}
NSInteger itemViewIndex = -1;
for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
itemViewIndex++;
NSInteger itemIndex = -1;
bool itemFound = false;
for (id<TGModernGalleryItem> item in _model.items)
{
itemIndex++;
if ([item isEqual:itemView.item])
{
itemView.index = (NSUInteger)itemIndex;
itemFound = true;
break;
}
}
if (!itemFound)
{
if (removeIndices == nil)
removeIndices = [[NSMutableIndexSet alloc] init];
[removeIndices addIndex:(NSUInteger)itemViewIndex];
UIView *itemHeaderView = [itemView headerView];
if (itemHeaderView != nil)
[_view removeItemHeaderView:itemHeaderView];
UIView *itemDefaultLeftAcessoryView = [itemView defaultFooterAccessoryLeftView];
if (itemDefaultLeftAcessoryView != nil)
[_view.interfaceView removeItemLeftAcessoryView:itemDefaultLeftAcessoryView];
UIView *itemDefaultRightAcessoryView = [itemView defaultFooterAccessoryRightView];
if (itemDefaultRightAcessoryView != nil)
[_view.interfaceView removeItemRightAcessoryView:itemDefaultRightAcessoryView];
UIView *itemFooterView = [itemView footerView];
if (itemFooterView != nil)
[_view removeItemFooterView:itemFooterView];
[itemView removeFromSuperview];
[self enqueueView:itemView];
}
}
if (removeIndices != nil)
[_visibleItemViews removeObjectsAtIndexes:removeIndices];
_reloadingItems = true;
NSUInteger index = (focusItem == nil || _model.items.count == 0) ? NSNotFound : [_model.items indexOfObject:focusItem];
if (index != NSNotFound && index != _lastReportedFocusedIndex)
{
_lastReportedFocusedIndex = NSNotFound;
[self setCurrentItemIndex:index == NSNotFound ? 0 : index synchronously:synchronously];
}
else
{
_lastReportedFocusedIndex = NSNotFound;
CGFloat itemWidth = _view.scrollView.bounds.size.width;
CGSize contentSize = CGSizeMake(_model.items.count * itemWidth, _view.scrollView.bounds.size.height);
if (!CGSizeEqualToSize(_view.scrollView.contentSize, contentSize))
{
_view.scrollView.contentSize = contentSize;
if (_view.scrollView.bounds.origin.x > contentSize.width - itemWidth)
{
_view.scrollView.bounds = CGRectMake(contentSize.width - itemWidth, 0.0f, itemWidth, _view.scrollView.bounds.size.height);
}
else
[self scrollViewBoundsChanged:_view.scrollView.bounds];
}
else
[self scrollViewBoundsChanged:_view.scrollView.bounds];
}
_reloadingItems = false;
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)__unused scrollView
{
}
- (void)scrollViewBoundsChanged:(CGRect)bounds
{
[self scrollViewBoundsChanged:bounds synchronously:_synchronousBoundsChange];
}
- (void)scrollViewBoundsChanged:(CGRect)bounds synchronously:(bool)synchronously
{
if (_view == nil)
return;
CGFloat itemWidth = bounds.size.width;
NSUInteger leftmostVisibleItemIndex = 0;
if (bounds.origin.x > 0.0f)
leftmostVisibleItemIndex = (NSUInteger)floor((bounds.origin.x + 1.0f) / itemWidth);
NSUInteger leftmostTrulyVisibleItemIndex = leftmostVisibleItemIndex;
NSUInteger rightmostVisibleItemIndex = _model.items.count - 1;
if (bounds.origin.x + bounds.size.width < _model.items.count * itemWidth)
rightmostVisibleItemIndex = (NSUInteger)CGFloor((bounds.origin.x + bounds.size.width - 1.0f) / itemWidth);
NSUInteger rightmostTrulyVisibleItemIndex = rightmostVisibleItemIndex;
if (_preloadVisibleItemViews)
{
if (leftmostVisibleItemIndex >= 1)
leftmostVisibleItemIndex = leftmostVisibleItemIndex - 1;
if (rightmostVisibleItemIndex < _model.items.count - 1)
rightmostVisibleItemIndex = rightmostVisibleItemIndex + 1;
}
if (leftmostVisibleItemIndex <= rightmostVisibleItemIndex && _model.items.count != 0)
{
CGSize contentSize = CGSizeMake(_model.items.count * itemWidth, bounds.size.height);
if (!CGSizeEqualToSize(_view.scrollView.contentSize, contentSize))
_view.scrollView.contentSize = contentSize;
NSUInteger loadedVisibleViewIndices[16];
NSUInteger loadedVisibleViewIndexCount = 0;
NSUInteger visibleViewCount = _visibleItemViews.count;
for (NSUInteger i = 0; i < visibleViewCount; i++)
{
TGModernGalleryItemView *itemView = _visibleItemViews[i];
if (itemView.index < leftmostVisibleItemIndex || itemView.index > rightmostVisibleItemIndex)
{
UIView *itemHeaderView = [itemView headerView];
if (itemHeaderView != nil)
[_view removeItemHeaderView:itemHeaderView];
UIView *itemDefaultLeftAcessoryView = [itemView defaultFooterAccessoryLeftView];
if (itemDefaultLeftAcessoryView != nil)
[_view.interfaceView removeItemLeftAcessoryView:itemDefaultLeftAcessoryView];
UIView *itemDefaultRightAcessoryView = [itemView defaultFooterAccessoryRightView];
if (itemDefaultRightAcessoryView != nil)
[_view.interfaceView removeItemRightAcessoryView:itemDefaultRightAcessoryView];
UIView *itemFooterView = [itemView footerView];
if (itemFooterView != nil)
[_view removeItemFooterView:itemFooterView];
[self enqueueView:itemView];
[itemView removeFromSuperview];
[_visibleItemViews removeObjectAtIndex:i];
i--;
visibleViewCount--;
}
else
{
if (loadedVisibleViewIndexCount < 16)
loadedVisibleViewIndices[loadedVisibleViewIndexCount++] = itemView.index;
[itemView setIsVisible:itemView.index >= leftmostTrulyVisibleItemIndex && itemView.index <= rightmostTrulyVisibleItemIndex];
[itemView setIsCurrent:itemView.index == [self currentItemIndex]];
CGRect itemFrame = CGRectMake(itemWidth * itemView.index + TGModernGalleryItemPadding, 0.0f, itemWidth - TGModernGalleryItemPadding * 2.0f, bounds.size.height);
if (!CGRectEqualToRect(itemView.frame, itemFrame))
itemView.frame = itemFrame;
}
}
for (NSUInteger i = leftmostVisibleItemIndex; i <= rightmostVisibleItemIndex; i++)
{
bool itemHasVisibleView = false;
for (NSUInteger j = 0; j < loadedVisibleViewIndexCount; j++)
{
if (loadedVisibleViewIndices[j] == i)
{
itemHasVisibleView = true;
break;
}
}
if (!itemHasVisibleView)
{
id<TGModernGalleryItem> item = _model.items[i];
TGModernGalleryItemView *itemView = [self dequeueViewForItem:item];
if (itemView != nil)
{
itemView.frame = CGRectMake(itemWidth * i + TGModernGalleryItemPadding, 0.0f, itemWidth - TGModernGalleryItemPadding * 2.0f, bounds.size.height);
[itemView setItem:item synchronously:synchronously];
itemView.index = i;
[_view.scrollView addSubview:itemView];
UIView *headerView = [itemView headerView];
if (headerView != nil)
[_view addItemHeaderView:headerView];
UIView *itemDefaultLeftAcessoryView = [itemView defaultFooterAccessoryLeftView];
if (itemDefaultLeftAcessoryView != nil)
[_view.interfaceView addItemLeftAcessoryView:itemDefaultLeftAcessoryView];
UIView *itemDefaultRightAcessoryView = [itemView defaultFooterAccessoryRightView];
if (itemDefaultRightAcessoryView != nil)
[_view.interfaceView addItemRightAcessoryView:itemDefaultRightAcessoryView];
UIView *footerView = [itemView footerView];
if (footerView != nil)
[_view addItemFooterView:footerView];
[_visibleItemViews addObject:itemView];
}
}
}
}
else if (_visibleItemViews.count != 0)
{
_view.scrollView.contentSize = CGSizeZero;
for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
UIView *itemHeaderView = [itemView headerView];
if (itemHeaderView != nil)
[_view removeItemHeaderView:itemHeaderView];
UIView *itemDefaultLeftAcessoryView = [itemView defaultFooterAccessoryLeftView];
if (itemDefaultLeftAcessoryView != nil)
[_view.interfaceView addItemLeftAcessoryView:itemDefaultLeftAcessoryView];
UIView *itemDefaultRightAcessoryView = [itemView defaultFooterAccessoryRightView];
if (itemDefaultRightAcessoryView != nil)
[_view.interfaceView addItemRightAcessoryView:itemDefaultRightAcessoryView];
UIView *itemFooterView = [itemView footerView];
if (itemFooterView != nil)
[_view removeItemFooterView:itemFooterView];
[itemView removeFromSuperview];
[self enqueueView:itemView];
}
[_visibleItemViews removeAllObjects];
}
CGFloat fuzzyIndex = MAX(0, MIN(_model.items.count - 1, (_view.scrollView.bounds.origin.x) / _view.scrollView.bounds.size.width));
CGFloat titleAlpha = 1.0f;
NSUInteger currentItemIndex = [self currentItemIndex];
TGModernGalleryItemView *currentItemView = nil;
for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
CGFloat alpha = MAX(0.0f, MIN(1.0f, 1.0f - ABS(itemView.index - fuzzyIndex)));
UIView *itemHeaderView = [itemView headerView];
if (itemHeaderView != nil)
{
itemHeaderView.alpha = alpha;
titleAlpha -= alpha;
}
CGFloat footerAlpha = itemView.index == currentItemIndex ? 1.0f : 0.0f;
if (itemView.index == currentItemIndex)
currentItemView = itemView;
UIView *itemDefaultLeftAcessoryView = [itemView defaultFooterAccessoryLeftView];
if (itemDefaultLeftAcessoryView != nil)
itemDefaultLeftAcessoryView.alpha = footerAlpha;
UIView *itemDefaultRightAcessoryView = [itemView defaultFooterAccessoryRightView];
if (itemDefaultRightAcessoryView != nil)
itemDefaultRightAcessoryView.alpha = footerAlpha;
UIView *itemFooterView = [itemView footerView];
if (itemFooterView != nil)
itemFooterView.alpha = footerAlpha;
}
_defaultHeaderView.alpha = MAX(0.0f, MIN(1.0f, titleAlpha));
if (_lastReportedFocusedIndex != [self currentItemIndex])
{
if (!_reloadingItems && _lastReportedFocusedIndex != NSNotFound)
[_view updateInterfaceVisibility];
NSUInteger previousFocusedIndex = _lastReportedFocusedIndex;
_lastReportedFocusedIndex = [self currentItemIndex];
if (_lastReportedFocusedIndex < _model.items.count)
{
if (_itemFocused)
_itemFocused(_model.items[_lastReportedFocusedIndex]);
[_view.interfaceView itemFocused:_model.items[_lastReportedFocusedIndex] itemView:currentItemView];
[_defaultHeaderView setItem:_model.items[_lastReportedFocusedIndex]];
[_defaultFooterView setItem:_model.items[_lastReportedFocusedIndex]];
if (previousFocusedIndex < _model.items.count)
[[self itemViewForItem:_model.items[previousFocusedIndex]] setFocused:false];
[[self itemViewForItem:_model.items[_lastReportedFocusedIndex]] setFocused:true];
}
}
}
- (void)_updateItemViewsCurrent
{
NSUInteger visibleViewCount = _visibleItemViews.count;
for (NSUInteger i = 0; i < visibleViewCount; i++)
{
TGModernGalleryItemView *itemView = _visibleItemViews[i];
//[itemView setIsVisible:itemView.index >= leftmostTrulyVisibleItemIndex && itemView.index <= rightmostTrulyVisibleItemIndex];
[itemView setIsCurrent:itemView.index == [self currentItemIndex]];
}
}
- (bool)scrollViewShouldScrollWithTouchAtPoint:(CGPoint)point
{
NSUInteger currentItemIndex = [self currentItemIndex];
for (TGModernGalleryItemView *itemView in _visibleItemViews)
{
if (itemView.index == currentItemIndex)
{
CGPoint localPoint = [itemView convertPoint:point fromView:_view.scrollView];
return [itemView allowsScrollingAtPoint:localPoint];
}
}
return true;
}
- (void)animateStatusBarTransition:(NSTimeInterval)duration
{
if (iosMajorVersion() >= 7)
{
[TGHacks animateApplicationStatusBarStyleTransitionWithDuration:duration];
}
}
- (void)processKeyCommand:(UIKeyCommand *)keyCommand
{
if ([keyCommand.input isEqualToString:UIKeyInputLeftArrow])
{
NSInteger newIndex = [self currentItemIndex] - 1;
if (newIndex >= 0)
[self setCurrentItemIndex:newIndex animated:true];
}
else if ([keyCommand.input isEqualToString:UIKeyInputRightArrow])
{
NSUInteger newIndex = [self currentItemIndex] + 1;
if (newIndex < _model.items.count)
[self setCurrentItemIndex:newIndex animated:true];
}
else if ([keyCommand.input isEqualToString:UIKeyInputEscape])
{
_view.transitionOut(0.0f);
}
}
- (NSArray *)availableKeyCommands
{
return @
[
[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputLeftArrow modifierFlags:0],
[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputRightArrow modifierFlags:0],
[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputEscape modifierFlags:0],
[TGKeyCommand keyCommandWithTitle:nil input:@"\t" modifierFlags:0]
];
}
- (bool)isExclusive
{
return true;
}
@end