Can't set property in parentViewController - objective-c

I have a view controller, MainViewController in which I have a UIButton as an IBOutlet that is connected up in Interface Builder
In the header I have
#interface MainViewController : UIViewController
{
SettingsViewController *settingsViewController;
UIButton *leftTextButton;
}
#property (nonatomic, retain) IBOutlet UIButton *leftTextButton;
in the .m I have synthesised the property
#synthesize leftTextButton;
I then add a settings subview as follows
SettingsViewController *settingsViewController = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController"
bundle:nil];
[self.view.superview addSubview:settingsViewController.view];
Within the SettingsViewController I then try to update the title of the UIButton *leftTextButton in the parent view
[ ((MainViewController*) self.parentViewController).leftTextButton setTitle:#"Ahhhhh !!" forState:UIControlStateNormal];
Although this doesn't crash it doesn't work - the title of the button does not change.
Any help would be greatly appreciated

The parentViewController property is nil unless the view controller is presented modally, or is under a UINavigationController or a UITabBarController.
Give your SettingsViewController a mainViewController property:
#interface SettingsViewController
...
#property (assign) MainViewController *mainViewController;
Set it when you create the SettingsViewController:
SettingsViewController *settingsViewController = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
settingsViewController.mainViewController = self;
Then you can use it to update the button title:
[self.mainViewController.leftTextButton setTitle:#"Ahhhhh !!" forState:UIControlStateNormal];

If you just add the view controller's view in the parent view, the view controller will have no idea of its parent view controller, thus the parentViewController property is nil. You also have to add your SettingsViewController to your MainViewController via addChildViewController: and then notify the SettingsViewController via didMoveToParentViewController: that you have created this hierarchy. Then you should be able to access the parentViewController.
(Note: this is for iOS 5 only, if you're still on iOS 4 see rob mayoff's reply)

Related

uninitialized property in prepareForSegue

I'm having some difficulty managing a segue. My view controller hierarchy is as follows:
BHGSplitViewController (subclass of UISplitViewController)
UINavigationController
MasterViewController (subclass of UITableViewController)
UINavigationController
DetailViewController (subclass of UIViewController)
These have the following properties:
BHGSplitViewController:
#interface BHGSplitViewController : UISplitViewController
#property (nonatomic, strong) MasterViewController* masterVC;
#end
MasterViewController:
#interface MasterViewController : UITableViewController
#property (strong, nonatomic) MenuDataSource *menuDataSource;
#property (strong, nonatomic) DetailViewController *detailViewController;
#end
DetailViewController:
#interface DetailViewController : UIViewController
#property (strong, nonatomic) NSURL* URL;
#property (strong, nonatomic) IBOutlet UIWebView *webView;
#end
These are related in my main storyboard through relationship segues.
After the initial app launch, I modally display a login view controller. After login, I segue back to my BHGSplitViewController, but I need to set some data. In the loginViewController's prepareForSegue method I attempt the following:
BHGSplitViewController *splitVC = [segue destinationViewController];
splitVC.masterVC.menuDataSource.var = someValue;
But, when debugging, splitVC.masterVC = nil, so obviously attempting to set a value on it is not going to work. How do I set-up and retain those relationships?
I inherited a version of this app that was built with storyboards. I'm guessing that I need to start intializing these properties. What's the best way to do that with storyboards? Do I need to override initWithCoder:? Should I be setting these properties in viewDidLoad?
Edit: Explanation of segue
So, I'm loading up my BHGSplitViewController in my AppDelegate and setting it up to handle collapses, etc. After that, I need to immediately display a modal view for login. I need this to animate in like a push (but Apple won't allow that), so I'm trying to hack my way around that by using a custom segue (which is hacky and I hate it):
Present Segue:
- (void)perform {
UIViewController *srcViewController = (UIViewController *) self.sourceViewController;
UIViewController *destViewController = (UIViewController *) self.destinationViewController;
UIView *prevView = srcViewController.view;
UIView *destView = destViewController.view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
[window insertSubview:destView aboveSubview:prevView];
[destView enterLeft:0.1 then:^{
[srcViewController presentViewController:destViewController animated:NO completion:nil];
}];
}
Dismiss Segue:
- (void)perform {
UIViewController *srcViewController = (UIViewController *) self.sourceViewController;
UIViewController *destViewController = self.destinationViewController;
UIView *prevView = srcViewController.view;
UIView *destView = destViewController.view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
[window insertSubview:destView aboveSubview:prevView];
[destView enterRight:0.1 then:^{
[srcViewController presentViewController:destViewController animated:NO completion:nil];
}];
}
You can set the properties in viewDidLoad. initWithCoder is definitely too early since the split view controller is instantiated first, and then the other controllers it houses. So, you can do this in viewDidLoad of BHGSplitViewController,
-(void)viewDidLoad {
[super viewDidLoad];
UINavigationController *nav = (UINavigationController *)self.viewControllers.firstObject;
self.masterVC = (MasterViewController *)nav.topViewController;
}

How do I use a button to get back to my first view

I am using a single view application template in xcode. I created the first view controller, and then added another with a new .m and .h and xib.
I can click a button IBAction, and get to my second view, however the code I am using for the "back" button wont take me back to my first view, everything crashes. I have included my code which seems to follow the tutorial I was using. Additionally I just control clicked my button and dragged the line to my IBAction in the .h to hook in the secondViewController buttons, which is what I did on the first view controller and it seems to work there.
If anyone can help that would be great!
//from my first view controller .h which works
-(IBAction) buttonPressedPayTable: (id) sender;
//from my first view controller.m which also works and gets me to the second view
-(IBAction) buttonPressedPayTable: (id) sender
{
SecondViewController *payTableView = [[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
[self.view addSubview:payTableView.view];
}
//from my second view controller .h that will not get me back to the first view without crashing
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
{
}
-(IBAction) back: (id)sender;
#end
//from my second view controller .m which doesn't seem to work
#import "SecondViewController.h"
#implementation SecondViewController
-(IBAction) back: (id)sender
{
[self.view removeFromSuperview];
}
#end
use UINavigation controller
[self.navigationController popViewControllerAnimated:YES];
You might be better off using modal views. So instead of addSubView use:
[payTableView setModalPresentationStyle:UIModalPresentationFullScreen];
[payTableView setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentModalViewController:payTableView animated:YES];
Then on the seconViewController back method:
[self dismissModalViewControllerAnimated:YES];
You can change the ModalTransitionStyle to the few that apple gives you :D
Hope this helps
You can use a navigation method for switching from one view controller to the other.
See the apple docs about view controllers
Don't add your second ViewController as a subview of the view.
Use a UINavigationController to add the new UIViewController to the view stack using [self.navigationController pushViewController:payTableView animated:YES];
Then you can either use the nav controllers back button, or use your own button with the code [self.navigationController popViewControllerAnimated:YES];
Alternately, you can use Storyboards and use a simple segue.
[payTableView.view removeFromSuperview];
i think you are removing the whole view .that is why app is crashing
You need to use delegate here:
in .h of first view declare a member variable :
#interface FirstViewControllerClassName: UIViewController
{
SecondViewController *payTableView;
}
#property (nonatomic, retain) SecondViewController *payTableView;
in .m :
#synthesize payTableView;
-(IBAction) buttonPressedPayTable: (id) sender
{
if (!payTableView)
payTableView = [[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
payTableView.delegate = self;
payTableView.isFinishedSelector = #selector(removeView);
[self.view addSubview:payTableView.view];
}
- (void)removeView
{
[payTableView.view removeFromSuperview];
[payTableView release];
payTableView = nil;
}
//Just declare a member variable delegate in secondviewcontroller like:
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
{
id delegate;
SEL isFinishedSelector;
}
#property (nonatomic, assign) id delegate;
#property (nonatomic, assign) SEL isFinishedSelector;
-(IBAction) back: (id)sender;
#end
#import "SecondViewController.h"
#implementation SecondViewController
#synthesize delegate;
#synthesize isFinishedSelector;
-(IBAction) back: (id)sender
{
if ([self.delegate respondsToSelector:isFinishedSelector])
[self.delegate performSelector:isFinishedSelector];
}
#end

Adding navigation controller on a view controller subclass

How do you add a navigation controller on a newly created view controller? i've search everywhere but all the tutorials are from creating a navigation controller project.
Anyone can lead mo to a tutorial that creates a navigation controller using a view controller subclass?
What i'm doing so far:
I created a UIViewController Project, and i have something like this to go to another view controller, with a navigation controller.
NavController *view=[[NavController alloc] initWithNibName:nil bundle:nil];
view.modalTransitionStyle=UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:view animated:YES];
[view release];
Added a new view controller subclass.
Add > New File > UIViewController subclass with nib
on NavController.h
#import <UIKit/UIKit.h>
#interface NavController : UIViewController {
IBOutlet UIWindow *window;
IBOutlet UINavigationController *navCon;
}
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) UINavigationController *navCon;
#end
on NavController.m
#import "NavController.h"
#implementation NavController
#synthesize window,navCon;
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[window release];
[navCon release];
[super dealloc];
}
#end
i already dragged a Navigation Conrtoller and a Window on my IB, and connected window to window and the Navigation Controller to navcon outlets, but whats next?
If you're using the storyboards select your view controller then in top menu choose "editor" / "embed in" / "navigation controller".
Normally, you have to create an Navigationcontroller object inside your Appdelegate.h (like the existing window object). After that you import the h.File of a ViewController into the Appdelegate.m and init it like in the following example the menuviewcontroller.
stackoverflow
To call another view use following lines of code, so the navigationcontroller will handle everything for you.
#import Viewcontroller
ViewControllerName controllerVarName = [ViewControllerName alloc] init];
[self.navigationcontroller pushViewController:_ViewControllerName animated:YES];
Inside your specific ViewController use this line to set the title the Navigationcontroller will use:
self.title = #"titleName";

