Explorar o código

Change approach to status bar and rotation handling.

Rather than trying to mimic system behavior with status bars and rotation, we can do better by trying to get out of the way entirely. This resolves the UIAlertView/UIAlertController related infinite recursion crashes that started in 8.3. Unfortunately, this approach requires using private API.
Ryan Olson %!s(int64=11) %!d(string=hai) anos
pai
achega
c38b90ee60

+ 15 - 65
Classes/ExplorerToolbar/FLEXExplorerViewController.m

@@ -120,76 +120,26 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 }
 
 
-#pragma mark - Status Bar Wrangling for iOS 7
-
-// Try to get the preferred status bar properties from the app's root view controller (not us).
-// In general, our window shouldn't be the key window when this view controller is asked about the status bar.
-// However, we guard against infinite recursion and provide a reasonable default for status bar behavior in case our window is the keyWindow.
-
-- (UIViewController *)viewControllerForStatusBarAndOrientationProperties
-{
-    UIViewController *viewControllerToAsk = [[[UIApplication sharedApplication] keyWindow] rootViewController];
-    
-    // On iPhone, modal view controllers get asked
-    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
-        while (viewControllerToAsk.presentedViewController) {
-            viewControllerToAsk = viewControllerToAsk.presentedViewController;
-        }
-    }
-    
-    return viewControllerToAsk;
-}
-
-- (UIStatusBarStyle)preferredStatusBarStyle
-{
-    UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
-    UIStatusBarStyle preferredStyle = UIStatusBarStyleDefault;
-    if (viewControllerToAsk && viewControllerToAsk != self) {
-        // We might need to foward to a child
-        UIViewController *childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarStyle];
-        while (childViewControllerToAsk && childViewControllerToAsk != viewControllerToAsk) {
-            viewControllerToAsk = childViewControllerToAsk;
-            childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarStyle];
-        }
-        
-        preferredStyle = [viewControllerToAsk preferredStatusBarStyle];
-    }
-    return preferredStyle;
-}
-
-- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
-{
-    UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
-    UIStatusBarAnimation preferredAnimation = UIStatusBarAnimationFade;
-    if (viewControllerToAsk && viewControllerToAsk != self) {
-        preferredAnimation = [viewControllerToAsk preferredStatusBarUpdateAnimation];
-    }
-    return preferredAnimation;
-}
+#pragma mark - Rotation
 
-- (BOOL)prefersStatusBarHidden
+- (UIViewController *)viewControllerForRotationAndOrientation
 {
-    UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
-    BOOL prefersHidden = NO;
-    if (viewControllerToAsk && viewControllerToAsk != self) {
-        // Again, we might need to forward to a child
-        UIViewController *childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarHidden];
-        while (childViewControllerToAsk && childViewControllerToAsk != viewControllerToAsk) {
-            viewControllerToAsk = childViewControllerToAsk;
-            childViewControllerToAsk = [viewControllerToAsk childViewControllerForStatusBarHidden];
-        }
-        
-        prefersHidden = [viewControllerToAsk prefersStatusBarHidden];
+    UIWindow *window = self.previousKeyWindow ?: [[UIApplication sharedApplication] keyWindow];
+    UIViewController *viewController = window.rootViewController;
+    NSString *viewControllerSelectorString = [@[@"_vie", @"wContro", @"llerFor", @"Supported", @"Interface", @"Orientations"] componentsJoinedByString:@""];
+    SEL viewControllerSelector = NSSelectorFromString(viewControllerSelectorString);
+    if ([viewController respondsToSelector:viewControllerSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+        viewController = [viewController performSelector:viewControllerSelector];
+#pragma clang diagnostic pop
     }
-    return prefersHidden;
+    return viewController;
 }
 
-
-#pragma mark - Rotation
-
 - (NSUInteger)supportedInterfaceOrientations
 {
-    UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
+    UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
     NSUInteger supportedOrientations = [FLEXUtility infoPlistSupportedInterfaceOrientationsMask];
     if (viewControllerToAsk && viewControllerToAsk != self) {
         supportedOrientations = [viewControllerToAsk supportedInterfaceOrientations];
@@ -206,7 +156,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 
 - (BOOL)shouldAutorotate
 {
-    UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
+    UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
     BOOL shouldAutorotate = YES;
     if (viewControllerToAsk && viewControllerToAsk != self) {
         shouldAutorotate = [viewControllerToAsk shouldAutorotate];
@@ -861,7 +811,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 - (void)resignKeyAndDismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
 {
     [self.previousKeyWindow makeKeyWindow];
-    
+    [[self.previousKeyWindow rootViewController] setNeedsStatusBarAppearanceUpdate];
     self.previousKeyWindow = nil;
     
     // Restore the status bar window's normal window level.

+ 19 - 0
Classes/ExplorerToolbar/FLEXWindow.m

@@ -7,6 +7,7 @@
 //
 
 #import "FLEXWindow.h"
+#import <objc/runtime.h>
 
 @implementation FLEXWindow
 
@@ -33,4 +34,22 @@
     return pointInside;
 }
 
+- (BOOL)shouldAffectStatusBarAppearance
+{
+    return [self isKeyWindow];
+}
+
++ (void)initialize
+{
+    // This adds a method (superclass override) at runtime which gives us the status bar behavior we want.
+    // The FLEX window is intended to be an overlay that generally doesn't affect the app underneath.
+    // Most of the time, we want the app's main window(s) to be in control of status bar behavior.
+    // Done at runtime with an obfuscated selector because it is private API. But you shoudn't ship this to the App Store anyways...
+    NSString *canAffectSelectorString = [@[@"_can", @"Affect", @"Status", @"Bar", @"Appearance"] componentsJoinedByString:@""];
+    SEL canAffectSelector = NSSelectorFromString(canAffectSelectorString);
+    Method shouldAffectMethod = class_getInstanceMethod(self, @selector(shouldAffectStatusBarAppearance));
+    IMP implementation = method_getImplementation(shouldAffectMethod);
+    class_addMethod(self, canAffectSelector, implementation, method_getTypeEncoding(shouldAffectMethod));
+}
+
 @end