UIViewAnimation done by a UIViewController belonging to a UINavigationController? - uiview

I have an UINavigationController which the user navigates with.
When pushing a specific UIViewController onto the navigation stack, a "settings" button appear in the navigationBar. When the user clicks this button I would like to flip the current view/controller, i.e. everything on screen, including the navigationBar, over to a settings view.
So I have a SettingsViewController which I would like to flip to from my CurrentViewController that lives on a navigationController stack.
I get all kinds of strange behavior trying to do this, the UIViews belonging to the SettingsViewController will start to animate, sliding into place, the navigationButtons moves around, nothing acts as I would think.
-(void)settingsHandler {
SettingViewController *settingsView = [[SettingViewController alloc] init];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight
forView:self.navigationController.view
cache:YES];
[self.navigationController.view addSubview:settingsView.view];
[UIView commitAnimations];
}
The above results in the views flipping correctly, but the subviews of the SettingsViewController are all positioned in (0, 0) and after the transition, they 'snap' into place?
Is it because I instantiate and add my subviews in viewDidLoad, like this?
- (void)viewDidLoad {
UIImageView *imageBg = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
[imageBg setImage:[UIImage imageNamed:#"background.png"]];
[self.view addSubview:imageBg];
[imageBg release];
SettingsSubview *switchView = [[SettingsSubview alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
[self.view addSubview:switchView];
[switchView release];
[super viewDidLoad];
}
1: How should I correctly do the "flip" transition, from within the UIViewController in the UINavigationController, to a new UIViewController and subsequently from the new UIViewController and back to the "original" UIViewController residing on the UINavigationControllers stack?
2: Should I use a different approach, than the "viewDidLoad" method, when instantiating and adding subviews to a UIViewController?
-question 2 is more of a "best practice" thing. I have seen different ways
of doing it and I am having trouble either finding or understanding the life-cycle documentation and the different threads and posts on the subject. I am missing the "best practice" examples.
Thank You very much for any help given:)

If you want to create your view hierarchy programmatically, the place to do it is in -loadView. To do so you must create the view yourself, add all of its subviews, and then assign it to the view property, like this:
- (void)loadView {
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
UIImageView *imageBg = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
[imageBg setImage:[UIImage imageNamed:#"background.png"]];
[containerView addSubview:imageBg];
[imageBg release];
SettingsSubview *switchView = [[SettingsSubview alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
[containerView addSubview:switchView];
[switchView release];
self.view = containerView;
[containerView release];
}
It helps to understand the context in which this method gets called, and how it behaves by default. The first time the view property of a UIViewController is accessed, the default getter method calls -loadView to lazy-load the view. The default implementation of -loadView loads the view from a nib if one was specified. Otherwise it creates a plain UIView object and sets that as the controller's view. By overriding this method, you can ensure that your view's hierarchy will be fully formed the first time it is accessed.
-viewDidLoad should be used for any subsequent setup that needs to occur after the view hierarchy is fully loaded. This method will get called whether the view is loaded from a nib or constructed programmatically in loadView.

Related

UIButton target action inside custom class

I've been struggling with a bug, and I found a work-around, but I'd like to understand what is exactly going on. it has something to do with UIButton target actions misfiring depending on different subview hierarchies, inside a subclass.
Brief summary: I have a subclass of NSObject with a UIView property object, a UIButton attached to it, and a target added to the button calling a function inside the subclass. Inside the main ViewController, I init the subclass and add its view to the view stack, click the button, and it throws me to main.mm with the error - EXC_BAD_ACCESS, gives me little feedback. so the hierarchy looks like this:
-CustomClass
--UIView <-this is added as a subview to the View Controller
---UIButton (onRelease calling a function)
so I fixed it by changing the custom class to be a subclass of UIView instead of NSObject, then add its #property UIView to be a subview of the custom class (and the button is still attached to the subview), and then in the main View Controller, I add the custom class itself as a subview, not the class's subview property object. then the button successfully calls the function. so the new arrangement looks like this:
-CustomClass (now UIView) <-this is added as a subview to the View Controller
--UIView <-this is added as a subview to CustomClass
---UIButton (onRelease calling a function)
then, i realized i can just keep the CustomClass a subclass of UIView for both instances, the problem persists with the original setup if everything else is unchanged.
okay, more detail, here's code:
CustomClass:
.h
#interface Temp : UIView
#property UIView *subview;
#property UIButton *but;
#end
.m
-(id) init{
self = [super initWithFrame:[[UIScreen mainScreen] bounds]];
if(self){
_subview = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//[self addSubview:_subview]; // FOR THE FIX
_but = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_but setTitle:#"OKAY" forState:UIControlStateNormal];
[_but setBackgroundColor:[UIColor blackColor]];
[_but setFrame:CGRectMake(50, 50, 200, 200)];
[_subview addSubview:_but];
[_but addTarget:self action:#selector(pageTurn) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(void) pageTurn{
NSLog(#"WORKS");
}
inside view controller:
Temp *temp = [[Temp alloc] init];
[self.view addSubview:temp.subview];
//[self.view addSubview:temp]; // FOR THE FIX, instead of above line
Who's holding onto temp?
If temp isn't referenced by anyone then it's released. At that point the target is zombie and of course you will crash. temp.subview is being held by self.view.
In the second setup, adding temp as a subview of self.view keeps a reference to it.
You can fix this by adding a Temp * property in the view controller.
self.temp = [[Temp alloc] init];
[self.view addSubview:self.temp.subview];
You're messing with all kinds of view hierarchy stuff you don't need to, which is likely the cause of the problem. I created a Test UIView subclass that had a UIButton instance variable that I added as a subview in the Test object, there's no need to add another view as a subview and then add the button to the subview, and then in your view controller add the button's subview to the view - it's WAY more complicated than it needs to be.
In a nutshell - Create the Temp UIView, add the button as a subview, then in your view controller class add the Temp UIView as a subview. Simple as that, here is the code:
- (id)init {
self = [super initWithFrame:[[UIScreen mainScreen] bounds]];
if(self){
_but = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_but setTitle:#"OKAY" forState:UIControlStateNormal];
[_but setBackgroundColor:[UIColor blackColor]];
[_but setFrame:CGRectMake(100, 100, 200, 200)];
[_but addTarget:self action:#selector(pageTurn) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_but];
}
return self;
}
- (void)pageTurn {
NSLog(#"WORKS");
}
Then added an instance to my view controller:
Test *temp = [[Test alloc] init];
temp.backgroundColor = [UIColor redColor];
[self.view addSubview:temp];
This was the result:

How to show UIViewController in other UIViewController?

This is typical question and possibly duplicated, but..
There is iPad app which has UINavigationBar and UITabBar. I have created a button on navigationBar which must show appinfoViewController.
When I present it navigationBar and tabTab are still available to tap. But I would like to show appinfoViewController to full app's main screen size (like modal)
This is my code:
- (void)showInfo
{
AboutViewController *about = [[AboutViewController alloc] init];
[about.view setFrame: self.view.frame];
[about.view setAlpha:0.0];
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
[about.view setAlpha:1.0];
[self.view addSubview:about.view];
[self addChildViewController:about];
[about didMoveToParentViewController:self];
}
completion:^(BOOL finished){
}];
}
How to present appinfoViewController to full screen?
May be it's possible to present it modally but without BLACK background?
As suggested in the comments, using
[self presentViewController:about animated:YES completion:nil]
would get rid of the nav bar and the tab bar. If you need to keep them, you need to use
[self.navigationController pushViewController:about animated:YES];
EDIT:
In order to have the user interaction disabled everywhere except for your about view, it's slightly trickier: first off, you need to have all of your UI elements embedded in a view that is not the main view of your presenting view controller.
Let's say you have only a button (the "show about" button), you wouldn't just place it in your main view, but you would use another view (let's call it "outer view") that is just as big as the view controller's view and where you place the button (along with any other ui element you might have). You also need an outlet to this outer view. Then write a method such as:
-(void)userInteractionEnabled:(BOOL)enabled
{
self.navigationController.navigationBar.userInteractionEnabled = enabled;
self.tabBarController.tabBar.userInteractionEnabled = enabled;
self.outerView.userInteractionEnabled = enabled;
}
Alternatively you could simply disable every "interactive" outlet instead of outerView. So if, for example, you have 2 textviews, 3 buttons and one UIPickerView, you would set userInteractionEnabled = enabled for each of those outlets (instead of doing it only for the parent view).
Now, in your showInfo method you can have:
CGRect frame = CGRectMake(50, 50, 200, 200); //Use whatever origin and size you need
about.view.frame = frame;
[self.view addSubview:about.view];
[self userInteractionEnabled:NO]
And in your btnClose method you can just put:
[about.view removeFromSuperview];
[self userInteractionEnabled:YES];
I hope this helps, let me know if this is what you needed!
P.S. Maybe you're already aware of this, but there is a class UIPopoverController, only available for iPad's apps, that would pretty much do all of this for you. You can find a tutorial on how to use it here.
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
AboutViewController *about = (AboutViewController *)[storyBoard instantiateViewControllerWithIdentifier:#"about"];
[self.navigationController pushViewController:about animated:YES];
You can add viewcontroller view layer directly to presenting viewcontroller view.
Code should be look like --
AboutViewController *about = [[AboutViewController alloc] init];
[about.view setFrame: self.view.bound];
[about.view setAlpha:0.0];
[self.view addSubview:about.view];
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
[about.view setAlpha:1.0];
}
completion:^(BOOL finished){
}];

UIViewController subviews release?

UIViewController *viewController = [[UIViewController alloc] init];
UIView *view1 = [[UIView alloc] init];
[viewController.view addSubview:view1];
[view1 release];
if I want release viewController;
[viewController release];
do I need manual release view1 before release viewController?
UIView *view = (UIView *)[[viewController.view subviews] objectAtIndex:0];
[view release];
[viewController release];
should I do this? or just release viewController?
No, you dont have to do that. Just release viewController and it will release all of its subviews internally. Rest will be taken care by framework.
If you are not using ARC, your code will look like this,
UIViewController *viewController = [[UIViewController alloc] init];
UIView *view1 = [[UIView alloc] init];
[viewController.view addSubview:view1];
[view1 release];
[viewController release];
Since you have allocated both viewController and view1 once, you have to release it once as shown above. You dont have to do a release again since you are not doing any retain on this after that.
If you do this,
UIView *view = (UIView *)[[viewController.view subviews] objectAtIndex:0];
[view release];
It will mostly result in a crash when viewController is released since you are releasing it twice and viewController's subviews are also getting released internally.
Here once thing you have to note is that, addSubview retains view1 as mentioned in Apple documentation.
The view to be added. This view is retained by the receiver.
After being added, this view appears on top of any other subviews.
This will be released once viewController is released and you dont have to manually release it since you dont own it.
This is the correct way.
UIViewController *viewController = [[UIViewController alloc] init];
UIView *view1 = [[UIView alloc] init];
[viewController.view addSubview:view1];
[view1 release];
[viewController release];
When you add a view as subview it'll be retained by the viewcontroller.
addSubview:
Adds a view to the end of the receiver’s list of subviews.
- (void)addSubview:(UIView *)view
Parameters
view
The view to be added. This view is retained by the receiver. After being added, this view appears on top of any other subviews.
Discussion
This method retains view and sets its next responder to the receiver,
which is its new superview.
Views can have only one superview. If view already has a superview and
that view is not the receiver, this method removes the previous
superview before making the receiver its new superview.
Reference UIView
Important: A view controller is the sole owner of its view and any
subviews it creates. It is responsible for creating those views and
for relinquishing ownership of them at the appropriate times such as
when the view controller itself is released
Reference : UIViewController Class
UIView *view = (UIView *)[[viewController.view subviews] objectAtIndex:0];
[view release];
It'll surely crash when you call release on viewController.

How to present a UIViewController with an bottom-up animation without hiding TabBar

So, i have a UITabbarController with an UINavigationController in it. On the press of a button, i would like to bring in another UINavigationController, animating it like when using presentModalViewController:animated:, but i do not want it to hide the TabBar.
Is there anything in UIKit (3.1.3 and later) that i could use for this or will i have to do the animating myself?
Just test the code, maybe you need to set the navigationController as property if you need do sth like pushViewController:animated:.
UIViewController * aViewController = [[UIViewController alloc] init];
[aViewController.view setFrame:self.view.frame];
[aViewController.view setBackgroundColor:[UIColor redColor]];
UINavigationController * navigationController = [[UINavigationController alloc] initWithRootViewController:aViewController];
[aViewController release];
[navigationController.view setFrame:CGRectMake(0.0f, 480.0f, 320.0f, 480.0f)];
[self.navigationController.view addSubview:navigationController.view];
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^{
[navigationController.view setFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
}
completion:nil];
[navigationController release];
The only way by default to present something from the bottom up is the presentModalViewController. You can actually override the animations for your navigationController, but it isn't something you can achieve just by calling a different method, you will have to create your own, and handle the animations too.
Another way to possibly cheat this would be to reload your tabBar in the view you are presenting modally, but that could get messy.

How to use presentModalViewController to create a transparent view

I am displaying a modal view with
[self presentModalViewController:controller animated:YES];
When the view moves up the screen it is transparent as per the setting in the xib file it is created from, but once it fills the screen it goes opaque.
Is there anyway of keeping the view transparent?
I suspect that the view it is being placed over is not being rendered rather then that the modal view is becoming opaque.
After iOS 3.2 there is a method to do this without any “tricks” – see the documentation for the modalPresentationStyle property. You have a rootViewController which will present the viewController.
So here's the code to success:
viewController.view.backgroundColor = [UIColor clearColor];
rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
[rootViewController presentModalViewController:viewController animated:YES];
With this method the viewController's background will be transparent and the underlying rootViewController will be visible. Please note that this only seems to work on the iPad, see comments below.
Your view is still transparent, but once your modal controller is at the top of the stack, the view behind it is hidden (as is the case with any top-most view controller). The solution is to manually animate a view yourself; then the behind-viewController won't be hidden (since you won't have 'left' it).
What I needed to get this to work:
self.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
For those who want to see some code, here's what I added to my transparent view's controller:
// Add this view to superview, and slide it in from the bottom
- (void)presentWithSuperview:(UIView *)superview {
// Set initial location at bottom of superview
CGRect frame = self.view.frame;
frame.origin = CGPointMake(0.0, superview.bounds.size.height);
self.view.frame = frame;
[superview addSubview:self.view];
// Animate to new location
[UIView beginAnimations:#"presentWithSuperview" context:nil];
frame.origin = CGPointZero;
self.view.frame = frame;
[UIView commitAnimations];
}
// Method called when removeFromSuperviewWithAnimation's animation completes
- (void)animationDidStop:(NSString *)animationID
finished:(NSNumber *)finished
context:(void *)context {
if ([animationID isEqualToString:#"removeFromSuperviewWithAnimation"]) {
[self.view removeFromSuperview];
}
}
// Slide this view to bottom of superview, then remove from superview
- (void)removeFromSuperviewWithAnimation {
[UIView beginAnimations:#"removeFromSuperviewWithAnimation" context:nil];
// Set delegate and selector to remove from superview when animation completes
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
// Move this view to bottom of superview
CGRect frame = self.view.frame;
frame.origin = CGPointMake(0.0, self.view.superview.bounds.size.height);
self.view.frame = frame;
[UIView commitAnimations];
}
The Apple-approved way to do this in iOS 8 is to set the modalPresentationStyle to 'UIModalPresentationOverCurrentContext'.
From the UIViewController documentation:
UIModalPresentationOverCurrentContext
A presentation style where the content is displayed over only the
parent view controller’s content. The views beneath the presented
content are not removed from the view hierarchy when the presentation
finishes. So if the presented view controller does not fill the screen
with opaque content, the underlying content shows through.
When presenting a view controller in a popover, this presentation
style is supported only if the transition style is
UIModalTransitionStyleCoverVertical. Attempting to use a different
transition style triggers an exception. However, you may use other
transition styles (except the partial curl transition) if the parent
view controller is not in a popover.
Available in iOS 8.0 and later.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/
The 'View Controller Advancements in iOS 8' video from WWDC 2014 goes into this in some detail.
Be sure to give your presented view controller a clear background color (otherwise, it will still appear opaque).
There is another option: before showing the modal controller, capture a screenshot of the whole window. Insert the captured image into an UIImageView and add the image view to the controller's view you're about to show.
Then send to back.
Insert another view above the image view (background black, alpha 0.7).
Show your modal controller and it looks like it was transparent.
Just tried it on iPhone 4 running iOS 4.3.1. Like charm.
this is quite old, but i solved the same problem as follows:
Since i need to present a navigation controller in iPhone, adding a subview wasn't a viable solution.
So what i did:
1) Before presenting the view controller, take a screenshot of your current screen:
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
2) Create the view controller you want to present, and add the background as a subview, sending it to back.
UIViewController * presentingVC = [UIViewController new];
UIImageView * backgroundImageOfPreviousScreen = [[UIImageView alloc] initWithImage:backgroundImage];
[presentingVC.view addSubview:backgroundImageOfPreviousScreen];
[presentingVC.view sendSubviewToBack:backgroundImageOfPreviousScreen];
3) Present your view controller, but before that in the new view controller, add a transparent view in the viewDidLoad (i used ILTranslucentView)
-(void)viewDidLoad
{
[super viewDidLoad];
ILTranslucentView * translucentView = [[ILTranslucentView alloc] initWithFrame:self.view.frame];
[self.view addSubview:translucentView];
[self.view sendSubviewToBack:translucentView];
}
And that's all!
I wrote down my findings about this in a different question, but the gist of it is that you have to call modalPresentationStyle = UIModalPresentationCurrentContext on whatever owns the full screen at the moment. Most of the time, it's whatever is the [UIApplication sharedApplication].delegate.window's rootViewController. It could also be a new UIViewController that was presented with modalPresentationStyle = UIModalPresentationFullScreen.
Please read my other much more detailed post if you're wondering how I specifically solved this problem. Good luck!
This appears to be broken in IOS 8, I am using a navigation controller and the context that is being displayed is the Navigation menus context which in our case is a sliding Menu controller.
We are using pod 'TWTSideMenuViewController', '0.3' have not checked to see if this is an issue with the library yet or the method described above.
This worked to me in iOS 8-9:
1- Set your view controller's background with an alpha
2- add this code:
TranslucentViewController *tvc = [[TranslucentViewController alloc] init];
self.providesPresentationContextTransitionStyle = YES;
self.definesPresentationContext = YES;
[tvc setModalPresentationStyle:UIModalPresentationOverCurrentContext];
[self.navigationController presentViewController:tvc animated:YES completion:nil];
I know this is pretty old question. I was stuck on this issue and I was able to get a lead from this thread. So putting here how I got it worked :) .
I am using storyboard and I have segue to the ViewController which is to be presented. The view controller have a transparent background colour. Now in the Attributes inspector of the segue I set the presentation to "Over current context".And it worked for me. I am developing for iPhone.
I've created open soruce library MZFormSheetController to present modal form sheet on additional UIWindow. You can use it to present transparency modal view controller, even adjust the size of the presented view controller.
In my condition i am having view on same viewController. So make a new view controller for holding UIView. Make that view transparent by setting it's alpha property.
Then on a button click i wrote this code. It looks good.
UIGraphicsBeginImageContext(objAppDelegate.window.frame.size);
[objAppDelegate.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIViewController *controllerForBlackTransparentView=[[[UIViewController alloc] init] autorelease];
[controllerForBlackTransparentView setView:viewForProfanity];
UIImageView *imageForBackgroundView=[[UIImageView alloc] initWithFrame:CGRectMake(0, -20, 320, 480)];
[imageForBackgroundView setImage:viewImage];
[viewForProfanity insertSubview:imageForBackgroundView atIndex:0];
[self.navigationController presentModalViewController:controllerForBlackTransparentView animated:YES];
And it shows what i want. hope it help some one.
Here's a category I've created that will solve the problem.
//
// UIViewController+Alerts.h
//
#import <UIKit/UIKit.h>
#interface UIViewController (Alerts)
- (void)presentAlertViewController:(UIViewController *)alertViewController animated:(BOOL)animated;
- (void)dismissAlertViewControllerAnimated:(BOOL)animated;
#end
//
// UIViewController+Alerts.m
//
#import "UIViewController+Alerts.h"
#implementation UIViewController (Alerts)
- (void)presentAlertViewController:(UIViewController *)alertViewController animated:(BOOL)animated
{
// Setup frame of alert view we're about to display to just off the bottom of the view
[alertViewController.view setFrame:CGRectMake(0, self.view.frame.size.height, alertViewController.view.frame.size.width, alertViewController.view.frame.size.height)];
// Tag this view so we can find it again later to dismiss
alertViewController.view.tag = 253;
// Add new view to our view stack
[self.view addSubview:alertViewController.view];
// animate into position
[UIView animateWithDuration:(animated ? 0.5 : 0.0) animations:^{
[alertViewController.view setFrame:CGRectMake(0, (self.view.frame.size.height - alertViewController.view.frame.size.height) / 2, alertViewController.view.frame.size.width, alertViewController.view.frame.size.height)];
}];
}
- (void)dismissAlertViewControllerAnimated:(BOOL)animated
{
UIView *alertView = nil;
// find our tagged view
for (UIView *tempView in self.view.subviews)
{
if (tempView.tag == 253)
{
alertView = tempView;
break;
}
}
if (alertView)
{
// clear tag
alertView.tag = 0;
// animate out of position
[UIView animateWithDuration:(animated ? 0.5 : 0.0) animations:^{
[alertView setFrame:CGRectMake(0, self.view.frame.size.height, alertView.frame.size.width, alertView.frame.size.height)];
}];
}
}
#end
After a lot of research looks like this will solve our issue and serve our purpose.
create a segue from source VC to destination VC with an identifier.
for example "goToDestinationViewController"
okay to makes lives easy let's consider the current view controller i.e, the one you want behind your transparent view as source and the destination as destination
Now in source VC in viewDidLoad: or view
performSegueWithIdentifier("goToDestinationViewController", sender: nil)
good we are half way through.
Now go to your storyboard. Click on the segue. which should look like this:
segue
change the options to what are shown.
Now comes the real solution.
in your destination view controller's viewDidLoad add this code.
self.modalPresentationStyle = .Custom
.........................................................................THAT EASY..................................................................
Alternate way is to use a "container view". Just make alpha below 1 and embed with seque.
XCode 5, target iOS7.
can't show image, not enough reputation)))
Container view available from iOS6.
This code works fine on iPhone under iOS6 and iOS7:
presentedVC.view.backgroundColor = YOUR_COLOR; // can be with 'alpha'
presentingVC.modalPresentationStyle = UIModalPresentationCurrentContext;
[presentingVC presentViewController:presentedVC animated:YES completion:NULL];
But along this way you loose 'slide-from-the-bottom' animation.
I found this elegant and simple solution for iOS 7 and above!
For iOS 8 Apple added UIModalPresentationOverCurrentContext, but it does not work for iOS 7 and prior, so I could not use it for my case.
Please, create the category and put the following code.
.h file
typedef void(^DismissBlock)(void);
#interface UIViewController (Ext)
- (DismissBlock)presentController:(UIViewController *)controller
withBackgroundColor:(UIColor *)color
andAlpha:(CGFloat)alpha
presentCompletion:(void(^)(void))presentCompletion;
#end
.m file
#import "UIViewController+Ext.h"
#implementation UIViewController (Ext)
- (DismissBlock)presentController:(UIViewController *)controller
withBackgroundColor:(UIColor *)color
andAlpha:(CGFloat)alpha
presentCompletion:(void(^)(void))presentCompletion
{
controller.modalPresentationStyle = UIModalPresentationCustom;
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
__block UIView *overlay = [[UIView alloc] initWithFrame:keyWindow.bounds];
if (color == nil) {
color = [UIColor blackColor];
}
overlay.backgroundColor = color;
overlay.alpha = alpha;
if (self.navigationController != nil) {
[self.navigationController.view addSubview:overlay];
}
else if (self.tabBarController != nil) {
[self.tabBarController.view addSubview:overlay];
}
else {
[self.view addSubview:overlay];
}
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:controller
animated:true
completion:presentCompletion];
DismissBlock dismissBlock = ^(void) {
[self dismissViewControllerAnimated:YES completion:nil];
[UIView animateWithDuration:0.25
animations:^{
overlay.alpha = 0;
} completion:^(BOOL finished) {
[overlay removeFromSuperview];
}];
};
return dismissBlock;
}
#end
Note: it works also for navigationContoller, tabBarController.
Example of usage:
// Please, insure that your controller has clear background
ViewController *controller = [ViewController instance];
__block DismissBlock dismissBlock = [self presentController:controller
withBackgroundColor:[UIColor blackColor]
andAlpha:0.5
presentCompletion:nil];
// Supposed to be your controller's closing callback
controller.dismissed = ^(void) {
dismissBlock();
};
Enjoy it! and please, leave some feedbacks.
This is the best and cleanest way I found so far:
#protocol EditLoginDelegate <NSObject>
- (void)dissmissEditLogin;
#end
- (IBAction)showtTransparentView:(id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"foo bar"
delegate:self
cancelButtonTitle:#"cancel"
destructiveButtonTitle:#"destructive"
otherButtonTitles:#"ok", nil];
[actionSheet showInView:self.view];
}
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet{
UIStoryboard *loginStoryboard = [UIStoryboard storyboardWithName:#"Login" bundle:nil];
self.editLoginViewController = [loginStoryboard instantiateViewControllerWithIdentifier:#"EditLoginViewController"];
self.editLoginViewController.delegate = self;
[self.editLoginViewController viewWillAppear:NO];
[actionSheet addSubview:self.editLoginViewController.view];
[self.editLoginViewController viewDidAppear:NO];
}
The best solution I have come across is to use the addChildViewController method.
Here is an excellent example : Add a child view controller's view to a subview of the parent view controller
I try to use multiple methods to solve but still failed, the follow code implemented finally.
The resolution by Swift:
// A.swift init method
modalPresentationStyle = .currentContext // or overCurrentContent
modalTransitionStyle = .crossDissolve // dissolve means overlay
then in B view controller:
// B.swift
let a = A()
self.present(a, animated: true, completion: nil)

Resources