I'm trying to modally display a UIWebview on top of the currently displayed view controller. The code to popup the webview lives in a static library and has no idea about the view controller hierarchy of the app that it lives in. The code below works for my test cases, but not sure if it would work for all possible view controller hierarchies.
UIViewController *rootViewController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
UIViewController *presentedVC = [rootViewController presentedViewController];
if (presentedVC) {
// We have a modally displayed viewcontroller on top of rootViewController.
if (!presentedVC.isBeingPresented) {
[presentedVC presentViewController:vc animated:YES completion:^{
//
}];
} else {
rootViewController presentViewController:vc animated:YES completion:^{
//
}];
}
Does the presentedViewController always give the currently visible view controller?
If what you want to do is to display the UIWebView modally on top of whatever View Controller is displayed in your app, you don't need the "topmost" one. Getting the Root View Controller will suffice. I do this all the time whenever I want to present a View on top of everything else.
You only need a reference to the root view controller in your library:
self.rootVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
Having this reference you have two options now:
The first one is to use UIViewController's method presentViewController:animated:completion: to display another View Controller which will contain your UIWebView
The second option is to fake the modal view controller by adding a subview that covers the entire screen, has a (semi)transparent background, and contains the view you want to display "modally". Here is an example:
#interface FakeModalView : UIView // the *.h file
#property (nonatomic, retain) UIWebView *webView;
#property (nonatomic, retain) UIView *background; // this will cover the entire screen
-(void)show; // this will show the fake modal view
-(void)close; // this will close the fake modal view
#end
#interface FakeModalView () // the *.m file
#property (nonatomic, retain) UIViewController *rootVC;
#end
#implementation FakeViewController
#synthesize webView = _webView;
#synthesize background = _background;
#synthesize rootVC = _rootVC;
-(id)init {
self = [super init];
if (self) {
[self setBackgroundColor: [UIColor clearColor]];
_rootVC = self.rootVC = [[[[[UIApplication sharedApplication] delegate] window] rootViewController] retain];
self.frame = _rootVC.view.bounds; // to make this view the same size as the application
_background = [[UIView alloc] initWithFrame:self.bounds];
[_background setBackgroundColor:[UIColor blackColor]];
[_background setOpaque:NO];
[_background setAlpha:0.7]; // make the background semi-transparent
_webView = [[UIWebView alloc] initWithFrame:CGRectMake(THE_POSITION_YOU_WANT_IT_IN)];
[self addSubview:_background]; // add the background
[self addSubview:_webView]; // add the web view on top of it
}
return self;
}
-(void)dealloc { // remember to release everything
[_webView release];
[_background release];
[_rootVC release];
[super dealloc];
}
-(void)show {
[self.rootVC.view addSubview:self]; // show the fake modal view
}
-(void)close {
[self removeFromSuperview]; // hide the fake modal view
}
#end
If you have any other questions just let me know.
Hope this helps!
Related
I have the following view controllers stack.
First, my app will show an app tour page. (Say TourViewController - super class is UIViewController). Added this controller in AppDelegate as rootviewcontroller.
self.window.rootViewController = tourViewController;
Then from the tour page, if the user taps on "Signin" button, I'm presenting the second view controller (Say LoginViewController - super class is UIViewController).
UINavigationController *loginNavigationController = [[UINavigationController alloc] initWithRootViewController:self.loginViewController];
[self presentViewController:loginNavigationController animated:YES completion:nil];
After a successful login, I need to resign the second view controller (LoginViewController) and want to show a tab bar based view for further needs.
I tried this code inside the login success method.
[self dismissViewControllerAnimated:YES completion:^{
TabBarViewController *tabController = [[TabBarViewController alloc] init];
[self presentViewController:tabController animated:NO completion:nil];
AppDelegate *applicationDelegate = [[UIApplication sharedApplication] delegate];
applicationDelegate.window.rootViewController = tabController;
}];
Problems:
When I'm in the LoginViewController, I have two view controllers in my stack. So even I resign the LoginViewController, the another one (TourViewController) remains in the screen.
If I tried the above code, tab bat controller was successfully added as root view controller. But, when the LoginViewController resigns, the background was filled by TourViewController
What I need is, When I resign the LoginViewController, the background view should be tab bar controller instead of TourViewController.
Help needed!!
u can change the root view controller in AppDelegate not in the success method of the loginNavigationController better u can do this way
in AppDelegate.h
#import <UIKit/UIKit.h>
#import "TabControllerViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
- (void)showTabController; //add this method call from on success method of log in completion
#end
in AppDelegate.m
- (void)showTabController;
{
TabControllerViewController *tabController = [[TabControllerViewController alloc] initWithNibName:#"TabControllerViewController" bundle:nil];
self.window.rootViewController = tabController;
[self.window makeKeyAndVisible];
}
and in loginNavigationController.m
[self dismissViewControllerAnimated:YES completion:^{
//TabBarViewController *tabController = [[TabBarViewController alloc] init];
// [self presentViewController:tabController animated:NO completion:nil]; //no nee to present
AppDelegate *applicationDelegate = [[UIApplication sharedApplication] delegate];
[applicationDelegate showTabController]; //there is no need to create a tab bar in loginview controller, create it in root view controller
//applicationDelegate.window.rootViewController = tabController;
}];
NOTE: above is not tested just try it once
Edit 1
one way u can do it but with different animation,
form this answer u can change to second window by doing some animation for example
in in AppDelegate.h
#import <UIKit/UIKit.h>
#import "TabViewController.h"
#import "LoginViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window; //holds initial window, holds tour and login controller
#property (strong, nonatomic) UIWindow *tabWindow; //holds only tab controller
//..other code below is my test
#property (strong, nonatomic) TabViewController *tabViewController;
#property (strong, nonatomic) LoginViewController *loginController;
- (void)showTabController;
#end
in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_tabWindow = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
_window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
// Override point for customization after application launch.
_loginController = [[LoginViewController alloc]initWithNibName:#"LoginViewController" bundle:nil];
_tabViewController = [[TabViewController alloc] initWithNibName:#"TabViewController" bundle:nil];
self.window.rootViewController = _loginController; //for test for your case it contains tour view controller
[self.window makeKeyAndVisible];
return YES;
}
- (void)showTabController;
{
[UIView transitionWithView:self.window duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
self.window.rootViewController = _tabViewController;
} completion:^(BOOL finished) {
// [_tabWindow makeKeyAndVisible];
}];
}
Do one thing,
Create UINavigationController in AppDelegate.h so you can access it anywhere.
Logic
Whenever you need to change navigation controller you must have to put your Controller to Navigation stack.
So first of all you have to create ViewController/ Tabbarcontroller object and assign it to navigationController and then show the navigationController.
AppDelegate* myDelegate = (((AppDelegate*) [UIApplication sharedApplication].delegate));
InitialViewController *initialVC = [self.storyboard instantiateViewControllerWithIdentifier:#“InitialVC"];
myDelegate.navController = [[UINavigationController alloc] initWithRootViewController:initialVC];
myDelegate.window.rootViewController = myDelegate.navController;
[myDelegate.window makeKeyAndVisible];
I am running into a weird problem. I am using a UISegmentedControl to switch between two views. When the UISegmentedViewController comes on screen, the default view(view1) is loaded.View1 looks exactly as it should, but when I click the UISegmentedControl to change views, view2 does not look properly and is cut off. When I switch back to view1 now it also exhibits the same behavior. I am attaching the code I am using for this UISegmentedViewController below - I found it in a SO post. I will attach pictures for clarity:
Edit: Also, the last tableview cell in the Courses section cannot be properly scrolled to. Why is the view getting cut off?
Code:
//
// PCFSegmentedRateViewController.m
// Purdue Course Sniper
//
// Created by Kamran Pirwani on 2/5/13.
// Copyright (c) 2013 Kamran Pirwani. All rights reserved.
//
#import "PCFSegmentedRateViewController.h"
#interface PCFSegmentedRateViewController ()
// Array of view controllers to switch between
#property (nonatomic, copy) NSArray *allViewControllers;
// Currently selected view controller
#property (nonatomic, strong) UIViewController *currentViewController;
#end
#implementation PCFSegmentedRateViewController
#synthesize classRating,professorRating,segmentedControl;
- (void)viewDidLoad
{
[super viewDidLoad];
[segmentedControl addTarget:self action:#selector(indexDidChangeForSegmentedControl:) forControlEvents:UIControlEventValueChanged];
classRating = [[self storyboard] instantiateViewControllerWithIdentifier:#"ClassRate"];
professorRating = [[self storyboard] instantiateViewControllerWithIdentifier:#"Professor"];
// Add A and B view controllers to the array
self.allViewControllers = [[NSArray alloc] initWithObjects:classRating, professorRating, nil];
[self cycleFromViewController:self.currentViewController toViewController:[self.allViewControllers objectAtIndex:self.segmentedControl.selectedSegmentIndex]];
}
#pragma mark - View controller switching and saving
- (void)cycleFromViewController:(UIViewController*)oldVC toViewController:(UIViewController*)newVC {
// Do nothing if we are attempting to swap to the same view controller
if (newVC == oldVC) return;
// Check the newVC is non-nil otherwise expect a crash: NSInvalidArgumentException
if (newVC) {
// Set the new view controller frame (in this case to be the size of the available screen bounds)
// Calulate any other frame animations here (e.g. for the oldVC)
newVC.view.frame = CGRectMake(CGRectGetMinX(self.view.bounds), CGRectGetMinY(self.view.bounds), CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
// Check the oldVC is non-nil otherwise expect a crash: NSInvalidArgumentException
if (oldVC) {
// Start both the view controller transitions
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
// Swap the view controllers
// No frame animations in this code but these would go in the animations block
[self transitionFromViewController:oldVC
toViewController:newVC
duration:0.25
options:UIViewAnimationOptionLayoutSubviews
animations:^{}
completion:^(BOOL finished) {
// Finish both the view controller transitions
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
// Store a reference to the current controller
self.currentViewController = newVC;
}];
} else {
// Otherwise we are adding a view controller for the first time
// Start the view controller transition
[self addChildViewController:newVC];
// Add the new view controller view to the ciew hierarchy
[self.view addSubview:newVC.view];
// End the view controller transition
[newVC didMoveToParentViewController:self];
// Store a reference to the current controller
self.currentViewController = newVC;
}
}
}
- (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl *)sender {
NSUInteger index = sender.selectedSegmentIndex;
if (UISegmentedControlNoSegment != index) {
UIViewController *incomingViewController = [self.allViewControllers objectAtIndex:index];
[self cycleFromViewController:self.currentViewController toViewController:incomingViewController];
}
}
#end
When the user pressed an 'add' button a modal view pops up for them to enter information. I have a 'cancel' button in the top left of a navigation bar and I want it to dismiss the current view controller when it is pressed. How do I set an object as the class's delegate? I understand creating protocols and implementing its methods but I cannot seem to make the delegate be set. When running the debugger my [self delegate] in the 'add' view controller is always nil.
Are you spawning the modal viewController through a segue set up in your Storyboard? If so, then in the prepareForSegue: method you would set the delegate there.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([identifier isEqualToString:#"userGuideSegue_home"]){
UserGuideViewController* vc = segue.destinationViewController;
[[segue destinationViewController] setDelegate:self];
}
}
On the other hand, if you are setting up the modal viewController entirely through code, then you would create an instance of the modal viewController then set it's delegate.
- (void)showModelView:(NSString*)viewName
{
// code ripped out of project so a bit specific
if ([viewName isEqualToString:#"userGuide_name"]) {
modalViewController = (UserGuideViewController * )
[[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:NULL]
instantiateViewControllerWithIdentifier:#"UserGuide"];
}
modalViewController.delegate = self;
modalViewController.modalPresentationStyle = UIModalPresentationFormSheet;
modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
modalViewController.view.frame = [[UIScreen mainScreen] applicationFrame];;
[self presentViewController:modalViewController
animated:YES
completion:^{
//put your code here
}];
}
Of course all this assumes you have defined a delegate property on your modal viewController.
If you created the view in IB, Control-drag the button into the ViewController's header file and add an IBOutlet. Inside of that method in the .m file you can
[self dismissModalViewControllerAnimated:YES];
alternatively you can create the button programmatically:
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(processCancel:)];
-(void)processCancel:(UIBarButtonItem *)item{
[self dismissModalViewControllerAnimated:YES];
}
#interface MyViewController : UIViewController {
id delegate;
}
#property (nonatomic,retain) id delegate;
#synthesize delegate;
This should do the job, you can now use [MyViewController setDelegate:self] before showing the modal view and call [[self delegate] dismissModalViewControllerAnimated:YES] in the cancel button tapped event in MyViewController
I have a navigation based application.On click of a button on the navigation bar in the first screen , I am able to push another view controller as follows :
-(void) buttonClicked:(id)sender
{
UIViewController* mv = [[SecondViewController alloc] init];
[[self navigationController] pushViewController:mv animated:YES];
}
Now i have a UIView(separate .h and .m files) as part of the first screen. On click of a button in the UIView, i want to push the SecondViewController.
I have tried the following :
UIViewController* mv = [[SecondViewController alloc] init];
UIViewController * home=[[FirstViewController alloc]init];
[[home navigationController] pushViewController:mv animated:YES];
It doesnt work!! Kindly help
UIViewController* mv = [[SecondViewController alloc] init];
UIViewController * home=[[FirstViewController alloc]init];
[[home navigationController] pushViewController:mv animated:YES];
The problem here is that home isn't part of the navigation stack, so [home navigationController] is surely nil. I'm not quite clear on what you're trying to do here, but just creating a view controller doesn't mean that it's actually part of the view controller graph.
Why would it work? Randomly creating view controllers whose view is not even visible, is not the solution. You can either keep a reference to the VC in the view like this:
#imlementation ViewController
- (id) init
{
// ...
aView = [[CustomView alloc] init];
aView.viewController = self;
// ...
}
#end
#interface CustomView
#property (assign) ViewController *viewController;
#end
Or you can search the responder chain at runtime:
UIResponder *next = [view nextResponder];
while (next)
{
if ([next isKindOfClass:[ViewController class]])
{
break;
}
next = [next nextResponder];
}
And now "next" will contain the view controller (or nil if it can't be found).
Try using the same navigationController to push view, this keeps the same stack of ViewControllers.
UIViewController* mv = [[SecondViewController alloc] init];
[[self navigationController] pushViewController:mv animated:YES];
[mv release];
I see your problem now! You need to #import your FirstViewController, then #class it. Then do your push.
So:
//.h
#import "FirstViewContoller.h"
#class FirstViewController;
#interface...
//.m
-(void)return {
FirstViewController *firstview = [[FirstViewController alloc]init(withnibname:)];
[firstView.navigationController pushViewController: firstView.navigationController.topViewController animated: TRUE];
}
If I am not wrong, your UIView though is in separate files, is still added to the screen from a UIViewController class.
Simply, post a notification from UIView to your FirstViewController class where you have access to the navigation controller. Then push the SecondViewController from there.
You Can use this. It Works very well for me:-
Firstly Create Object of AppDelegate in UIView Class and initialize it. Then create Navigationcontroller object in Appdelegate.h :-
#property(strong,nonatomic) UINavigationController *navControl;
In your UIView Class implement this code where you want to push :-
ViewController *objview = [[ViewController alloc]init]; [appDelegate.navControl pushViewController:objview animated:YES];
How can I create a UIPopoverController with integrated UINavigationController so I will be able to slide views inside the UIPopoverController left-right (with navigation bar).
UPDATE:
I open popup like this
- (void)showSettingsViewAtSenderForIPad:(id)sender
{
if (!settingsPopoverController_)
{
SettingsPopoverController *settings = [[SettingsPopoverController alloc] init];
settings.valuesGeneratorOptions = valuesGeneratorOptions_; // setting variables
self.settingsPopoverController_ = [[[UIPopoverController alloc] initWithContentViewController:settings] autorelease];
[settingsPopoverController_ setDelegate:self];
[settingsPopoverController_ setPopoverContentSize:CGSizeMake(320, 480)];
[settings release];
}
if (!infoPopoverController_.popoverVisible)
{
[settingsPopoverController_ presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
}
I created a controller which has a NSTableViewController as a root controller in UINavigationController
#interface SettingsPopoverController : UIViewController
{
ValuesGeneratorOptions *valuesGeneratorOptions;
IBOutlet SettingsViewController *settingsViewController;
IBOutlet UINavigationController *navigationController;
}
...
#implementation SettingsPopoverController
...
- (void)viewDidLoad
{
self.settingsViewController.valuesGeneratorOptions = self.valuesGeneratorOptions;
[self.view addSubview:self.navigationController.view];
[super viewDidLoad];
}
...
end
The problem is, that the table is not scrollable inside the popup. It also ignores the table style (initWithStyle not called).
Fix?
SOLUTION:
Found the solution: popOver table view
You create a new nib and a UIViewController. This nib has, as it's top level view, a plain jane UIView and a UINavigationController. The UINavigationController's top UIViewController is whatever view controller you want to display first.
You then display this nib inside your popover controller. In the view did load, you do something like this:
-(void)viewDidLoad
{
[self.view addSubview:self.navigationController.view];
}
This adds your navigation controller's view to your view in your nib, which allows it to be displayed.