Correct way of setting up UINavigationController and its RootViewController in IB

I have the following structure in my iPad App:
Application
UINavigationController (Providing the top bar with UIBarButtons etc.)
Initial Login screen
Second login screen
I am not sure how I should now set this up in Interfacebuilder correctly. My guess would be that I create two ViewControllers:
LoginVC1: (This one should also include the NavigationController since it is the first of the two screens)
LoginVC2: Based on some delegate callback from LoginVC1 my application would push to this ViewController.
This is my LoginVC1 in IB:
LoginVC1 http://k.minus.com/jpamEAFkBjpKT.png
And when I present it modally it looks like this which is not what I want:
Result http://k.minus.com/jHAYRnY788jFt.png
The result:
Nor the title of the ViewController nor the Cancel button is shown
The view seems to be empty despite my view in IB
I have set the Presentation mode of the UINavigationController to FormSheet which is ignored too since it is displayed in fullscreen.
What am I doing wrong?
Kjuly I simply do presentModalViewController to show the dialog, but
since my UINavigationControler contains a UIViewController (See
nib-screen), I would assume that this ViewController is displayed when
the modal is shown.
Not always Besi. Adding it to the UINavigationController Hierarchy doesn't add is as the rootViewController. That must be done in code like this:
UIViewController *rootViewController = [[[ExamplesViewController alloc] init] autorelease];
UINavigationController * navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
Also, check that you are presenting the NavigationController, and not the ViewController modally.
To solve the NavigationBar title issue, try setting the self.title property in the
-(void)viewDidload method and see if that works. If not, try self.//instance of UINavigationController//.navigationBar.title = #"string".
As for the other buttons not showing up, my guess is that if setting the root controller in code doesn't help, then you only made reference to them in the .h, instead of instantiating them. So either call something like this in the .h:
//.h
#implementation ExampleViewController: UIViewController <UITextFieldDelegate> {
IBOutlet UIBarButtonItem * CancelButton;
IBOutlet UITextField * usernameField;
IBOutlet UITextField * passwordField;
IBOutlet UIButton * loginButton;
}
#property (nonatomic, retain) IBOutlet UIBarButtonItem * CancelButton;
#property (nonatomic, retain) IBOutlet UITextField * usernameField;
#property (nonatomic, retain) IBOutlet UITextField * passwordField;
#property (nonatomic, retain) IBOutlet UIButton * loginButton;
Then connect the outlets in the XIB, or instantiate the buttons in the .m with:
//.m
-(void)viewDidLoad {
//do stuff
CancelButton = [[[UIBarButtonItem alloc]initWithTitle:#"Cancel" style:UIBarButtonItemStyleDone target:self action:#selector(dismissSelf)]autorelease];
//do more stuff
}

SplitViewController UI layout

I'm trying to create a UI whereby I have a SplitView with the Details area containing a TabBarController. The TabBarController will show 3 different types of detail for the item selected in the RootViewController of the SplitView.
So far, I've got the TabBar showing the SPlitView by doing the following;
1) Created a new SplitView based app.
2) Created a new TabBar based app
3) Copy the .xib, .h and .m files for the FirstView and SecondView controllers from the TabBar app into the SplitView app.
4) Added the following to my application delegate header file;
#class RootViewController;
#class DetailViewController;
#class FirstViewController;
#class SecondViewController;
#interface SplitViewTemplateAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UISplitViewController *splitViewController;
UITabBarController *tabBarController;
RootViewController *rootViewController;
DetailViewController *detailViewController;
FirstViewController *firstViewController;
SecondViewController *secondViewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UISplitViewController *splitViewController;
#property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
#property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) IBOutlet FirstViewController *firstViewController;
#property (nonatomic, retain) IBOutlet SecondViewController *secondViewController;
5) Opened up MainWindow.xib in IB and changed the class on the DetailsView to UITabController
6) Added the following code to my application delegate module file;
#import "FirstViewController.h"
#synthesize window, splitViewController, rootViewController, detailViewController, tabBarController, firstViewController, secondViewController;
-(void) makeTabBarController {
NSMutableArray *controllers = [NSMutableArray arrayWithArray:splitViewController.viewControllers];
int index = 0;
for (UIViewController *controller in splitViewController.viewControllers) {
if (index == 1) {
//NSLog(#"Index is: %#", index);
//NSLog(#"Controller name is: %#", controller.title);
UINavigationController *localNavController;
tabBarController = [[UITabBarController alloc] init];
NSMutableArray *localViewControllersArray = [[NSMutableArray alloc] initWithCapacity:2];
firstViewController = [[FirstViewController alloc] initWithNibName:#"FirstView" bundle:nil];
localNavController = [[UINavigationController alloc] initWithRootViewController:firstViewController];
localNavController.tabBarItem.title = #"First Tab";
[firstViewController release];
[localViewControllersArray addObject:localNavController];
[localNavController release]; // Retained by above array
secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondView" bundle:nil];
localNavController = [[UINavigationController alloc] initWithRootViewController:secondViewController];
localNavController.tabBarItem.title = #"Second Tab";
[secondViewController release];
[localViewControllersArray addObject:localNavController];
[localNavController release]; // Retained by above array
tabBarController.viewControllers = localViewControllersArray;
[localViewControllersArray release]; // Retained thru above setter
//tabBarController.delegate = splitViewController;
[controllers replaceObjectAtIndex:index withObject:tabBarController];
}
index++;
}
splitViewController.viewControllers = controllers;
}
7) Added the following to the didFinishLaunchingWithOptions method;
[self makeTabBarController];
So now I get an out of the box SplitView with a tab bar controller on the right with two tabs in it. The tabs work for switching between the views.
A couple of things that I am now struggling with are;
The button to fire the Popover is missing, do I need to add this to each tab view?
How do I hook the RootViewController with the TabBarController so that details for the selected item is shown?
not sure if you're still looking for an answer for this. I ran into a very similar issue with the question about multiple detail views. In the end, I found and used this solution from the apple developer site:
SubstitutableDetailViewController
Their solution is very straightforward to implement and very understandable.
With regards to the second part of your question, you could have the TabBarController Delegate inform the view on the left hand side of the split view who the current detail view is. It could then use that information to provide updates to the proper view.