I have an activity view that I have added in AppDelegate class to tap bar:
[self.mainTabBar.view addSubview: spinner];
When there are connection problems it is visible in each view controller and is spinning.
There is some button at certain view controller, makes to present some modal view controller.
That modal view controller overlaps the spinner. How to make that spinner always be on top of all views or at least on top of that modal view controller?
I tried to make such a thing in view controller that presents modal view controller:
[self presentModalViewController:selectionViewController animated:YES];
[self.view bringSubviewToFront:[self.tabBarController.view viewWithTag:15]];
Not works.
Add the view to the main window.
UIWindow* mainWindow = [[UIApplication sharedApplication] keyWindow];
[mainWindow addSubview: spinner];
While phix23's answer is correct, here is a more complete example:
//The view you want to present
UIViewController *viewControllerYouWantToPresentOnTop = [[UIViewController alloc] initWithNibName:nil bundle:nil];
//Create transparent host view for presenting the above view
UIWindow* mainWindow = [[UIApplication sharedApplication] keyWindow];
UIViewController *viewControllerForPresentation = [[UIViewController alloc] init];
[[viewControllerForPresentation view] setBackgroundColor:[UIColor clearColor]];
[[viewControllerForPresentation view] setOpaque:FALSE];
[mainWindow addSubview:[viewControllerForPresentation view]];
//Make your transparent view controller present your actual view controller
[viewControllerForPresentation presentViewController:viewControllerYouWantToPresentOnTop animated:TRUE];
Remember to clean up after yourself when you don't need these any longer.
This code can be used from anywhere in your app, even a library :)
An app normally displays its content within a single window throughout its life.
But there are situations where an extra window may be used to add content on top of everything else. Apple ensures UIAlertView always stays on top by adding it in a separate window.
UIView *contentView = [[UIView alloc] initWithFrame:contentFrame];
contentView.backgroundColor = [UIColor greenColor];
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(x,y,contentFrame.size.width, contentFrame.size.height)];
window.windowLevel = UIWindowLevelAlert;
[window addSubview:contentView];
[window makeKeyAndVisible];
Show and hide your window by setting window.hidden = Yes or No as needed.
This will always show your contentView on top of everything else in the app.
The modal controller is in a completely different layer, you cannot make any subview of the presenting controller to overlap it.
Use a UIAlertView with a spinner inside. The alerts are displayed in a layer which overlaps even modal controllers.
Place the view to the keyWindow, as suggested above. You might also need to set Presentation style of the modal view as Current Context, otherwise, it can still pop on top
I'm using a Navigation Controller. My RootViewController pushes a number of views but it also presents a modal view. That modal view presents a tableView. So what I'm trying to do is figure out of I can push the Navigation Controller across the modal view, then use it from that modal view to push the view with the tableView? or barring that, is there a way to implement a second Navigation Controller from the modal view?
The real issue is that I want to present the view with the tableView using a right to left transition, which of course is not available with modal views.
I have this code that SORT OF provides the right to left transition used by the Navigation Controller:
NewViewController *newViewController = [[NewViewController alloc] init];
[self presentModalViewController:newViewController animated:NO];
CGSize theSize = CGSizeMake(320, 460);
newViewController.view.frame = CGRectMake(0 + theSize.width, 0 + 20, theSize.width, theSize.height);
[UIView beginAnimations:#"animationID" context:NULL];
[UIView setAnimationDuration:0.5];
newViewController.view.frame = CGRectMake(0, 0 + 20, 320, 460);
[UIView commitAnimations];
[newViewController release];
The problem is that the OldViewController (the one calling the NewViewController) disappears immediately so the NewViewController transitions across a blank screen, instead of covering the OldViewController.
Presenting a UIViewController modally creates a whole new navigation stack. You could do something like this:
//Create the view you want to present modally
UIViewController *modalView = [[UIViewController alloc] init];
//Create a new navigation stack and use it as the new RootViewController for it
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:modalView];
//Present the new navigation stack modally
[self presentModalViewController:nav animated:YES];
//Release
[modalView release];
[nav release];
This can be done because UINavigationController is a subclass of UIViewController. When the new view is loaded, you can use it however you wish (push another view on top of it, us UIView animations like Waqas suggeste, etc').
I hope I got your question correctly. Do tell if I didn't.
As you guess am still a newbie, getting my head around iphone development.
I am just trying out basic view loading on demand, which i cant get to work
I have an app, with 2 view controllers, each view controller connected a different nib file.
I am trying to switch between view manually; there is no navigation control involved.
How can i manually push the second view to the first view?
self.navigationController pushViewController wont work since there is no navigation controller.
How else can I push the second view on top of the first view and destroy the first view; and ofcourse vice versa?
I have done this in the first view's button action:
SecondView *sv=[[SecondView alloc] initWithNibName:#"SecondView" bundle:nil];
[self.navigationController pushViewController:sv animated:YES];
obviously, it didn't work.
window addSubView didn't work either, because the first view controller is the root view controller (not sure i said that right). In other words, when i run the app, the first view is what I see with a button that is supposed to load the second view.
I have spent hours searching for a simple example, and I couldn't find any.
Any suggestions?
in the first view controller you need this:
- (IBAction)pushWithoutViewController:(id)selector {
NextNavigationController *page = [[NextNavigationController alloc] initWithNibName:NextNavigationController bundle:nil];
CGRect theFrame = page.view.frame;
theFrame.origin = CGPointMake(self.view.frame.size.width, 0);
page.view.frame = theFrame;
theFrame.origin = CGPointMake(0,0);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.8f];
page.view.frame = theFrame;
[UIView commitAnimations];
[self.view addSubview:page.view];
[page release];
}
and then link it to the button in nib. :)
try :
SecondView *sv=[[SecondView alloc] initWithNibName:#"SecondView" bundle:nil];
[self presentModalViewController:sv animated:YES];
IF you have first xib and you want to give navigation to another controller then you have to declare navigation in to appdelegate.m
write following code to app
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = navController;
then in ViewController.m
- (IBAction)NextButtonClicked:(id)sender
{
yourNextViewController *objyourNextViewController = [[yourNextViewController alloc] initWithNibName:#"yourNextViewController" bundle:nil];
[self.navigationController pushViewController:objStartUpViewController animated:TRUE];
}
I am running an "iPhone-only" app in the iPad simulator...When the orientation of the device is changed to landscape mode, I have a view controller that kicks in and programmatically loads a WebView. This works swimmingly in the iPhone (no gap on top of landscape view), but when simulating in the iPad, there's a 20px (I think?) gap at the top of the view.
Here's the code in the landscape view controller's viewDidLoad where I load the WebView:
[super viewDidLoad];
// Initialize webview and add as a subview to LandscapeController's view
CGRect webFrame = [[UIScreen mainScreen] bounds]; // Use bounds to take up entire screen
self.myWebView = [[[UIWebView alloc] initWithFrame:webFrame] autorelease];
self.myWebView.scalesPageToFit = YES;
self.myWebView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.myWebView.delegate = self;
[self.view addSubview: self.myWebView];
// remove status bar from top of screen
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
Setting the status bar to default, has no influence.
I can't seem to figure out why this would be fine on the iPhone, but emerge on the iPad???
Any ideas? Thanks in advance!
The screen bounds are in screen coordinates. You're adding the webview as a subview of self.view; its frame is in self.view coordinates. You want to fill your view, not the screen (your view is automatically resized by UIViewController/the rest of UIKit, which should end up resizing the web view to to auto-resizing):
self.myWebView = [[[UIWebView alloc] initWithFrame:self.view.bounds] autorelease];
It's not safe to change the status bar in -viewDidLoad. View-loading can happen anywhere (it happens when anything calls viewController.view). I'm also not sure why you're setting the style; you want to set hidden-ness:
In -viewWillAppear:, do [application setStatusBarHidden:YES animated:animated];
In -viewWillDisappear:, do [application setStatusBarHidden:NO animated:animated];
Finally, you might be seeing different behaviour because the iPad is running OS 3.2.x and the phone is running 3.1.x or 4.x. Additionally, the iPhone-compatibility mode uses a dummy status bar; the "real" status bar always stays at the edges of the screen.
20 pixels at the top is almost always invariably related to the height of the status bar.
The code you posted looks fine... for setting up the UIWebView. But you're adding it as a subview to your view controller's view. How is that sized? What is its frame?
I finally figured this out. The key missing component, which I didn't mention in the original post, is that the view controller that manages landscape is actually implemented as a modal view. (See the View Controller User Guide for code on how to do this) In concept, I have a Portrait view controller. (which is the primary controller) In the Portrait view controller's viewDidLoad I apply for a Notifier that is triggered off of a change in the orientation like so:
- (void)viewDidLoad {
[super viewDidLoad];
// SECTION to setup automatic alternate landscape view on rotation
// Uses a delegate to bring the landscape view controller up as a modal view controller
isShowingLandscapeView = NO;
// Create Landscape Controller programmatically
self.landscapeViewController = [[LandscapeViewController alloc] initWithNibName:#"LandscapeViewController" bundle:[NSBundle mainBundle]];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
// END SECTION landscape modal view controller
Then, when orientation changes this method is called:
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
// Load Landscape view
landscapeViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:self.landscapeViewController animated:YES];
isShowingLandscapeView = YES;
}
At the same time I was removing the status bar from the Landscape view controller's viewWillAppear method:
- (void)viewWillAppear:(BOOL)animated
{
// remove status bar from top of screen
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:animated];
self.myWebView.delegate = self; // setup the delegate as the web view is shown
}
and this is where the problem is introduced. The Portrait view controller captures the screen dimensions, before transitioning to landscape as a Modal View. Then, viewWillAppear, in the Landscape view controller, removes the status bar.
So, the solution is to move the
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:animated];
statement to the orientationChanged method in the Portrait view controller, BEFORE transitioning to the Landscape Modal View.
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
// remove status bar from top of screen
// NOTE: this must be declared BEFORE presenting the Modal View!!!! If it's not, the landscape view will
// contain an ugly white bar in place of the missing status bar at the top of the view.
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// Load Landscape view
landscapeViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:self.landscapeViewController animated:YES];
isShowingLandscapeView = YES;
}
Note, that as tc helpfully mentioned above, if you want the status bar to appear when orienting back to Portrait, then you need
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:animated];
in the viewWillDisappear method in the Landscape view controller.
I'm trying to create a transparent modal View on top of my navigation controller. Does anyone know if this is possible?
A modal view will cover the view it is pushed on top of as well as the navigation bar for your navigation controller. However, if you use the -presentModalViewController:animated: approach, then once the animation finishes the view just covered will actually disappear, which makes any transparency of your modal view pointless. (You can verify this by implementing the -viewWillDisappear: and -viewDidDisappear: methods in your root view controller).
You can add the modal view directly to the view hierarchy like so:
UIView *modalView =
[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
modalView.opaque = NO;
modalView.backgroundColor =
[[UIColor blackColor] colorWithAlphaComponent:0.5f];
UILabel *label = [[[UILabel alloc] init] autorelease];
label.text = #"Modal View";
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
[label sizeToFit];
[label setCenter:CGPointMake(modalView.frame.size.width / 2,
modalView.frame.size.height / 2)];
[modalView addSubview:label];
[self.view addSubview:modalView];
Adding the modalView as a subview to the root view like this will not actually cover the navigation bar, but it will cover the entire view below it. I tried playing around with the origin of the frame used to init the modalView, but negative values cause it to not display. The best method that I found to cover the entire screen besides the status bar is to add the modalView as a subview of the window itself:
TransparentModalViewAppDelegate *delegate = (TransparentModalViewAppDelegate *)[UIApplication sharedApplication].delegate;
[delegate.window addSubview:modalView];
The easiest way is to use modalPresentationStyle property of navigationController (but you'll have to make animation by yourself):
self.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:modalViewController animated:NO];
modalViewController.view.alpha = 0;
[UIView animateWithDuration:0.5 animations:^{
modalViewController.view.alpha = 1;
}];
I accomplish this most easily by setting up an "OverlayViewController" that sits above all other subviews of my window or root view. Set this up in your app delegate or root view controller, and make OverlayViewController a singleton so that it can be accessed from anywhere in your code or view controller hierarchy. You can then call methods to show modal views, show activity indicators, etc, whenever you need to, and they can potentially cover any tab bars or navigation controllers.
Sample code for root view controller:
- (void)viewDidLoad {
OverlayViewController *o = [OverlayViewController sharedOverlayViewController];
[self.view addSubview:o.view];
}
Sample code you might use to display your modal view:
[[OverlayViewController sharedOverlayViewController] presentModalViewController:myModalViewController animated:YES];
I haven't actually used -presentModalViewController:animated: with my OverlayViewController but I expect this would work just fine.
See also: What does your Objective-C singleton look like?
I had this same problem and in order to The solution is to add the modal view with addSubview: and animate the change in the view hierarchy with UIView’s animateWithDuration:delay:options:animations:completion:
I added a property and 2 methods to a subclass of UIViewController (FRRViewController) that includes other functionalities. I will be publishing the whole stuff on gitHub soon, but until then you can see the relevant code below. For more info, you can check my blog: How to display a transparent modal view controller.
#pragma mark - Transparent Modal View
-(void) presentTransparentModalViewController: (UIViewController *) aViewController
animated: (BOOL) isAnimated
withAlpha: (CGFloat) anAlpha{
self.transparentModalViewController = aViewController;
UIView *view = aViewController.view;
view.opaque = NO;
view.alpha = anAlpha;
[view.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIView *each = obj;
each.opaque = NO;
each.alpha = anAlpha;
}];
if (isAnimated) {
//Animated
CGRect mainrect = [[UIScreen mainScreen] bounds];
CGRect newRect = CGRectMake(0, mainrect.size.height, mainrect.size.width, mainrect.size.height);
[self.view addSubview:view];
view.frame = newRect;
[UIView animateWithDuration:0.8
animations:^{
view.frame = mainrect;
} completion:^(BOOL finished) {
//nop
}];
}else{
view.frame = [[UIScreen mainScreen] bounds];
[self.view addSubview:view];
}
}
-(void) dismissTransparentModalViewControllerAnimated:(BOOL) animated{
if (animated) {
CGRect mainrect = [[UIScreen mainScreen] bounds];
CGRect newRect = CGRectMake(0, mainrect.size.height, mainrect.size.width, mainrect.size.height);
[UIView animateWithDuration:0.8
animations:^{
self.transparentModalViewController.view.frame = newRect;
} completion:^(BOOL finished) {
[self.transparentModalViewController.view removeFromSuperview];
self.transparentModalViewController = nil;
}];
}
}
Here's what I did to solve the problem - Google the details but this approach worked very well for me:
Take a screenshot of the underlying view. https://devforums.apple.com/message/266836 - this leads to a ready-made method that returns a UIView for the current screen.
Hand the screenshot to the modal view (I used a property)
Present the modal view
In the modal view controller's viewDidAppear, set the image as UIImageView at index 0. Adjust the vertical position of the image by the height of the status bar.
In the modal view controller's viewWillDisappear, remove the image again
The effect is:
The view animates in as any modal view does - the semi transparent parts of the modal view glide over the existing view
As soon as the animation stops, the background is set to the screenshot - this makes it appear as if the old view is still underneath even though it isn't.
As soon as the modal view's disappear animation starts, the image is removed. The OS meanwhile shows the old navigation view so the modal view transparently glides away and out of sight as you'd expect.
I tried animating in my own overlay view but it didn't work very well. I got a crash with no indication as to what has crashed. Rather than chase this down I did the bg view & Works really well.
Code in the modal view - I think you can figure out the rest, namely setting the property modalView.bgImage...
- (void)viewDidAppear:(BOOL)animated {
// background
// Get status bar frame dimensions
CGRect statusBarRect = [[UIApplication sharedApplication] statusBarFrame];
UIImageView *imageView = [[UIImageView alloc] initWithImage:self.bgImage];
imageView.tag = 5;
imageView.center = CGPointMake(imageView.center.x, imageView.center.y - statusBarRect.size.height);
[self.view insertSubview:imageView atIndex:0];
}
- (void)viewWillDisappear:(BOOL)animated {
[[self.view viewWithTag:5] removeFromSuperview];
}
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:newview animated:YES];
and make sure you setup the modal view background to be transparent,
self.view.background = .... alpha:0.x;
if you set modalPresentationStyle for the modal view controller to:
viewController.modalPresentationStyle = 17;
The view in the background is not removed. (TWTweetComposeViewController use it).
I did not try to pass App Store review with this code though
This post about displaying a semi-transparent "Loading..." view might give a few pointers on how to proceed.
Yeah, you have to add the view manually, and if you want to slide in from the bottom or whatever you have to do the animation yourself too.
I wrote a class to do this, and a semi-modal datepicker using that class as an example.
You can find documentation in this blog post, the code is on github
I've been researching this same issue for the past week. I tried all the various answers and examples found in Google and here on StackOverflow. None of them worked that well.
Being new to iOS programming, I wasn't aware of something called UIActionSheet. So if you're trying to accomplish this in order to show a modal overlay of buttons (such as a modal asking someone how they want to share something), just use UIActionSheet.
Here is a webpage that shows an example of how to do this.
I got this idea from https://gist.github.com/1279713
Prepare:
In the modal view xib (or scene using storyboard), I setup the full-screen background UIImageView (hook it with the .h file and give it a property "backgroundImageView") with 0.3 alpha. And I set the view (UIView) background color as plain black.
Idea:
Then in "viewDidLoad" of the modal view controller I capture the screenshot from the original status and set that image to the background UIImageView. Set the initial Y point to -480 and let it slide to Y point 0 using 0.4-second duration with EaseInOut animation option. When we dismiss the view controller, just do the reverse thing.
Code for the Modal View Controller Class
.h file:
#property (weak, nonatomic) IBOutlet UIImageView *backgroundImageView;
- (void) backgroundInitialize;
- (void) backgroundAnimateIn;
- (void) backgroundAnimateOut;
.m file:
- (void) backgroundInitialize{
UIGraphicsBeginImageContextWithOptions(((UIViewController *)delegate).view.window.frame.size, YES, 0.0);
[((UIViewController *)delegate).view.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
backgroundImageView.image=screenshot;
}
- (void) backgroundAnimateIn{
CGRect backgroundImageViewRect = backgroundImageView.frame;
CGRect backgroundImageViewRectTemp = backgroundImageViewRect;
backgroundImageViewRectTemp.origin.y=-480;
backgroundImageView.frame=backgroundImageViewRectTemp;
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationCurveEaseInOut animations:^{
backgroundImageView.frame=backgroundImageViewRect;
} completion:^(BOOL finished) {
}];
}
- (void) backgroundAnimateOut{
CGRect backgroundImageViewRect = backgroundImageView.frame;
backgroundImageViewRect.origin.y-=480;
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationCurveEaseInOut animations:^{
backgroundImageView.frame=backgroundImageViewRect;
} completion:^(BOOL finished) {
}];
}
In viewDidLoad, simply call:
[self backgroundInitialize];
[self backgroundAnimateIn];
In anywhere we dismiss the modal view controller, we call:
[self backgroundAnimateOut];
Please note that this will ALWAYS animate the background image. So if this modal view controller transition style (or the segue transition style) is not set to "Cover Vertical", you may not need to call the animation methods.
I finally accomplished this, for a navigation or tab bar interface, by combining an overlay view controller (see: pix0r's answer) that's hidden / un-hidden before hiding or showing a view controller based on this very good blog post.
Concerning the view controller, the tip is to make its background view the clearColor, then the semi-transparent overlay view is visible and whatever views are added as subviews in the view controller are in front and most importantly opaque.
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.
For iOS 8+ you can use UIModalPresentationOverCurrentContext presentation style for presented view controller to easy achieve desired behavior.
UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.9f];
viewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:viewController animated:YES completion:nil];
If you also need to support iOS 7 - check this thread.
You can achieve transparent/semi-transparent modal view effect by overlaying a transparent/semi-transparent button on both the view and the navigation bar.
You can access the navigation bar through the navigationBar property of the UINavigationController.
I found that UIButton unlike UILabel will trap mouse events - hence giving the correct modal behavior.
I just found a workaround for that. Just create a 1X1 of UIViewController and add it to your parent view controller. And show the transparent modal view controller in that UIViewController.
on viewDidLoad;
self.dummyViewController = [[UIViewController alloc] init];
[self.dummyViewController.view setFrame:CGRectMake(0, 0, 1, 1)];
[self.view addSubView:self.dummyViewController.view];
when you need to open a transparentViewController;
[self.dummyViewController presentModalViewController:yourTransparentModalViewController animated:true];
If you need a screen like the attached one, the below code may help you.
The code:
MyViewController * myViewController = [[MyViewController alloc] initWithNibName:nibName bundle:nil];
UINavigationController * myNavigationController = [[UINavigationController alloc] initWithRootViewController: myViewController];
myNavigationController.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentModalViewController: myNavigationController animated:YES];
If say you want a screen overlay, use the parentViewController.view, it will place above navigation bar ++
MyCustomViewController* myOverlayView = [[MyCustomViewController alloc] init];
[self.parentViewController.view addSubview:myOverlayView];
This worked for me:
UIViewController *modalViewController = [[UIViewController alloc] init];
modalViewController.view.backgroundColor = [UIColor whiteColor] colorWithAlpha:0.5];
[self showDetailViewController:modalViewController sender:nil];