Swiftgram/submodules/LegacyComponents/Sources/TGNavigationController.m
2023-09-22 18:09:36 +04:00

908 lines
31 KiB
Objective-C

#import "TGNavigationController.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "LegacyComponentsInternal.h"
#import "TGImageUtils.h"
#import "TGStringUtils.h"
#import "Freedom.h"
#import "TGNavigationBar.h"
#import "TGViewController.h"
#import "TGHacks.h"
#import <QuickLook/QuickLook.h>
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>
#import <objc/message.h>
@interface TGDelegateProxy<__covariant DelegateType:NSObject<NSObject> *> : NSObject
@property(nonatomic, weak) DelegateType delegate;
@end
@interface TGNavigationPanGestureRecognizer : UIPanGestureRecognizer
@property (nonatomic, readonly) CGFloat _edgeRegionSize;
@end
@interface TGNavigationGestureDelegateProxy : TGDelegateProxy<UIGestureRecognizerDelegate>
@property (nonatomic, copy) bool (^shouldBegin)(TGNavigationPanGestureRecognizer *);
@property (nonatomic, copy) bool (^shouldRecognizeWith)(TGNavigationPanGestureRecognizer *, UIGestureRecognizer *);
@end
@interface TGNavigationPercentTransition : UIPercentDrivenInteractiveTransition
@end
@interface TGNavigationController () <UINavigationControllerDelegate, UIGestureRecognizerDelegate, UINavigationBarDelegate>
{
UITapGestureRecognizer *_dimmingTapRecognizer;
CGSize _preferredContentSize;
id<SDisposable> _playerStatusDisposable;
CGFloat _currentAdditionalStatusBarHeight;
TGNavigationGestureDelegateProxy *_gestureDelegate;
bool _animatingControllerPush;
bool _fixNextInteractiveTransition;
id _enterBackgroundObserver;
id _becomeActiveObserver;
bool _didFirstLayout;
}
@property (nonatomic) bool wasShowingNavigationBar;
@property (nonatomic, strong) TGAutorotationLock *autorotationLock;
@end
@implementation TGNavigationController
+ (TGNavigationController *)navigationControllerWithRootController:(UIViewController *)controller
{
return [self navigationControllerWithControllers:[NSArray arrayWithObject:controller]];
}
+ (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers
{
return [self navigationControllerWithControllers:controllers navigationBarClass:[TGNavigationBar class]];
}
+ (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers navigationBarClass:(Class)navigationBarClass
{
return [self navigationControllerWithControllers:controllers navigationBarClass:navigationBarClass inhibitPresentation:false];
}
+ (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers navigationBarClass:(Class)navigationBarClass inhibitPresentation:(bool)inhibitPresentation
{
TGNavigationController *navigationController = [[TGNavigationController alloc] initWithNavigationBarClass:navigationBarClass toolbarClass:[UIToolbar class]];
if (!inhibitPresentation && [[LegacyComponentsGlobals provider] respondsToSelector:@selector(navigationBarPallete)])
[((TGNavigationBar *)navigationController.navigationBar) setPallete:[[LegacyComponentsGlobals provider] navigationBarPallete]];
bool first = true;
for (id controller in controllers) {
if ([controller isKindOfClass:[TGViewController class]]) {
[(TGViewController *)controller setIsFirstInStack:first];
}
first = false;
}
[navigationController setViewControllers:controllers];
((TGNavigationBar *)navigationController.navigationBar).navigationController = navigationController;
return navigationController;
}
+ (TGNavigationController *)makeWithRootController:(UIViewController *)controller {
return [self navigationControllerWithControllers:[NSArray arrayWithObject:controller]];
}
- (instancetype)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass
{
self = [super initWithNavigationBarClass:navigationBarClass toolbarClass:toolbarClass];
if (self != nil)
{
}
return self;
}
- (void)dealloc
{
[_playerStatusDisposable dispose];
self.delegate = nil;
[_dimmingTapRecognizer.view removeGestureRecognizer:_dimmingTapRecognizer];
if (_becomeActiveObserver != nil)
[[NSNotificationCenter defaultCenter] removeObserver:_becomeActiveObserver];
}
- (void)loadView
{
[super loadView];
if (@available(iOS 11.0, *)) {
self.navigationBar.prefersLargeTitles = false;
}
if (iosMajorVersion() >= 8 && !TGIsRTL())
{
object_setClass(self.interactivePopGestureRecognizer, [TGNavigationPanGestureRecognizer class]);
self.interactivePopGestureRecognizer.delaysTouchesBegan = false;
self.interactivePopGestureRecognizer.delaysTouchesEnded = true;
self.interactivePopGestureRecognizer.cancelsTouchesInView = true;
_gestureDelegate = [[TGNavigationGestureDelegateProxy alloc] init];
_gestureDelegate.delegate = self.interactivePopGestureRecognizer.delegate;
__weak TGNavigationController *weakSelf = self;
_gestureDelegate.shouldBegin = ^bool(TGNavigationPanGestureRecognizer *gestureRecognizer)
{
__strong TGNavigationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
bool shouldBegin = [strongSelf _gestureRecognizerShouldBegin:gestureRecognizer];
if (shouldBegin)
{
CGPoint location = [gestureRecognizer locationInView:nil];
CGPoint velocity = [gestureRecognizer velocityInView:nil];
if (strongSelf->_fixNextInteractiveTransition)
{
bool fixableLocation = (!TGIsRTL() && location.x > 44.0f) || (TGIsRTL() && location.x < gestureRecognizer.view.frame.size.width - 44.0f);
bool fixableVelovity = TGIsRTL() ? velocity.x <= -150 : velocity.x >= 150;
if (fixableLocation && fixableVelovity)
{
[strongSelf popViewControllerAnimated:true];
shouldBegin = false;
}
}
strongSelf->_fixNextInteractiveTransition = false;
}
return shouldBegin;
}
return false;
};
_gestureDelegate.shouldRecognizeWith = ^bool(TGNavigationPanGestureRecognizer *gestureRecognizer, UIGestureRecognizer *otherGestureRecognizer)
{
__strong TGNavigationController *strongSelf = weakSelf;
if (strongSelf != nil)
return [strongSelf _gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
return false;
};
self.interactivePopGestureRecognizer.delegate = _gestureDelegate;
if ([TGViewController hasTallScreen] && _becomeActiveObserver == nil && _enterBackgroundObserver == nil)
{
__weak TGNavigationController *weakSelf = self;
_enterBackgroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(__unused NSNotification * _Nonnull note)
{
__strong TGNavigationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf->_enterBackgroundObserver];
strongSelf->_enterBackgroundObserver = nil;
if (strongSelf->_becomeActiveObserver == nil)
{
strongSelf->_becomeActiveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
__strong TGNavigationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if ([TGViewController hasTallScreen])
strongSelf->_fixNextInteractiveTransition = true;
}
}];
}
}
}];
}
}
}
- (bool)_gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
{
if (_animatingControllerPush)
return false;
CGPoint velocity = [gestureRecognizer velocityInView:gestureRecognizer.view];
if (fabs(velocity.y) > fabs(velocity.x))
return false;
if ((!TGIsRTL() && velocity.x < FLT_EPSILON) || (TGIsRTL() && velocity.x > FLT_EPSILON))
return false;
CGPoint location = [gestureRecognizer locationInView:gestureRecognizer.view];
if (location.y < CGRectGetMaxY([self.navigationBar convertRect:self.navigationBar.bounds toView:gestureRecognizer.view]))
return false;
UIView *view = [gestureRecognizer.view hitTest:location withEvent:nil];
if (view.tag == 0xdead || ([view isKindOfClass:[UIControl class]] && ![view isKindOfClass:[UIButton class]]))
return false;
return self.viewControllers.count > 1;
}
- (bool)_gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (_animatingControllerPush)
return false;
CGPoint location = [gestureRecognizer locationInView:gestureRecognizer.view];
if (self.viewControllers.count == 1)
return false;
bool (^isEdgePan)(void) = ^bool
{
if ((!TGIsRTL() && location.x < 44.0f) || (TGIsRTL() && location.x > gestureRecognizer.view.frame.size.width - 44.0f))
{
CGPoint velocity = [gestureRecognizer velocityInView:gestureRecognizer.view];
if (fabs(velocity.x) >= fabs(velocity.y))
{
otherGestureRecognizer.enabled = false;
otherGestureRecognizer.enabled = true;
return true;
}
}
return false;
};
if (otherGestureRecognizer.view.tag == 0xdead)
{
return false;
}
else if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]])
{
UIScrollView *scrollView = (UIScrollView *)otherGestureRecognizer.view;
bool viewIsHorizontalScrollView = !TGIsRTL() && scrollView.contentSize.height > FLT_EPSILON && scrollView.contentSize.width > scrollView.contentSize.height && fabs(scrollView.contentOffset.x + scrollView.contentInset.left) < FLT_EPSILON && scrollView.tag != 0xbeef;
bool viewIsDeceleratingScrollView = scrollView.contentSize.height > scrollView.contentSize.width && scrollView.isDecelerating;
if (viewIsHorizontalScrollView || viewIsDeceleratingScrollView)
{
if (viewIsHorizontalScrollView)
{
CGPoint velocity = [gestureRecognizer velocityInView:gestureRecognizer.view];
if ((!TGIsRTL() && velocity.x < FLT_EPSILON) || (TGIsRTL() && velocity.x > FLT_EPSILON))
return false;
otherGestureRecognizer.enabled = false;
otherGestureRecognizer.enabled = true;
}
return true;
}
else
{
if (isEdgePan())
return true;
}
}
else
{
if (isEdgePan())
return true;
}
return false;
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
_animatingControllerPush = false;
}
- (void)setDisplayPlayer:(bool)displayPlayer
{
_displayPlayer = displayPlayer;
if (_displayPlayer && [self.navigationBar isKindOfClass:[TGNavigationBar class]])
{
__weak TGNavigationController *weakSelf = self;
[_playerStatusDisposable dispose];
_playerStatusDisposable = [[[TGNavigationBar musicPlayerProvider].musicPlayerIsActive deliverOn:[SQueue mainQueue]] startStrictWithNext:^(NSNumber *nIsActive)
{
__strong TGNavigationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
bool isActive = [nIsActive boolValue];
if (isActive && strongSelf->_currentAdditionalNavigationBarHeight < FLT_EPSILON)
{
strongSelf->_minimizePlayer = false;
[(TGNavigationBar *)self.navigationBar setMinimizedMusicPlayer:_minimizePlayer];
}
CGFloat currentAdditionalNavigationBarHeight = !isActive ? 0.0f : (strongSelf->_minimizePlayer ? 2.0f : 37.0f);
if (ABS(strongSelf->_currentAdditionalNavigationBarHeight - currentAdditionalNavigationBarHeight) > FLT_EPSILON)
{
strongSelf->_currentAdditionalNavigationBarHeight = currentAdditionalNavigationBarHeight;
[((TGNavigationBar *)strongSelf.navigationBar) showMusicPlayerView:isActive animation:^
{
[strongSelf updatePlayerOnControllers];
}];
}
}
} file:__FILE_NAME__ line:__LINE__];
}
else
{
[_playerStatusDisposable dispose];
}
}
- (void)setMinimizePlayer:(bool)minimizePlayer
{
if (_minimizePlayer != minimizePlayer)
{
_minimizePlayer = minimizePlayer;
if (_currentAdditionalNavigationBarHeight > FLT_EPSILON)
_currentAdditionalNavigationBarHeight = _minimizePlayer ? 2.0f : 37.0f;
[(TGNavigationBar *)self.navigationBar setMinimizedMusicPlayer:_minimizePlayer];
[UIView animateWithDuration:0.25 animations:^
{
[self updatePlayerOnControllers];
}];
}
}
- (void)setupStatusBarOnControllers:(NSArray *)controllers
{
if ([[self navigationBar] isKindOfClass:[TGNavigationBar class]])
{
for (id maybeController in controllers)
{
if ([maybeController isKindOfClass:[TGViewController class]])
{
TGViewController *controller = maybeController;
[controller setAdditionalStatusBarHeight:_currentAdditionalStatusBarHeight];
}
else if ([maybeController isKindOfClass:[UITabBarController class]] && [maybeController conformsToProtocol:@protocol(TGNavigationControllerTabsController)])
{
[self setupPlayerOnControllers:((UITabBarController *)maybeController).viewControllers];
}
}
}
}
- (void)updateStatusBarOnControllers
{
if ([[self navigationBar] isKindOfClass:[TGNavigationBar class]])
{
for (id maybeController in [self viewControllers])
{
if ([maybeController isKindOfClass:[TGViewController class]])
{
TGViewController *viewController = (TGViewController *)maybeController;
[viewController setAdditionalStatusBarHeight:_currentAdditionalStatusBarHeight];
[viewController setNeedsStatusBarAppearanceUpdate];
}
else if ([maybeController isKindOfClass:[UITabBarController class]] && [maybeController conformsToProtocol:@protocol(TGNavigationControllerTabsController)])
{
for (id controller in ((UITabBarController *)maybeController).viewControllers)
{
if ([controller isKindOfClass:[TGViewController class]])
{
[((TGViewController *)controller) setAdditionalStatusBarHeight:_currentAdditionalStatusBarHeight];
}
}
if (iosMajorVersion() >= 7)
[((UIViewController *)maybeController) setNeedsStatusBarAppearanceUpdate];
}
}
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)dimmingViewTapped:(UITapGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateRecognized)
{
[self.presentingViewController dismissViewControllerAnimated:true completion:nil];
}
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGSize screenSize = TGScreenSize();
static Class containerClass = nil;
if (freedomInitialized())
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
containerClass = freedomClass(0xf045e5dfU);
});
}
for (UIView *view in self.view.subviews)
{
bool isContainerView = false;
if (freedomInitialized())
isContainerView = [view isKindOfClass:containerClass];
else
isContainerView = [NSStringFromClass(view.class) rangeOfString:@"TransitionView"].location != NSNotFound;
if (isContainerView)
{
CGRect frame = view.frame;
if (ABS(frame.size.width - screenSize.width) < FLT_EPSILON)
{
if (ABS(frame.size.height - screenSize.height + 20) < FLT_EPSILON)
{
frame.origin.y = frame.size.height - screenSize.height;
frame.size.height = screenSize.height;
}
else if (frame.size.height > screenSize.height + FLT_EPSILON)
{
frame.origin.y = 0;
frame.size.height = screenSize.height;
}
}
else if (ABS(frame.size.width - screenSize.height) < FLT_EPSILON)
{
if (frame.size.height > screenSize.width + FLT_EPSILON)
{
frame.origin.y = 0;
frame.size.height = screenSize.width;
}
}
if (ABS(frame.size.height) < FLT_EPSILON)
{
frame.size.height = screenSize.height;
}
if (!CGRectEqualToRect(view.frame, frame))
view.frame = frame;
break;
}
}
_didFirstLayout = true;
}
- (void)viewDidLoad
{
self.delegate = self;
[super viewDidLoad];
}
- (void)updateControllerLayout:(bool)__unused animated
{
}
- (void)setupNavigationBarForController:(UIViewController *)viewController animated:(bool)animated
{
UIBarStyle barStyle = UIBarStyleDefault;
bool navigationBarShouldBeHidden = false;
UIStatusBarStyle statusBarStyle = UIStatusBarStyleLightContent;
bool statusBarShouldBeHidden = false;
if ([viewController conformsToProtocol:@protocol(TGViewControllerNavigationBarAppearance)])
{
id<TGViewControllerNavigationBarAppearance> appearance = (id<TGViewControllerNavigationBarAppearance>)viewController;
barStyle = [appearance requiredNavigationBarStyle];
navigationBarShouldBeHidden = [appearance navigationBarShouldBeHidden];
if ([appearance respondsToSelector:@selector(preferredStatusBarStyle)])
statusBarStyle = [appearance preferredStatusBarStyle];
if ([appearance respondsToSelector:@selector(statusBarShouldBeHidden)])
statusBarShouldBeHidden = [appearance statusBarShouldBeHidden];
}
if (navigationBarShouldBeHidden != self.navigationBarHidden)
{
[self setNavigationBarHidden:navigationBarShouldBeHidden animated:animated];
}
if ([[LegacyComponentsGlobals provider] isStatusBarHidden] != statusBarShouldBeHidden)
[[LegacyComponentsGlobals provider] setStatusBarHidden:statusBarShouldBeHidden withAnimation:animated ? UIStatusBarAnimationFade : UIStatusBarAnimationNone];
if ([[LegacyComponentsGlobals provider] statusBarStyle] != statusBarStyle)
[[LegacyComponentsGlobals provider] setStatusBarStyle:statusBarStyle animated:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (_restrictLandscape)
return interfaceOrientation == UIInterfaceOrientationPortrait;
if (self.topViewController != nil)
return [self.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (BOOL)shouldAutorotate
{
if (_restrictLandscape)
return false;
if (self.topViewController != nil)
{
if ([self.topViewController respondsToSelector:@selector(shouldAutorotate)])
{
if (![self.topViewController shouldAutorotate])
return false;
}
}
bool result = [super shouldAutorotate];
if (!result)
return false;
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
{
UIGestureRecognizerState state = self.interactivePopGestureRecognizer.state;
if (state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged)
return false;
}
return true;
}
- (void)acquireRotationLock
{
if (_autorotationLock == nil)
_autorotationLock = [[TGAutorotationLock alloc] init];
}
- (void)releaseRotationLock
{
_autorotationLock = nil;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
if (_restrictLandscape)
return UIInterfaceOrientationMaskPortrait;
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated
{
if (!hidden)
self.navigationBar.alpha = 1.0f;
[(TGNavigationBar *)self.navigationBar setHiddenState:hidden animated:animated];
[super setNavigationBarHidden:hidden animated:animated];
}
- (void)setupPlayerOnControllers:(NSArray *)controllers
{
if ((_displayPlayer || _forceAdditionalNavigationBarHeight) && [[self navigationBar] isKindOfClass:[TGNavigationBar class]])
{
for (id maybeController in controllers)
{
if ([maybeController isKindOfClass:[TGViewController class]])
{
TGViewController *controller = maybeController;
[controller setAdditionalNavigationBarHeight:_currentAdditionalNavigationBarHeight];
}
else if (_displayPlayer && [maybeController isKindOfClass:[UITabBarController class]] && [maybeController conformsToProtocol:@protocol(TGNavigationControllerTabsController)])
{
[self setupPlayerOnControllers:((UITabBarController *)maybeController).viewControllers];
}
}
}
}
- (void)updatePlayerOnControllers
{
if ((_displayPlayer || _forceAdditionalNavigationBarHeight) && [[self navigationBar] isKindOfClass:[TGNavigationBar class]])
{
for (id maybeController in [self viewControllers])
{
if ([maybeController isKindOfClass:[TGViewController class]])
{
[((TGViewController *)maybeController) setAdditionalNavigationBarHeight:_currentAdditionalNavigationBarHeight];
}
else if (_displayPlayer && [maybeController isKindOfClass:[UITabBarController class]] && [maybeController conformsToProtocol:@protocol(TGNavigationControllerTabsController)])
{
for (id controller in ((UITabBarController *)maybeController).viewControllers)
{
if ([controller isKindOfClass:[TGViewController class]])
{
[((TGViewController *)controller) setAdditionalNavigationBarHeight:_currentAdditionalNavigationBarHeight];
}
}
}
}
}
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (_animatingControllerPush)
return;
_fixNextInteractiveTransition = false;
if (self.viewControllers.count == 0) {
if ([viewController isKindOfClass:[TGViewController class]]) {
[(TGViewController *)viewController setIsFirstInStack:true];
} else {
[(TGViewController *)viewController setIsFirstInStack:false];
}
}
_isInControllerTransition = true;
if (viewController != nil) {
[self setupPlayerOnControllers:@[viewController]];
[self setupStatusBarOnControllers:@[viewController]];
}
[super pushViewController:viewController animated:animated];
_isInControllerTransition = false;
_animatingControllerPush = animated;
if (_animatingControllerPush)
{
TGDispatchAfter(0.3, dispatch_get_main_queue(), ^
{
_animatingControllerPush = false;
});
}
if (iosMajorVersion() >= 8 && [viewController isKindOfClass:[QLPreviewController class]])
object_setClass(self.interactivePopGestureRecognizer, [UIScreenEdgePanGestureRecognizer class]);
}
- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
{
bool first = true;
for (id controller in viewControllers) {
if ([controller isKindOfClass:[TGViewController class]]) {
[(TGViewController *)controller setIsFirstInStack:first];
}
first = false;
}
_isInControllerTransition = true;
[self setupPlayerOnControllers:viewControllers];
[self setupStatusBarOnControllers:viewControllers];
[super setViewControllers:viewControllers animated:animated];
_isInControllerTransition = false;
_animatingControllerPush = animated;
if (_animatingControllerPush)
{
TGDispatchAfter(0.3, dispatch_get_main_queue(), ^
{
_animatingControllerPush = false;
});
}
}
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
if (self.viewControllers.count < navigationBar.items.count)
return true;
bool shouldPop = true;
if (self.shouldPopController != nil) {
shouldPop = self.shouldPopController(self.topViewController);
}
if (shouldPop)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self popViewControllerAnimated:YES];
});
} else
{
for (UIView *subview in [navigationBar subviews])
{
if (0.< subview.alpha && subview.alpha < 1.)
{
[UIView animateWithDuration:.25 animations:^
{
subview.alpha = 1.;
}];
}
}
}
return false;
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
_fixNextInteractiveTransition = false;
if (animated)
{
static ptrdiff_t controllerOffset = -1;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
controllerOffset = freedomIvarOffset([UINavigationController class], 0xb281e8fU);
});
if (controllerOffset != -1)
{
__unsafe_unretained NSObject **controller = (__unsafe_unretained NSObject **)(void *)(((uint8_t *)(__bridge void *)self) + controllerOffset);
if (*controller != nil)
{
static Class decoratedClass = Nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
decoratedClass = freedomMakeClass([*controller class], [TGNavigationPercentTransition class]);
});
if (decoratedClass != Nil && ![*controller isKindOfClass:decoratedClass])
object_setClass(*controller, decoratedClass);
}
}
}
_isInPopTransition = true;
UIViewController *result = [super popViewControllerAnimated:animated];
_isInPopTransition = false;
if (iosMajorVersion() >= 8 && !TGIsRTL() && [self.interactivePopGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
object_setClass(self.interactivePopGestureRecognizer, [TGNavigationPanGestureRecognizer class]);
return result;
}
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
for (NSUInteger i = self.viewControllers.count - 1; i >= 1; i--)
{
UIViewController *viewController = self.viewControllers[i];
if (viewController.presentedViewController != nil)
[viewController dismissViewControllerAnimated:false completion:nil];
}
return [super popToRootViewControllerAnimated:animated];
}
TGNavigationController *findNavigationControllerInWindow(UIWindow *window)
{
if ([window.rootViewController isKindOfClass:[TGNavigationController class]])
return (TGNavigationController *)window.rootViewController;
return nil;
}
TGNavigationController *findNavigationController()
{
NSArray *windows = [[LegacyComponentsGlobals provider] applicationWindows];
for (int i = (int)windows.count - 1; i >= 0; i--)
{
TGNavigationController *result = findNavigationControllerInWindow(windows[i]);
if (result != nil)
return result;
}
return nil;
}
- (CGFloat)myNominalTransitionAnimationDuration
{
return 0.2f;
}
- (void)setPreferredContentSize:(CGSize)preferredContentSize
{
_preferredContentSize = preferredContentSize;
}
- (CGSize)preferredContentSize
{
return _preferredContentSize;
}
- (BOOL)prefersStatusBarHidden
{
if (iosMajorVersion() >= 7)
return [super prefersStatusBarHidden];
return false;
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return self.topViewController.preferredStatusBarStyle;
}
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
if (_customDismissSelf) {
_customDismissSelf();
if (completion) {
completion();
}
} else {
[super dismissViewControllerAnimated:flag completion:completion];
}
}
@end
@implementation TGNavigationPercentTransition
- (void)updateInteractiveTransition:(CGFloat)percentComplete
{
[super updateInteractiveTransition:percentComplete];
}
- (void)finishInteractiveTransition
{
[super finishInteractiveTransition];
}
- (void)cancelInteractiveTransition
{
[super cancelInteractiveTransition];
}
@end
@implementation TGDelegateProxy
@end
@implementation TGNavigationPanGestureRecognizer
- (UIRectEdge)edges {
return UIRectEdgeLeft;
}
- (void)_setEdgeRegionSize:(CGFloat)edgeRegionSize
{
__edgeRegionSize = edgeRegionSize;
}
- (id)recognizerTouchesToIgnoreForEvent:(id)event
{
return nil;
}
@end
@implementation TGNavigationGestureDelegateProxy
- (BOOL)gestureRecognizerShouldBegin:(TGNavigationPanGestureRecognizer *)gestureRecognizer
{
bool result = [self.delegate respondsToSelector:@selector(gestureRecognizerShouldBegin:)] && [self.delegate gestureRecognizerShouldBegin:gestureRecognizer];
if (result && self.shouldBegin != nil)
result = self.shouldBegin(gestureRecognizer);
return result;
}
- (BOOL)gestureRecognizer:(TGNavigationPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
bool result = [self.delegate respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)] && [self.delegate gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
if (!result && self.shouldRecognizeWith != nil)
result = self.shouldRecognizeWith(gestureRecognizer, otherGestureRecognizer);
return result;
}
@end