I'm attempting to load a Modal View Controller (1st) from a Modal View Controller (2nd). While it sounds complicated, it probably isn't.
The 1st controller is actually a UIWebView which is initialized in the loadView method of the .m file:
- (void)loadView {
// Initialize webview and add as a subview to LandscapeController's view
myWebView = [[[UIWebView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
//CGRect forceframe = CGRectMake(0, 0, 480, 320);
//myWebView = [[[UIWebView alloc] initWithFrame:forceframe] autorelease];
myWebView.scalesPageToFit = YES;
myWebView.autoresizesSubviews = YES;
myWebView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
myWebView.delegate = self;
self.view = myWebView;
}
Then in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// Load HTML file as an NSURL request
[self.myWebView loadHTMLString:updated_html baseURL:nil];
// Invoke the covering modal view on condition
if (some_condition) {
landscapeCoverController = [[UIViewController alloc] initWithNibName:#"LandscapeCoverController" bundle:[NSBundle mainBundle]];
[self presentModalViewController:landscapeCoverController animated:YES];
[landscapeCoverController release];
}
The intended 2nd Modal View Controller (landscapeCoverController) is initialized with a NIB that I set up in IB.
My intended objective, is to conditionally cover up the UIWebView with the "LandscapeCoverController" view, which will have some buttons and interactivity which will result in the 2nd Modal View being dismissed.
Why isn't my landscapeCoverController loading? Any thoughts greatly appreciated!
Also...the 1st Modal View controller (LandscapeViewController) .h looks like:
#class LandscapeCoverController;
#interface LandscapeViewController : UIViewController <UIWebViewDelegate> {
UIWebView *myWebView;
LandscapeViewController *landscapeCoverController;
}
#property (nonatomic, retain) UIWebView *myWebView;
#property (nonatomic, retain) LandscapeViewController *landscapeCoverController; // Modal view controller
and...the 2nd Modal View controller (landscapeCoverController) viewDidLoad does nothing:
// NIB initialized in LandscapeViewController.m viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
}
as I think the
landscapeCoverController = [[UIViewController alloc] initWithNibName:#"LandscapeCoverController" bundle:[NSBundle mainBundle]];
[self presentModalViewController:landscapeCoverController animated:YES];
[landscapeCoverController release];
statement should handle initialization and loading of the controller...
You are declaring landscapeCoverController as an instance of LandscapeViewController, and allocating it as a UIViewController. This is most likely your problem (probably the first one, as you aren't calling any methods specific to LandscapeViewController). Also, since landscapeCoverController is an instance variable, you don't really need to release it after presentModalViewController. Try to pick more dissimilar class names. It will save you from confusion like this in the future.
Related
I have page-based app. On each page I have 3 uibuttons at the top, uiscrollview with alphabet (uibuttons to sort data in uitable) at the right and uitableview at the center. How to show cell's detail view? If it is necessary to add uinavigationcontroller I can't do this. If I adds it, it disables interaction with my table, buttons and scrollview.
And another question is how to show new data in tableview and scrollview when goes to next page??
I have rootViewController class and DataViewController class.
rootViewController listing:
#interface RootViewController ()
#property (readonly, strong, nonatomic) ModelController *modelController;
#end
#implementation RootViewController
#synthesize pageViewController = _pageViewController;
#synthesize modelController = _modelController;
#synthesize navContr = _navContr;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Configure the page view controller and add it as a child view controller.
//[self presentModalViewController:navContr animated:YES];
self.pageViewController = [[[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil] autorelease];
self.pageViewController.delegate = self;
DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = [NSArray arrayWithObject:startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
self.navContr = [[UINavigationController alloc] initWithRootViewController:self.pageViewController];
[self.view addSubview:self.navContr.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
for (UIGestureRecognizer *recognizer in self.pageViewController.gestureRecognizers){
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]){
[recognizer setEnabled:NO];
}
}
}
After few manipulations it works but I need help to get it work fine!
So now it looks like this
Next question: how do I remove the brown space at the top???
::UPDATE::
Problem solved. It needs just to set y-axis position of UINavigationController to -20 ;)
i'm not sure if this link about creating navigation-based project may help you.. (http://iosmadesimple.blogspot.com/2012/08/navigation-based-project-doing-it.html)
From that tutorial, there's a class called SampleViewController, subclass of UIViewController. You might want to put a tableView in SampleViewController.xib file. Then in your SampleViewController.h file, add an IBOutlet UITableView* yourTable property and synthesize it. Connect it to your tableView in your .xib file. //Or you may do it programmatically
in your SampleViewController.h, make your interface header look like this.. I think you already know this...
#interface SampleViewController:UIViewController < UITableviewDelegate, UITableViewDatasource >
in your SampleViewcontroller.m, under viewDidLoad method, set the table delegate and datasource to self:
yourTableView.delegate = self;
yourTableView.datasource = self;
Afterwhich, you implement tableView delegate and datasource methods... //you already know those since you were already able to show a tableview ;)
one of these methods is the "tableview:didSelectAtIndexpath:" --> this is the part wherein you can put your code when you click one of the cells.
Let's assume you have the DetailsViewController Class, this is the class you would like to show after clicking a cell and show its details.
DetailsViewController Class must have a variable that will accept the data you would like to show. Let's say, an NSString *detailsMessage; //do the #property and #synthesize thing...
Let's go back to SampleViewController.m file, under tableview:didSelectAtIndexpath: Method:
inside that Method.. put these codes.
DetailsViewController *detailsVC = [[DetailsViewController alloc] init];
detailsVC.detailsMessage = #"The Data you want to pass.";
[self.navigationController pushViewController:detailsVC animated:YES];
I hope this helps. :(
There may be other ways but by far the easiest way is to use a navigation controller. In fact, it is built to do exactly this.
If you don't want the navigationBar then you can hide it in the viewWillAppear function.
[self.navigationController setNavigationBarHidden:YES animated:YES];
Then you can add an other UIViewController to push when the user selects a cell.
Having read your OP again I'm not sure how you are adding your navigationController.
To use a navigationController you create it and load it at start time. You then create your current viewController (the one with the buttons and table etc...) and set this as the rootViewController of the navigationController.
Then you display the navigationController.
Could you explain how you are adding your navigationController as it may help understand what is going wrong.
Thanks
::EDIT::
OK, my assumption was correct.
The way you are using the navigation controller is not how it was intended.
OK, so at the moment your AppDelegate file will have a method Application didFinishLaunching...
It will look something like this...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[OJFViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
You should change it so that it is like this...
first add a property to your appDelegate...
#property (nonatomic, strong) UINavigationController *navigationController;
Then change the didFinishLaunchingMethod to this...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[OJFViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
This will still show you MainViewController but it will now be contained within a navigationController.
Next in your MainViewController function viewWillAppearAnimated add the line...
[self.navigationController setNavigationBarHidden:YES animated:animated];
This will then hide the navigationBar at the top of the view so you still have access to your buttons.
The you need a new ViewController and xib file (for example DetailViewController).
When the user selects a table row you need to then do something like...
DetailViewController *detailView = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
//pass in details of which row was selected.
[self.navigationController pushViewController:detailView animated:YES];
This will then display your new view and new viewController. You will also need to write a way of passing the data in (set up a property on the DetailViewController).
Hope this helps.
I'm going mad using navigation controllers on the iPhone.
I have an app, with a main xib (the one with the window) inside wich I have put a NavigationController, inside wich I have a viewController. Everything is connected and the ViewController is defined with the correct inherited class name.
In the didFinishLaunchingWithOptions, i have :
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
In the .h I have :
#interface MainAppDelegate : NSObject <UIApplicationDelegate> {
IBOutlet UIWindow *window;
IBOutlet UINavigationController* navigationController;
}
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) UINavigationController* navigationController;
#end
Then in the First ViewController I have a button connected to this method :
- (IBAction) definePreferences:(id)sender {
PreferencesController *nextWindow = [[[PreferencesController alloc] initWithNibName:#"Preferences" bundle:nil] autorelease];
UINavigationController* navController = [[[UINavigationController alloc] initWithRootViewController:nextWindow] autorelease];
[self.navigationController presentModalViewController:navController animated:YES];
}
all items in the main xib seems to be connected... and retained by the properties. The AppDelegate with its window and navigationController... the Window rootviewcontroller with the same navigationController... and the file owner with the app delegate...
Everything runs fine, but the preferences window never appears...
Can you see why ?
If needed, I must say that this first view controller makes the camera interface appear and put an overlay over it. The button is onto this overlay. The imagePicker is show like this in viewDidAppear :
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
[self presentModalViewController:picker animated:YES];
[picker release];
EDIT :
In viewDidAppear, self.navigationController is ok at the start and end of method.
In definePreferences, self.navigationController is nil. Nothing is called beetween those two calls.
Nothing
EDIT :
The problem may come from the way I init the viewController on which the button is.
Here is the method called from the firstView called by the Navigation Controller.
- (void) viewDidAppear:(BOOL)animated {
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
// Set the image picker source:
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.showsCameraControls = NO;
picker.navigationBarHidden = YES;
picker.wantsFullScreenLayout = YES;
// Insert the overlay
OverlayViewController* overlayController = [[OverlayViewController alloc] initWithNibName:#"Overlay" bundle:nil];
picker.cameraOverlayView = overlayController.view;
// Show the picker:
[self presentModalViewController:picker animated:NO];
[picker release];
[super viewDidAppear:YES];
}
But... how should I do ?
First, never call an IBAction setPreferences:. That violates KVC, and can eventually cause all kinds of bizarre behaviors. setX: is a reserved name for the setter of the property named x.
You should not be creating a nav controller in this method (i.e. navController). You should be using the one you created in the NIB (self.navigationController). Check if that is nil. If it is, then you either didn't set up a navigation controller in the NIB, or you didn't wire it to this view controller.
You should also verify that nextWindow is non-nil.
I've finaly solved the problem.
See https://stackoverflow.com/questions/5317968/iphone-camera-overlay-going-down-after-a-modal-view-transition
I have some hair less... :-)
I am trying to present a image selector to my user while they are on a view presented as modal view.
I am building the view in a table view app. Right now the code runs but does not open the ImagePicker.
In DetailTableViewController.m
#implementation DetailTableViewController
There is an (+) button in the nav and when it is clicked I call
- (void)add
{
DetailTableViewController *controller = [[DetailTableViewController alloc]
initWithStyle:UITableViewStyleGrouped];
UINavigationController *newNavController = [[UINavigationController alloc]
initWithRootViewController:controller];
newNavController.delegate = self;
[[self navigationController] presentModalViewController:newNavController
animated:YES];
DetailTableViewController *controller2 = [[DetailTableViewController alloc]
initWithStyle:UITableViewStyleGrouped];
imgPicker = [[UIImagePickerController alloc]
initWithRootViewController:controller2];
imgPicker.allowsEditing = YES;
imgPicker.delegate = self;
//______________HERE I CREATE THE INTERFACE FOR THIS VIEW IN CODE__________________
NSLog(#"Load add View");
}
One of the button on this page is a select image button which is here:
- (IBAction)grabImage {
[newNavController pushViewController:imgPicker animated:YES];
NSLog(#"grabImage");
}
This does not present the imgPicker view. But the button click is logged.
I have in the top of file: DetailTableViewController.m
#implementation DetailTableViewController
#synthesize imgPicker;
In DetailTableViewController.h
#interface DetailTableViewController : UITableViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextViewDelegate> {
MWFeedItem *item;
NSString *dateString, *summaryString;
IBOutlet UIWebView *webview;
IBOutlet UIButton *dismissViewButton;
UIImagePickerController *imgPicker;
UINavigationController *newNavController;
IBOutlet UIButton *upload;
IBOutlet UIButton *doneEdit;
IBOutlet UIImageView *image;
}
How can I make the imgPicker display?
I have tried:
using presentModalViewController instead of push in grabImage
List item many combinations of which controller imgPicker is presented to. With some
I get the error: (Application tried to present a nil modal view controller on target)
several variations from this question:
iPhone modal view inside another modal view?
UPDATED including corrections from #aBitObvious, but the issue remains.
#aBitObvious pointed me to the answer. The local override turned out to be the answer but not on imgPicker but on newNavController.
Thanks again!
I have a viewcontroller which contains an instance variable containing a dictionary object with a bunch of data. The view is fairly complex and contains several subviews that i instantiate and embed from seperate view files(To avoid having a thousand lines of UI code in the actual viewcontroller) - But how do these subviews, which exists in their own files, get access to my dictionary object from the viewcontroller?
So when im editing the DescriptionView.m file - How do i get access to the contents of the locationData dictionary object from the ViewController?
Hope you understand what i mean.
Here's a snippet from the ViewController:
CaseViewController.h
#import "DescriptionView.h"
#interface CaseViewController : UIViewController {
NSDictionary *locationData;
DescriptionView *descriptionView;
}
#property (nonatomic, retain) NSDictionary *locationData;
#property (nonatomic, retain) DescriptionView *descriptionView;
#end
CaseViewController.m
- (void)loadView {
UIView *view = [[UIView alloc] init];
descriptionView = [[DescriptionView alloc] initWithFrame:CGRectMake(0, 130, 320, 237)];
descriptionView.hidden = NO;
[view addSubview:descriptionView];
self.view = view;
[view release];
}
Ideally you should never access any properties of viewcontroller from the view.
The main idea of MVC architecture is that viewcontroller tells it's views what to render and not vise versa.
So you just have to provide all the data that your view needs for rendering during it's initialization:
- (void)loadView {
UIView *view = [[UIView alloc] init];
descriptionView = [[DescriptionView alloc] initWithFrame:CGRectMake(0, 130, 320, 237) paramDict: self.locationData]; descriptionView.hidden = NO;
[view addSubview:descriptionView];
[descriptionView release]; // BTW add this line here (or in dealloc) or you'll have a leak
self.view = view; [view release];
}
If you need to update your view dynamically, then you should add some methods to your view and call them from viewcolnroller.
E.g.:
DescriptionView.m:
-(void) updateWithDict:(NSDictionary*) udict;
If you need to perform some actions when some button in DescriptionView is pressed (or any other user interaction) a good idea would be declaring a protocol like DescriptionViewDelegate (or smth like that):
-(void) descriptionViewButton1Pressed:(DescriptionView*) dview;
-(void) descriptionViewButton2Pressed:(DescriptionView*) dview;
then make your CaseViewController a delegate and implement that methods there.
The simpliest way to have a reference to its viewcontroller from a view is to extend UIView:
#interface MyView: UIView {
UIViewController *mViewController;
}
Then in loadView
MyView *view = [[MyView alloc] init];
view.mViewController = self;
I want to use a modal view (UIViewController) as a "normal" view, which can be pushed on the navigation controller stack. Normally, a modal view is presented like this:
LoginViewController *myView = [[MyViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:myView];
[self.navigationController presentModalViewController:navController animated:YES];
[myView release];
myView = nil;
[navController release];
navController = nil;
But I want to do something like this:
[[self navigationController] pushViewController:myView animated:YES];
The problem is that my modal view has a right and a left button. So I would have to check how the view is loaded and present the buttons in another way. The idea behind this is to have the back button. So I can use the same modal view a few times.
Edit:
#petert:
Now I followed your example. My issue is that I'm using a UINavigationBar for the modal view. To get this UINavigationBar I create a navigation controller. I'm using the navigation bar because I have my buttons in it. So checking if parentViewController is of type UINavigationController does not work for me. I'm always getting a modal view. Here is how I do it:
// load modal view
MyViewController *myView = [[MyViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:myView];
[[self navigationController] presentModalViewController:navController animated:YES];
[navController release];
navController = nil;
[myView release];
myView = nil;
// load as normal view
MyViewController *myView = [[MyViewController alloc] init];
[[self navigationController] pushViewController:myView animated:YES];
Good tips in this StackOverflow answer.
I prefer to use UIViewController's property:
#property(nonatomic, readonly) UIViewController *parentViewController
in a view controller's subclass:
Look at the value of the controller's parentViewController property. If it's an instance of UINavigationController, then you're in the navigation stack. If you're being displayed modally, it'll be an instance of your last view controller.
So in -viewDidLoad for example:
- (void)viewDidLoad
{
if ([self.parentViewController isKindOfClass:[UINavigationController class]])
{
// navigation controller
self.title = #"...";
}
else
{
// modal
self.title = #"Modal";
// add cancel and done buttons now...
}
}
Or, a pretty simple solution would be to customize your init method to your MyViewController class to encode your intent for the view controller.
Add the following to the MyViewController header:
#interface MyViewController : UIViewController
{
BOOL modal;
}
- (id)initForModal:(BOOL)isModal;
#end
Now in the implementation file:
#interface MyViewController ()
#property (nonatomic) BOOL modal;
#end
#implementation MyViewController
#synthesize modal;
- (id)initForModal:(BOOL)isModal;
{
if (self = [super initWithNibName:#"MyViewController" bundle:nil])
{
self.modal = isModal;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.modal)
{
// add cancel and done buttons …
}
else
{
// assuming we're presented from a navigation view …
}
}
Now to use this modally:
// load modal view
MyViewController *myView = [[MyViewController alloc] initForModal:YES];
Or not modally:
// load as normal view
MyViewController *myView = [[MyViewController alloc] initForModal:NO];
I'm assuming you're creating the view controller(s) from NIBs, but as always see the View Controller Progamming Guide for iOS and especially the section titled "Defining a Custom View Controller Class".
For clarification: myView isn't modal. You just present it as a modal one.
If you just push it into a UINavigationController hierarchy it will behave like a "normal" one.
You can't push the same view controller onto the navigation stack several times. Just once.
Also see this for how to customize the view:
SO modal question