#import "TGViewController.h" #import "LegacyComponentsInternal.h" #import "TGFont.h" #import "TGImageUtils.h" #import "Freedom.h" #import "TGNavigationController.h" #import "TGOverlayControllerWindow.h" #import #import "TGHacks.h" #import static __strong NSTimer *autorotationEnableTimer = nil; static bool autorotationDisabled = false; static std::set autorotationLockIds; @interface TGViewControllerSizeView : UIView { CGSize _validSize; } @property (nonatomic, copy) void (^sizeChanged)(CGSize size); @end @implementation TGViewControllerSizeView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self != nil) { _validSize = frame.size; } return self; } - (void)setFrame:(CGRect)frame { [super setFrame:frame]; if (!CGSizeEqualToSize(_validSize, frame.size)) { _validSize = frame.size; if (_sizeChanged) { _sizeChanged(frame.size); } } } - (void)setBounds:(CGRect)bounds { [super setBounds:bounds]; if (!CGSizeEqualToSize(_validSize, bounds.size)) { _validSize = bounds.size; if (_sizeChanged) { _sizeChanged(bounds.size); } } } @end @interface UIViewController () - (void)setAutomaticallyAdjustsScrollViewInsets:(BOOL)value; @end @implementation TGAutorotationLock - (id)init { self = [super init]; if (self != nil) { static int nextId = 1; _lockId = nextId++; int lockId = _lockId; if ([NSThread isMainThread]) { autorotationLockIds.insert(lockId); } else { dispatch_async(dispatch_get_main_queue(), ^ { autorotationLockIds.insert(lockId); }); } } return self; } - (void)dealloc { int lockId = _lockId; if ([NSThread isMainThread]) { autorotationLockIds.erase(lockId); } else { dispatch_async(dispatch_get_main_queue(), ^ { autorotationLockIds.erase(lockId); }); } } @end @interface TGViewController () { id _context; bool _hatTargetNavigationItem; id _sizeClassDisposable; NSTimeInterval _currentSizeChangeDuration; } @property (nonatomic, strong) UIView *viewControllerStatusBarBackgroundView; @property (nonatomic) UIInterfaceOrientation viewControllerRotatingFromOrientation; @property (nonatomic, weak) UINavigationItem *targetNavigationItem; @property (nonatomic, weak) UIViewController *targetNavigationTitleController; @property (nonatomic, strong) UIBarButtonItem *leftBarButtonItem; @property (nonatomic, strong) NSArray *rightBarButtonItems; @property (nonatomic, strong) NSString *titleText; @property (nonatomic, strong) UIView *titleView; @property (nonatomic, strong) TGAutorotationLock *autorotationLock; @end @implementation TGViewController static id _defaultContext = nil; + (void)setDefaultContext:(id)defaultContext { _defaultContext = defaultContext; } + (UIFont *)titleFontForStyle:(TGViewControllerStyle)__unused style landscape:(bool)landscape { if (!landscape) { static UIFont *font = nil; if (font == nil) font = TGBoldSystemFontOfSize(20); return font; } else { static UIFont *font = nil; if (font == nil) font = TGBoldSystemFontOfSize(17); return font; } } + (UIFont *)titleTitleFontForStyle:(TGViewControllerStyle)__unused style landscape:(bool)landscape { if (!landscape) { static UIFont *font = nil; if (font == nil) font = TGBoldSystemFontOfSize(16); return font; } else { static UIFont *font = nil; if (font == nil) font = TGBoldSystemFontOfSize(15); return font; } } + (UIFont *)titleSubtitleFontForStyle:(TGViewControllerStyle)__unused style landscape:(bool)landscape { if (!landscape) { static UIFont *font = nil; if (font == nil) font = TGSystemFontOfSize(13); return font; } else { static UIFont *font = nil; if (font == nil) font = TGSystemFontOfSize(13); return font; } } + (UIColor *)titleTextColorForStyle:(TGViewControllerStyle)style { if (style == TGViewControllerStyleDefault) { static UIColor *color = nil; if (color == nil) color = UIColorRGB(0xffffff); return color; } else { static UIColor *color = nil; if (color == nil) color = UIColorRGB(0xffffff); return color; } } + (CGSize)screenSize:(UIDeviceOrientation)orientation { CGSize mainScreenSize = TGScreenSize(); CGSize size = CGSizeZero; if (UIDeviceOrientationIsPortrait(orientation)) size = CGSizeMake(mainScreenSize.width, mainScreenSize.height); else size = CGSizeMake(mainScreenSize.height, mainScreenSize.width); return size; } + (CGSize)screenSizeForInterfaceOrientation:(UIInterfaceOrientation)orientation { CGSize mainScreenSize = TGScreenSize(); CGSize size = CGSizeZero; if (UIInterfaceOrientationIsPortrait(orientation)) size = CGSizeMake(mainScreenSize.width, mainScreenSize.height); else size = CGSizeMake(mainScreenSize.height, mainScreenSize.width); return size; } + (bool)isWidescreen { static bool isWidescreenInitialized = false; static bool isWidescreen = false; if (!isWidescreenInitialized) { isWidescreenInitialized = true; CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:UIInterfaceOrientationPortrait]; if (screenSize.width > 321 || screenSize.height > 481) isWidescreen = true; } return isWidescreen; } + (bool)hasLargeScreen { static bool value = false; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:UIInterfaceOrientationPortrait]; CGFloat side = MAX(screenSize.width, screenSize.height); value = side >= 667.0f - FLT_EPSILON; }); return value; } + (bool)hasVeryLargeScreen { static bool value = false; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:UIInterfaceOrientationPortrait]; CGFloat side = MAX(screenSize.width, screenSize.height); value = side >= 736 - FLT_EPSILON; }); return value; } + (bool)hasTallScreen { static bool value = false; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:UIInterfaceOrientationPortrait]; CGFloat side = MAX(screenSize.width, screenSize.height); value = side >= 812 - FLT_EPSILON; }); return value; } + (void)disableAutorotation { autorotationDisabled = true; } + (void)enableAutorotation { autorotationDisabled = false; } + (void)disableAutorotationFor:(NSTimeInterval)timeInterval { [self disableAutorotationFor:timeInterval reentrant:false]; } + (void)disableAutorotationFor:(NSTimeInterval)timeInterval reentrant:(bool)reentrant { if (reentrant && autorotationDisabled) return; autorotationDisabled = true; if (autorotationEnableTimer != nil) { if ([autorotationEnableTimer isValid]) { [autorotationEnableTimer invalidate]; } autorotationEnableTimer = nil; } autorotationEnableTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval] interval:0 target:self selector:@selector(enableTimerEvent) userInfo:nil repeats:false]; [[NSRunLoop mainRunLoop] addTimer:autorotationEnableTimer forMode:NSRunLoopCommonModes]; } + (bool)autorotationAllowed { return !autorotationDisabled && autorotationLockIds.empty(); } + (void)attemptAutorotation { if ([TGViewController autorotationAllowed]) { [UIViewController attemptRotationToDeviceOrientation]; } } + (void)enableTimerEvent { autorotationDisabled = false; [self attemptAutorotation]; autorotationEnableTimer = nil; } - (id)initWithNibName:(NSString *)__unused nibNameOrNil bundle:(NSBundle *)__unused nibBundleOrNil { return [self initWithContext:_defaultContext]; } - (id)init { return [self initWithContext:_defaultContext]; } - (id)initWithContext:(id)context { self = [super initWithNibName:nil bundle:nil]; if (self != nil) { [self _commonViewControllerInit:context]; } return self; } - (id)initWithCoder:(NSCoder *)coder { return [self init]; } - (void)_commonViewControllerInit:(id)context { assert(context != nil); _context = context; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" self.wantsFullScreenLayout = true; #pragma clang diagnostic pop self.automaticallyManageScrollViewInsets = true; self.autoManageStatusBarBackground = true; __block bool initializedSizeClass = false; _currentSizeClass = UIUserInterfaceSizeClassCompact; __weak TGViewController *weakSelf = self; _sizeClassDisposable = [[_context sizeClassSignal] startStrictWithNext:^(NSNumber *next) { __strong TGViewController *strongSelf = weakSelf; if (strongSelf != nil) { if (strongSelf->_currentSizeClass != [next integerValue]) { strongSelf->_currentSizeClass = (UIUserInterfaceSizeClass)[next integerValue]; if (initializedSizeClass) { [strongSelf updateSizeClass]; } } } } file:__FILE_NAME__ line:__LINE__]; initializedSizeClass = true; if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) [self setAutomaticallyAdjustsScrollViewInsets:false]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewControllerStatusBarWillChangeFrame:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewControllerKeyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewControllerKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } - (void)dealloc { [_sizeClassDisposable dispose]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } - (id)context { return _context; } - (void)presentWithContext:(UIViewController *(^)(id))generator { UIViewController *controller = generator(_context); [self presentViewController:controller animated:true completion:nil]; } - (NSMutableArray *)associatedWindowStack { if (_associatedWindowStack == nil) _associatedWindowStack = [[NSMutableArray alloc] init]; return _associatedWindowStack; } - (UINavigationController *)navigationController { UIViewController *customParentViewController = _customParentViewController; if (customParentViewController.navigationController != nil) return customParentViewController.navigationController; return [super navigationController]; } - (bool)shouldIgnoreStatusBarInOrientation:(UIInterfaceOrientation)orientation { if (_currentSizeClass != UIUserInterfaceSizeClassCompact) { if ([self.navigationController isKindOfClass:[TGNavigationController class]]) { switch (((TGNavigationController *)self.navigationController).presentationStyle) { case TGNavigationControllerPresentationStyleRootInPopover: case TGNavigationControllerPresentationStyleChildInPopover: case TGNavigationControllerPresentationStyleInFormSheet: return true; default: break; } } } if (!TGIsPad() && iosMajorVersion() >= 11) return UIInterfaceOrientationIsLandscape(orientation); return false; } - (bool)shouldIgnoreStatusBar { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self shouldIgnoreStatusBarInOrientation:self.interfaceOrientation]; #pragma clang diagnostic pop } - (bool)shouldIgnoreNavigationBar { if ([self.navigationController isKindOfClass:[TGNavigationController class]]) { switch (((TGNavigationController *)self.navigationController).presentationStyle) { case TGNavigationControllerPresentationStyleRootInPopover: return iosMajorVersion() < 8; default: break; } } return false; } - (bool)inPopover { if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) return false; else { if ([self.navigationController isKindOfClass:[TGNavigationController class]]) { switch (((TGNavigationController *)self.navigationController).presentationStyle) { case TGNavigationControllerPresentationStyleRootInPopover: case TGNavigationControllerPresentationStyleChildInPopover: return true; default: break; } } return false; } } - (bool)inFormSheet { if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) return false; else { if ([self.navigationController isKindOfClass:[TGNavigationController class]]) { switch (((TGNavigationController *)self.navigationController).presentationStyle) { case TGNavigationControllerPresentationStyleInFormSheet: return true; default: break; } } return self.modalPresentationStyle == UIModalPresentationFormSheet; } } - (bool)willCaptureInputShortly { return false; } - (void)acquireRotationLock { if (_autorotationLock == nil) _autorotationLock = [[TGAutorotationLock alloc] init]; } - (void)releaseRotationLock { _autorotationLock = nil; } - (void)localizationUpdated { } + (int)preferredAnimationCurve { return iosMajorVersion() >= 7 ? 7 : 0; } - (CGSize)referenceViewSizeForOrientation:(UIInterfaceOrientation)orientation { if ([self inFormSheet]) return CGSizeMake(540.0f, 620.0f); else if ([self inPopover]) return CGSizeMake(320.0f, 528.0f); else return [TGViewController screenSizeForInterfaceOrientation:orientation]; } - (UIInterfaceOrientation)currentInterfaceOrientation { if ([self inFormSheet]) return UIInterfaceOrientationPortrait; return (self.view.bounds.size.width >= TGScreenSize().height - FLT_EPSILON) ? UIInterfaceOrientationLandscapeLeft : UIInterfaceOrientationPortrait; } - (BOOL)shouldAutorotate { if (self.presentedViewController != nil && ![self.presentedViewController shouldAutorotate]) return false; return [TGViewController autorotationAllowed]; } - (void)loadView { [super loadView]; TGViewControllerSizeView *sizeView = [[TGViewControllerSizeView alloc] initWithFrame:self.view.bounds]; sizeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; __weak TGViewController *weakSelf = self; sizeView.sizeChanged = ^(CGSize size) { __strong TGViewController *strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf layoutControllerForSize:size duration:strongSelf->_currentSizeChangeDuration]; } }; sizeView.userInteractionEnabled = false; sizeView.hidden = true; [self.view addSubview:sizeView]; } - (void)viewDidLoad { if (_autoManageStatusBarBackground && [self preferredStatusBarStyle] == UIStatusBarStyleDefault && ![self shouldIgnoreStatusBar]) { _viewControllerStatusBarBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 20)]; _viewControllerStatusBarBackgroundView.userInteractionEnabled = false; _viewControllerStatusBarBackgroundView.layer.zPosition = 1000; _viewControllerStatusBarBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth; _viewControllerStatusBarBackgroundView.backgroundColor = [UIColor blackColor]; if (iosMajorVersion() < 7) [self.view addSubview:_viewControllerStatusBarBackgroundView]; } [super viewDidLoad]; } - (void)viewWillAppear:(BOOL)animated { if (_ignoreAppearEvents) { return; } _viewControllerIsAnimatingAppearanceTransition = true; _viewControllerIsAppearing = true; //_viewControllerHasEverAppeared = true; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && iosMajorVersion() < 7) { CGSize size = CGSizeMake(320, 491); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" self.contentSizeForViewInPopover = size; #pragma clang diagnostic pop } if ([self.navigationController isKindOfClass:[TGNavigationController class]]) [(TGNavigationController *)self.navigationController setupNavigationBarForController:self animated:animated]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation force:false notify:true]; [self adjustToInterfaceOrientation:self.interfaceOrientation]; #pragma clang diagnostic pop [super viewWillAppear:animated]; if (self.customAppearanceMethodsForwarding) { for (UIViewController *controller in self.childViewControllers) { [controller viewWillAppear:animated]; } } } - (void)viewDidAppear:(BOOL)animated { if (_ignoreAppearEvents) { return; } _viewControllerIsAppearing = false; _viewControllerIsAnimatingAppearanceTransition = false; _viewControllerHasEverAppeared = true; [super viewDidAppear:animated]; if (self.customAppearanceMethodsForwarding) { for (UIViewController *controller in self.childViewControllers) { [controller viewDidAppear:animated]; } } } - (void)viewWillDisappear:(BOOL)animated { _viewControllerIsDisappearing = true; _viewControllerIsAnimatingAppearanceTransition = true; [super viewWillDisappear:animated]; if (self.customAppearanceMethodsForwarding) { for (UIViewController *controller in self.childViewControllers) { [controller viewWillDisappear:animated]; } } } - (void)viewDidDisappear:(BOOL)animated { _viewControllerIsDisappearing = false; _viewControllerIsAnimatingAppearanceTransition = false; [super viewDidDisappear:animated]; if (self.customAppearanceMethodsForwarding) { for (UIViewController *controller in self.childViewControllers) { [controller viewDidDisappear:animated]; } } } - (void)_adjustControllerInsetForRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { float additionalKeyboardHeight = [self _keyboardAdditionalDeltaHeightWhenRotatingFrom:_viewControllerRotatingFromOrientation toOrientation:toInterfaceOrientation]; CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; [self _updateControllerInsetForOrientation:toInterfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:[self _currentKeyboardHeight:toInterfaceOrientation] + additionalKeyboardHeight force:false notify:true]; } - (UIEdgeInsets)controllerInsetForInterfaceOrientation:(UIInterfaceOrientation)orientation { UIEdgeInsets safeAreaInset = _context.safeAreaInset; CGFloat statusBarHeight = safeAreaInset.top > FLT_EPSILON ? safeAreaInset.top : 0.0; CGFloat keyboardHeight = [self _currentKeyboardHeight:orientation]; CGFloat navigationBarHeight = ([self navigationBarShouldBeHidden] || [self shouldIgnoreNavigationBar]) ? 0 : [self navigationBarHeightForInterfaceOrientation:orientation]; UIEdgeInsets edgeInset = UIEdgeInsetsMake(([self shouldIgnoreStatusBarInOrientation:orientation] ? 0.0f : statusBarHeight) + navigationBarHeight, 0, 0, 0); edgeInset.left += _parentInsets.left; edgeInset.top += _parentInsets.top; edgeInset.right += _parentInsets.right; edgeInset.bottom += _parentInsets.bottom; if ([self.parentViewController isKindOfClass:[UITabBarController class]]) edgeInset.bottom += [self tabBarHeight:UIInterfaceOrientationIsLandscape(orientation)]; if (!_ignoreKeyboardWhenAdjustingScrollViewInsets) edgeInset.bottom = MAX(edgeInset.bottom, keyboardHeight); edgeInset.bottom += safeAreaInset.bottom; edgeInset.left += _explicitTableInset.left; edgeInset.right += _explicitTableInset.right; edgeInset.top += _explicitTableInset.top; edgeInset.bottom += _explicitTableInset.bottom; return edgeInset; } - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { _viewControllerIsChangingInterfaceOrientation = true; _viewControllerRotatingFromOrientation = self.interfaceOrientation; _currentSizeChangeDuration = duration; if (_adjustControllerInsetWhenStartingRotation) [self _adjustControllerInsetForRotationToInterfaceOrientation:toInterfaceOrientation]; [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; } - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [self adjustToInterfaceOrientation:toInterfaceOrientation]; if (!_adjustControllerInsetWhenStartingRotation) [self _adjustControllerInsetForRotationToInterfaceOrientation:toInterfaceOrientation]; [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; } - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { //TGLegacyLog(@"Did rotate"); _viewControllerIsChangingInterfaceOrientation = false; _currentSizeChangeDuration = 0.0; [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; } - (CGFloat)_currentKeyboardHeight:(UIInterfaceOrientation)orientation { if ([self inPopover]) return 0.0f; if ([self isViewLoaded] && !_viewControllerHasEverAppeared && ([self findFirstResponder:self.view] == nil && ![self willCaptureInputShortly])) return 0.0f; return 0.0f; } - (float)_keyboardAdditionalDeltaHeightWhenRotatingFrom:(UIInterfaceOrientation)fromOrientation toOrientation:(UIInterfaceOrientation)toOrientation { if ([TGHacks isKeyboardVisible]) { if (UIInterfaceOrientationIsPortrait(fromOrientation) != UIInterfaceOrientationIsPortrait(toOrientation)) { } } return 0.0f; } + (void)disableUserInteractionFor:(NSTimeInterval)timeInterval { [[LegacyComponentsGlobals provider] disableUserInteractionFor:timeInterval]; } - (CGFloat)_currentStatusBarHeight { if (_context.isStatusBarHidden) return 0.0; if (_context.safeAreaInset.top > 20.0f) return _context.safeAreaInset.top; CGRect statusBarFrame = [[LegacyComponentsGlobals provider] statusBarFrame]; CGFloat minStatusBarHeight = [self prefersStatusBarHidden] ? 0.0f : 20.0f; CGFloat statusBarHeight = MAX(minStatusBarHeight, MIN(statusBarFrame.size.width, statusBarFrame.size.height)); return MIN(40.0f, statusBarHeight + _additionalStatusBarHeight); } - (void)viewControllerStatusBarWillChangeFrame:(NSNotification *)notification { if (!_viewControllerIsChangingInterfaceOrientation) { CGRect statusBarFrame = [[[notification userInfo] objectForKey:UIApplicationStatusBarFrameUserInfoKey] CGRectValue]; CGFloat minStatusBarHeight = [self prefersStatusBarHidden] ? 0.0f : 20.0f; CGFloat statusBarHeight = MAX(minStatusBarHeight, MIN(statusBarFrame.size.width, statusBarFrame.size.height)); statusBarHeight = MIN(40.0f, statusBarHeight + _additionalStatusBarHeight); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; #pragma clang diagnostic pop [UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop } completion:nil]; } } - (UIView *)findFirstResponder:(UIView *)view { if ([view isFirstResponder]) return view; for (UIView *subview in view.subviews) { UIView *result = [self findFirstResponder:subview]; if (result != nil) return result; } return nil; } - (void)viewControllerKeyboardWillChangeFrame:(NSNotification *)notification { if (!_viewControllerIsChangingInterfaceOrientation && ![self inPopover]) { CGFloat statusBarHeight = [self _currentStatusBarHeight]; CGRect keyboardFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGFloat keyboardHeight = MIN(keyboardFrame.size.height, keyboardFrame.size.width); if (CGRectGetMaxY(keyboardFrame) < [UIScreen mainScreen].bounds.size.height) { keyboardHeight = 0.0f; } double duration = ([[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]); if ([self isViewLoaded] && !_viewControllerHasEverAppeared && ([self findFirstResponder:self.view] == nil && ![self willCaptureInputShortly])) { } else if (_viewControllerIsAnimatingAppearanceTransition || !_viewControllerHasEverAppeared) { [UIView performWithoutAnimation:^ { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop }]; } else { [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop } completion:nil]; } } } - (void)viewControllerKeyboardWillHide:(NSNotification *)notification { if (!_viewControllerIsChangingInterfaceOrientation && ![self inPopover]) { CGFloat statusBarHeight = [self _currentStatusBarHeight]; float keyboardHeight = 0.0f; double duration = ([[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]); if ([self isViewLoaded] && !_viewControllerHasEverAppeared && [self findFirstResponder:self.view] == nil && ![self willCaptureInputShortly]) { } else if (_viewControllerIsAnimatingAppearanceTransition || !_viewControllerHasEverAppeared) { [UIView performWithoutAnimation:^ { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop }]; } else { [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop } completion:nil]; } } } #pragma mark - - (void)adjustNavigationItem:(UIInterfaceOrientation)__unused orientation { } #pragma mark - - (UIBarStyle)requiredNavigationBarStyle { return UIBarStyleDefault; } - (bool)navigationBarHasAction { return false; } - (void)navigationBarAction { } - (void)navigationBarSwipeDownAction { } - (bool)statusBarShouldBeHidden { return false; } - (UIStatusBarStyle)preferredStatusBarStyle { if (![_context rootCallStatusBarHidden]) { return UIStatusBarStyleLightContent; } else { if ([_context respondsToSelector:@selector(prefersLightStatusBar)]) return [_context prefersLightStatusBar] ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; else return UIStatusBarStyleDefault; } } - (void)setNeedsStatusBarAppearanceUpdate { if (iosMajorVersion() < 7) return; [super setNeedsStatusBarAppearanceUpdate]; if (iosMajorVersion() < 8) return; if (self.isViewLoaded) { UIWindow *lastWindow = [[LegacyComponentsGlobals provider] applicationWindows].lastObject; if (lastWindow != self.view.window && [lastWindow isKindOfClass:[TGOverlayControllerWindow class]]) { [[LegacyComponentsGlobals provider] forceStatusBarAppearanceUpdate]; } } } - (void)adjustToInterfaceOrientation:(UIInterfaceOrientation)orientation { [self adjustNavigationItem:orientation]; } - (void)setExplicitTableInset:(UIEdgeInsets)explicitTableInset { [self setExplicitTableInset:explicitTableInset scrollIndicatorInset:_explicitScrollIndicatorInset]; } - (void)setExplicitScrollIndicatorInset:(UIEdgeInsets)explicitScrollIndicatorInset { [self setExplicitTableInset:_explicitTableInset scrollIndicatorInset:explicitScrollIndicatorInset]; } - (void)setAdditionalNavigationBarHeight:(CGFloat)additionalNavigationBarHeight { _additionalNavigationBarHeight = additionalNavigationBarHeight; CGFloat statusBarHeight = [self _currentStatusBarHeight]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop } - (void)setAdditionalStatusBarHeight:(CGFloat)additionalStatusBarHeight { _additionalStatusBarHeight = additionalStatusBarHeight; CGFloat statusBarHeight = [self _currentStatusBarHeight]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop } - (void)setExplicitTableInset:(UIEdgeInsets)explicitTableInset scrollIndicatorInset:(UIEdgeInsets)scrollIndicatorInset { _explicitTableInset = explicitTableInset; _explicitScrollIndicatorInset = scrollIndicatorInset; CGFloat statusBarHeight = [self _currentStatusBarHeight]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; #pragma clang diagnostic pop } - (bool)_updateControllerInset:(bool)force { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self _updateControllerInsetForOrientation:self.interfaceOrientation force:force notify:true]; #pragma clang diagnostic pop } - (bool)_updateControllerInsetForOrientation:(UIInterfaceOrientation)orientation force:(bool)force notify:(bool)notify { CGFloat statusBarHeight = [self _currentStatusBarHeight]; CGFloat keyboardHeight = [self _currentKeyboardHeight:orientation]; return [self _updateControllerInsetForOrientation:orientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:force notify:notify]; } - (CGFloat)navigationBarHeightForInterfaceOrientation:(UIInterfaceOrientation)orientation { static CGFloat portraitHeight = 56.0f; static CGFloat landscapeHeight = 32.0f; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { CGSize screenSize = TGScreenSize(); CGFloat widescreenWidth = MAX(screenSize.width, screenSize.height); if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && ABS(widescreenWidth - 736) > FLT_EPSILON) { portraitHeight = 56.0f; landscapeHeight = 56.0f; } else { portraitHeight = 56.0f; landscapeHeight = 56.0f; } }); bool large = UIInterfaceOrientationIsPortrait(orientation) || self.alwaysUseTallNavigationBarHeight; return (large ? portraitHeight : landscapeHeight) + _additionalNavigationBarHeight; } - (CGFloat)tabBarHeight:(bool)landscape { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) return iosMajorVersion() >= 11 ? (landscape ? 32.0f : 49.0f) : 49.0f; else return 56.0f; } - (UIEdgeInsets)calculatedSafeAreaInset { UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait; if (self.view.frame.size.width > self.view.frame.size.height) orientation = UIInterfaceOrientationLandscapeLeft; bool hasOnScreenNavigation = false; if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; } return [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; } + (UIEdgeInsets)safeAreaInsetForOrientation:(UIInterfaceOrientation)orientation hasOnScreenNavigation:(bool)hasOnScreenNavigation { int height = (int)TGScreenSize().height; if (!TGIsPad() && (height != 812 && height != 896 && height != 780 && height != 844 && height != 852 && height != 926 && height != 932) && !hasOnScreenNavigation) return UIEdgeInsetsZero; if (TGIsPad()) { if (height == 1194 || hasOnScreenNavigation) { return UIEdgeInsetsMake(24.0f, 0.0f, 21.0f, 0.0f); } else { return UIEdgeInsetsZero; } } switch (orientation) { case UIInterfaceOrientationLandscapeLeft: case UIInterfaceOrientationLandscapeRight: return UIEdgeInsetsMake(0.0f, 44.0f, 21.0f, 44.0f); default: return UIEdgeInsetsMake(44.0f, 0.0f, 34.0f, 0.0f); } } - (bool)_updateControllerInsetForOrientation:(UIInterfaceOrientation)orientation statusBarHeight:(CGFloat)statusBarHeight keyboardHeight:(CGFloat)keyboardHeight force:(bool)force notify:(bool)notify { bool hasOnScreenNavigation = false; if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; } UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; CGFloat navigationBarHeight = ([self navigationBarShouldBeHidden] || [self shouldIgnoreNavigationBar]) ? 0 : [self navigationBarHeightForInterfaceOrientation:orientation]; //statusBarHeight = safeAreaInset.top > FLT_EPSILON ? safeAreaInset.top : statusBarHeight; UIEdgeInsets edgeInset = UIEdgeInsetsMake(([self shouldIgnoreStatusBarInOrientation:orientation] ? 0.0f : statusBarHeight) + navigationBarHeight, 0, 0, 0); edgeInset.left += _parentInsets.left; edgeInset.top += _parentInsets.top; edgeInset.right += _parentInsets.right; edgeInset.bottom += _parentInsets.bottom; if ([self.parentViewController isKindOfClass:[UITabBarController class]]) edgeInset.bottom += [self tabBarHeight:UIInterfaceOrientationIsLandscape(orientation)]; if (!_ignoreKeyboardWhenAdjustingScrollViewInsets) edgeInset.bottom = MAX(edgeInset.bottom, keyboardHeight); edgeInset.bottom += safeAreaInset.bottom; UIEdgeInsets previousInset = _controllerInset; UIEdgeInsets previousCleanInset = _controllerCleanInset; UIEdgeInsets previousIndicatorInset = _controllerScrollInset; UIEdgeInsets scrollEdgeInset = edgeInset; scrollEdgeInset.left += _explicitScrollIndicatorInset.left; scrollEdgeInset.right += _explicitScrollIndicatorInset.right + safeAreaInset.right; scrollEdgeInset.top += _explicitScrollIndicatorInset.top; scrollEdgeInset.bottom += _explicitScrollIndicatorInset.bottom; UIEdgeInsets cleanInset = edgeInset; edgeInset.left += _explicitTableInset.left; edgeInset.right += _explicitTableInset.right; edgeInset.top += _explicitTableInset.top; edgeInset.bottom += _explicitTableInset.bottom; if (force || !UIEdgeInsetsEqualToEdgeInsets(previousInset, edgeInset) || !UIEdgeInsetsEqualToEdgeInsets(previousIndicatorInset, scrollEdgeInset) || !UIEdgeInsetsEqualToEdgeInsets(previousCleanInset, cleanInset)) { _controllerInset = edgeInset; _controllerCleanInset = cleanInset; _controllerScrollInset = scrollEdgeInset; _controllerSafeAreaInset = safeAreaInset; _controllerStatusBarHeight = statusBarHeight; if (notify) [self controllerInsetUpdated:previousInset]; return true; } return false; } - (void)_autoAdjustInsetsForScrollView:(UIScrollView *)scrollView previousInset:(UIEdgeInsets)previousInset { CGPoint contentOffset = scrollView.contentOffset; UIEdgeInsets finalInset = self.controllerInset; scrollView.contentInset = finalInset; scrollView.scrollIndicatorInsets = _explicitScrollIndicatorInset; if (!UIEdgeInsetsEqualToEdgeInsets(previousInset, UIEdgeInsetsZero)) { CGFloat maxOffset = scrollView.contentSize.height - (scrollView.frame.size.height - finalInset.bottom); if (![self shouldAdjustScrollViewInsetsForInversedLayout]) contentOffset.y += previousInset.top - finalInset.top; contentOffset.y = MAX(-finalInset.top, MIN(contentOffset.y, maxOffset)); [scrollView setContentOffset:contentOffset animated:false]; } else if (contentOffset.y < finalInset.top) { contentOffset.y = -finalInset.top; [scrollView setContentOffset:contentOffset animated:false]; } } - (bool)shouldAdjustScrollViewInsetsForInversedLayout { return false; } - (void)controllerInsetUpdated:(UIEdgeInsets)previousInset { if (self.isViewLoaded) { if (_automaticallyManageScrollViewInsets) { if (_scrollViewsForAutomaticInsetsAdjustment != nil) { for (UIScrollView *scrollView in _scrollViewsForAutomaticInsetsAdjustment) { [self _autoAdjustInsetsForScrollView:scrollView previousInset:previousInset]; } } else { for (UIView *view in self.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { [self _autoAdjustInsetsForScrollView:(UIScrollView *)view previousInset:previousInset]; break; } } } } } } - (BOOL)prefersStatusBarHidden { return false; } - (void)setNavigationBarHidden:(bool)navigationBarHidden animated:(BOOL)animated { [self setNavigationBarHidden:navigationBarHidden withAnimation:animated ? TGViewControllerNavigationBarAnimationSlide : TGViewControllerNavigationBarAnimationNone]; } - (void)setNavigationBarHidden:(bool)navigationBarHidden withAnimation:(TGViewControllerNavigationBarAnimation)animation { [self setNavigationBarHidden:navigationBarHidden withAnimation:animation duration:0.3f]; } - (void)setNavigationBarHidden:(bool)navigationBarHidden withAnimation:(TGViewControllerNavigationBarAnimation)animation duration:(NSTimeInterval)duration { if (navigationBarHidden != self.navigationController.navigationBarHidden || navigationBarHidden != self.navigationBarShouldBeHidden) { self.navigationBarShouldBeHidden = navigationBarHidden; if (animation == TGViewControllerNavigationBarAnimationFade) { if (navigationBarHidden != self.navigationController.navigationBarHidden) { if (!navigationBarHidden) { [self.navigationController setNavigationBarHidden:false animated:false]; self.navigationController.navigationBar.alpha = 0.0f; } [UIView animateWithDuration:duration animations:^ { self.navigationController.navigationBar.alpha = navigationBarHidden ? 0.0f : 1.0f; } completion:^(BOOL finished) { if (finished) { if (navigationBarHidden) { self.navigationController.navigationBar.alpha = 1.0f; [self.navigationController setNavigationBarHidden:true animated:false]; } } }]; } } else if (animation == TGViewControllerNavigationBarAnimationSlideFar) { if (navigationBarHidden != self.navigationController.navigationBarHidden) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat barHeight = [self navigationBarHeightForInterfaceOrientation:self.interfaceOrientation]; CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; if ([self shouldIgnoreStatusBarInOrientation:self.interfaceOrientation]) statusBarHeight = 0.0f; CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:self.interfaceOrientation]; #pragma clang diagnostic pop if (!navigationBarHidden) { [self.navigationController setNavigationBarHidden:false animated:false]; self.navigationController.navigationBar.frame = CGRectMake(0, -barHeight, screenSize.width, barHeight); } [UIView animateWithDuration:duration delay:0 options:0 animations:^ { if (navigationBarHidden) { self.navigationController.navigationBar.alpha = 0.0f; self.navigationController.navigationBar.frame = CGRectMake(0, -barHeight, screenSize.width, barHeight); } else { self.navigationController.navigationBar.frame = CGRectMake(0, statusBarHeight, screenSize.width, barHeight); } } completion:^(BOOL finished) { if (navigationBarHidden) self.navigationController.navigationBar.alpha = 1.0f; if (finished) { if (navigationBarHidden) [self.navigationController setNavigationBarHidden:true animated:false]; } }]; } } else { [self.navigationController setNavigationBarHidden:navigationBarHidden animated:animation == TGViewControllerNavigationBarAnimationSlide]; } [UIView animateWithDuration:UINavigationControllerHideShowBarDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { [self _updateControllerInset:false]; } completion:nil]; } } - (CGFloat)statusBarBackgroundAlpha { return _viewControllerStatusBarBackgroundView.alpha; } - (UIView *)statusBarBackgroundView { return _viewControllerStatusBarBackgroundView; } - (void)setStatusBarBackgroundAlpha:(float)alpha { _viewControllerStatusBarBackgroundView.alpha = alpha; } - (void)setTargetNavigationItem:(UINavigationItem *)targetNavigationItem titleController:(UIViewController *)titleController { bool updated = _targetNavigationItem != targetNavigationItem || _targetNavigationTitleController != titleController; _targetNavigationItem = targetNavigationItem; _targetNavigationTitleController = titleController; _hatTargetNavigationItem = true; if (targetNavigationItem != nil && updated) { [[self _currentNavigationItem] setLeftBarButtonItem:_leftBarButtonItem animated:false]; [self _setRightBarButtonItems:_rightBarButtonItems animated:false]; [[self _currentNavigationItem] setRightBarButtonItems:_rightBarButtonItems animated:false]; [[self _currentNavigationItem] setTitle:_titleText]; [[self _currentTitleController] setTitle:_titleText]; [[self _currentNavigationItem] setTitleView:_titleView]; } } - (UINavigationItem *)_currentNavigationItem { return _hatTargetNavigationItem ? _targetNavigationItem : self.navigationItem; } - (UIViewController *)_currentTitleController { return _hatTargetNavigationItem ? _targetNavigationTitleController : self; } - (void)setLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem { [self setLeftBarButtonItem:leftBarButtonItem animated:false]; } - (void)setLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem animated:(BOOL)animated { _leftBarButtonItem = leftBarButtonItem; [[self _currentNavigationItem] setLeftBarButtonItem:leftBarButtonItem animated:animated]; } - (void)setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem { [self setRightBarButtonItem:rightBarButtonItem animated:false]; } - (void)setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem animated:(BOOL)animated { NSMutableArray *items = [[NSMutableArray alloc] init]; if (rightBarButtonItem != nil) [items addObject:rightBarButtonItem]; _rightBarButtonItems = items; [self _setRightBarButtonItems:items animated:animated]; } - (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems animated:(BOOL)animated { _rightBarButtonItems = rightBarButtonItems; [self _setRightBarButtonItems:rightBarButtonItems animated:animated]; } - (void)_setRightBarButtonItems:(NSArray *)rightBarButtonItems animated:(BOOL)animated { if (rightBarButtonItems.count < 2) { UIBarButtonItem *rightBarButtonItem = rightBarButtonItems.firstObject; if (iosMajorVersion() >= 11 && !TGIsPad() && rightBarButtonItem.customView != nil) { UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; spacer.width = 8.0f; [[self _currentNavigationItem] setRightBarButtonItem:nil animated:false]; [[self _currentNavigationItem] setRightBarButtonItems:@[spacer, rightBarButtonItem] animated:animated]; } else { if (iosMajorVersion() >= 11 && !TGIsPad()) [[self _currentNavigationItem] setRightBarButtonItems:nil animated:false]; [[self _currentNavigationItem] setRightBarButtonItem:rightBarButtonItem animated:animated]; } } else { [[self _currentNavigationItem] setRightBarButtonItem:nil animated:false]; [[self _currentNavigationItem] setRightBarButtonItems:rightBarButtonItems animated:false]; } } - (void)setTitleText:(NSString *)titleText { _titleText = titleText; [[self _currentNavigationItem] setTitle:titleText]; [[self _currentTitleController] setTitle:titleText]; } - (void)setTitleView:(UIView *)titleView { _titleView = titleView; [[self _currentNavigationItem] setTitleView:titleView]; } - (BOOL)disablesAutomaticKeyboardDismissal { return false; } - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)())completion { if (TGIsPad() && iosMajorVersion() >= 7) viewControllerToPresent.preferredContentSize = [self.navigationController preferredContentSize]; if (iosMajorVersion() >= 8 && self.presentedViewController != nil && [self.presentedViewController isKindOfClass:[UIAlertController class]]) { dispatch_async(dispatch_get_main_queue(), ^ { [self presentViewController:viewControllerToPresent animated:flag completion:completion]; }); } else [super presentViewController:viewControllerToPresent animated:flag completion:completion]; } - (void)updateSizeClass { [self _updateControllerInset:true]; } - (void)layoutControllerForSize:(CGSize)__unused size duration:(NSTimeInterval)__unused duration { } - (NSArray> *)previewActionItems { if (self.externalPreviewActionItems != nil) return self.externalPreviewActionItems(); return [super previewActionItems]; } - (BOOL)shouldAutomaticallyForwardAppearanceMethods { return !self.customAppearanceMethodsForwarding; } - (void)removeFromParentViewController { if (_customRemoveFromParentViewController) { _customRemoveFromParentViewController(); } [super removeFromParentViewController]; } - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)())completion { if (_customDismissSelf) { _customDismissSelf(); if (completion) { completion(); } } else { [super dismissViewControllerAnimated:flag completion:completion]; } } @end @interface UINavigationController (DelegateAutomaticDismissKeyboard) @end @implementation UINavigationController (DelegateAutomaticDismissKeyboard) - (BOOL)disablesAutomaticKeyboardDismissal { return [self.topViewController disablesAutomaticKeyboardDismissal]; } @end