Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Presenting semi-transparent viewcontroller that works both in iOS7 and iOS8

Tags:

ios

ios7

ios8

I had the following code that worked perfectly in iOS7.

[UIView animateWithDuration:0.5 animations:^(void) {
    self.view.alpha = 0.5;
    [self.navigationController.navigationBar setAlpha:0.3];

}]; //to make the background view controller semi-transparent
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootViewController setModalPresentationStyle:UIModalPresentationCurrentContext];
OverlayViewController *ctlr = [storyBoard instantiateViewControllerWithIdentifier:@"OverlayViewController"];
//present the OverlayViewController
[self presentViewController:ctlr animated:YES completion:nil];

Then I had the following in viewWillAppear of the background view controller, to revert back its view to full opaque.

[UIView animateWithDuration:1.0 animations:^(void) {
    self.view.alpha = 1.0;
    [self.navigationController.view setAlpha:1.0];
}];

Now, with iOS8, the above code doesn't set the background to be semi-transparent. Black color surrounds the OverlayViewController.

I found online that using UIModalPresentationOverCurrentContext will give the expected behavior. It does actually, but the background view controller is never taken off the view hierarchy (edited to add reference to this behavior: https://developer.apple.com/documentation/uikit/uimodalpresentationstyle). So, the viewWillAppear is never called, and so the semi-transparency is never removed.

Obviously, I can resort to hacks like using NSNotificationCenter and fire off a notification when the OverlayViewController is removed, but it feels like a roundabout way to do what should be simple. Is there any other way in which I can gracefully achieve this?

Corollary questions:

1) If UIModalPresentationOverCurrentContext is the only way to achieve this, then I am wondering if I would be forced to put two versions of the code to get it working both in iOS7 and iOS8.

2) Obviously that new enum is not recognized in earlier versions of Xcode. So, should my team upgrade to Xcode 6 just to ensure they can run this code, even if the rest of their work is centered around iOS7 only? Or is there any way of telling the older version of Xcode to ignore the particular code block that is needed only for iOS8?

like image 397
Shankar Avatar asked Sep 25 '14 06:09

Shankar


3 Answers

You have to handle two configurations for iOS 7 and iOS8. In both case you need to make sure that the background view controller is not taken off the view hierarchy. This can be accomplish with:

  1. On iOS7 by setting the modalPresentationStyle flag to UIModalPresentationCurrentContext for the presenting view controller. You need to identify who is the presenting view controller: it is not necessarily self.navigationController if you have multiple container view controllers.

  2. On iOS8 by setting the modalPresentationStyle flag to UIModalPresentationOverCurrentContext for the presented view controller. If you are using storyboard make sure the presentation style is set to Default for the storyboard segue and within the prepareForSegue method set the presentation style to the destination view controller to UIModalPresentationOverCurrentContext.

Now to answer your corollary questions:

1) You need to have code to handle both situation on iOS7 and iOS8:

Define some macros to check the version number the application is running on. For example the following will do:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

2) Obviously if you have code that needs to be compiled on iOS7 and iOS8 so your team needs to update to the latest version of XCode" XCode 6.1 as of now.

like image 158
tiguero Avatar answered Nov 15 '22 19:11

tiguero


In Swift for iOS 8.x:

setting UIModalPresentationStyle.OverCurrentContext to the viewController being presented does the job.

    func presentTransparentController(){
        var viewController = self.storyboard?.instantiateViewControllerWithIdentifier("ViewControllerNamed") as! ViewController

        // set to .OverCurrentContext
        viewController.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext

        // presents the view controller as usual
        self.presentViewController(viewController, animated: true, completion: nil)
    }
like image 40
Marco Leong Avatar answered Nov 15 '22 18:11

Marco Leong


Based on @Tiguero's answer, I created a small category class to solve the problem.

@implementation UIViewController (Extensions)

- (void) presentTransparentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
    if(SYSTEM_VERSION_LESS_THAN(@"8.0")) {
        self.parentViewController.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
    }else{
        viewControllerToPresent.modalPresentationStyle = UIModalPresentationOverCurrentContext;
    }

    [self presentViewController:viewControllerToPresent animated:YES completion:completion];
}

@end

The iOS7 version required me to hardcode the controller that does the presenting.

like image 45
David van Dugteren Avatar answered Nov 15 '22 18:11

David van Dugteren