Converting a category on UIViewController into a custom subclass - objective-c

I had a class category that adds some methods to UIViewController. I needed to add some instance variables to the category, so I turned it into a custom UIViewController subclass, and added the instance variables. I then turned the UIViewController I was displaying into an instance of the new subclass.
Now I'm having trouble loading the new UIViewController when the application loads. I load the view controller from a nib in my AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[ATFIPresentationViewController alloc] initWithNibName:#"ViewController_iPhone" bundle:nil];
} else {
self.viewController = [[ATFIPresentationViewController alloc] initWithNibName:#"ViewController_iPad" bundle:nil];
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible]; //Crashes Here
return YES;
}
After doing so, an exception is thrown when I call makeKeyAndVisible on my application's window:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason:
'[<ATFIPresentationViewController 0x6c497d0> setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key basicPicker.'
Where basicPicker is an IBOutlet to a UITextField. This is happening for every IBOutlet I've defined in my viewController. Why would making my viewController a subclass of my subclass of UIViewController, prevent anything loading from my nib? The "Files Owner" in the nib is ViewController, not ATFIPresentationViewController.
EDIT: Well, I got tired of trying to get this to work the "proper" and less typing heavy way. I got it to work by turning the extension into an NSObject, and pass the UIViewController to it. I posted what I was using this for on gitHub if anyone wants to take a look.

That error is occurring because your nib is trying to set a value onto an instance of a class that doesn't have a property by the name basicPicker. This is a solid indication that you're sending messages to the wrong type of object.
You need set the class of your File's Owner in your nib files to be the new UIViewController subclass you have created and not just the default UIViewController. Select File's Owner in your nib and you can change it in the right panel 'Utilities' third tab named Identity Inspector in Xcode, right at the top.

Related

When does the UIViewController get deleted in ARC

I am Working on a ARC based project. I got two ViewControllers. In the First ViewController I have a button, on pressing that button I am pushing the new ViewController.
I am doing it as follows:
SecondViewController *second=[[SecondViewController alloc]initWithStrVal:self.strVal];
[self.navigationController pushViewController:second animated:YES];
My problem is when I push the SecondViewController,and then create an object of the FirstView Controller in SecondViewController the dealloc method in FirstViewController gets called.
Where as the dealloc method doesn't get called if I only do the push and don't create an instance of FirstViewController in SecondViewController. As far as I have heard when I push a View Controller, dealloc shouldn't be called in the normal scenario, because the previous ViewController shoudn't deleted in the normal Scenario in ARC based project
EDIT:
The First ViewController is set in the app delegate as follows :
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.navController=[[UINavigationController alloc]initWithRootViewController:viewController];
self.window.rootViewController = self.navController;

Call method from initialized UIViewController in storyboarding - objective c

I'm new to Storyboarding in objective c and I need to call method from UIVIewController. Before Storyboarding I was initializing UIViewController in AppDelegate or just assigning pointer there, and then simply call method from any class, accessing AppDelegate properties. How to do It in Storyboarding, If there are no UIViewController initializing by myself? My app is UITabBar application if it does matter.
You can get the root view controller of your storyboard from your app delegate. For example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UITabbarController *tbc = (UITabbarController *)self.window.rootViewController;
// Do any setup you want to here
// You aren't setting up the controller - just accessing the one that has been set up for you by the storyboard
return YES;
}
If you want to have this method available from any class you can keep doing the same. Place in in your AppDelegate and then just retrieve the shared instance on the controller you want to use it from. (all your application has access to your appdelegate) so just import the header of the app delegate, create a new instance of your specific appdelegatename class, and call the shared delegate. then you can use the method as if that class would have it.
Additionaly you can use something like this
#import "AppDelegate.h"
#define appDelegate (AppDelegate *) [[UIApplication sharedApplication] delegate]
IF you want to call a method from ANY of your viewcontrollers which are specified in the storyboard you can do it by referencing their identifier:
PreviewViewController *tmp = [[self storyboard] instantiateViewControllerWithIdentifier:#"PreviewViewController"];

Cannot load custom view from nib into another view made programmatically?

Looking at some of the other questions, this is the code I implemented in the .m of the view implementing the custom keyboard.
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.userInteractionEnabled = YES;
[self addGestureRecognizer:
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(becomeFirstResponder)]];
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"FormulaKeyboard" owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[FormulaKeyboard class]])
keyboard = (FormulaKeyboard *)object;
}
self.inputView = keyboard;
}
return self;
}
Below is the error that showed up.
* Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key view.'
When I load the nib in an actual view controller, it works fine. However, as evidenced above, it doesn't work when I try loading it inside another view.
Also, in the nib, I set the file owner's class to UIViewController and attached it to the main view, since this was what another SO question instructed. I'm not sure if I need to modify this since I'm adding the custom view inside another view and not a viewc controller.
Thanks
EDIT:
.h of view implementing keyboard
#interface EquationTextField : UIView <KeyInput> {
FormulaKeyboard *keyboard;
}
#property (readwrite, retain) UIView *inputView;
#end
Inside the xib if you ctrl + click the File's Owner you will most likely see that you have a view outlet that is connected to the top level view object in the xib.
Refers to an IBOutlet in your code
Refers to an object in Interface Builder
The reason this works inside a UIViewController is that a UIViewController has a property view which gets connected up when loading the xib.
A UIView subclass does not normally have a property called view therefore you get an exception.
Seems as you are grabbing the item from the top level objects array returned by the loadNibNamed:owner:options: method you do not need this connection in the xib. Therefore you can disconnect this connection in the xib file.
Alternatively
You could:
Change the File's Owner class to the class of your custom view.
Make inputView an IBOutlet
Connect inputView to your custom keyboard in the xib
Then just use
[[NSBundle mainBundle] loadNibNamed:#"FormulaKeyboard" owner:self options:nil];
It seems like your view does not have a property called 'inputView', hence when you set the property on self, you get the exception.

Load a UIViewController as main controller

I just created an empty iOS project and added a UIViewController with a nib file. This should load my main view.
How do I tell my application to load a certain UIViewController as main view? Do I have to set it on the app delegate or somewhere in xcode?
In your app delegate have a variable for the controller....
MyViewController myController;
Then you want...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
myController = [[MYViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[self.window addSubview:myController.view];
[self.window makeKeyAndVisible];
return YES;
}
You can also hook this up via Interface Builder, but the above is how you would do it in code.

Programming iOS: clarifications about Root View Controller

Through this question I would like to know if I understand well the notion of Root View Controller.
In iOS application, the Root View Controller (RVC) is the controller whose view gets added to the UIWindow application at startup, isn't true?
[window addSubview:rvcController.View];
[window makeKeyAndVisible];
Now, an UIWindow has also a rootViewController property. When running the previous snippet of code, does that property gets populated with the rvcController or do I have to set it explicitly?
Then, in a UINavigationController it is possible to set a RVC that is different from the previous RVC set for the entry point.
In this case, the first time I add a controller to the navigationController stack (pushing a new controller on it), does the framework set that controller as the RVC for the navigationController or do I have to set it explicitly through initWithRootViewController method?
Ya.. when I began iPhone dev.. the rootViewController thing threw me for a loop too. But it’s really straight forward.
when the app starts, I create a UIWindow object in my app delegate class. Also, in that class, I have a property of type UIWindow called window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIWindow *w = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
self.window=w;
[w release];
// other code here...
}
I then create a UIViewController whose view will be the first view in the window hierarchy, this could be called the "root view controller".
The confusing part is...that often we create a UINavigationController as the "root view controller" and that navigation controller has an init method that asks for a "RootViewController", which is the first viewcontroller it will place on its stack.
So, the window gets a "root view controller", which is the UINavigationController, which also has a RootViewController, which is the first view controller you want to show.
once you sort that out, its all makes sense.. I think :-)
here is some code that does it all.. (taken from a project I have open in front of me)
//called with the app first loads and runs.. does not fire on restarts while that app was in memory
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//create the base window.. an ios thing
UIWindow *w = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
self.window=w;
[w release];
// this is the home page from the user's perspective
//the UINavController wraps around the MainViewController, or better said, the MainViewController is the root view controller
MainViewController *vc = [[MainViewController alloc]init];
UINavigationController *nc = [[UINavigationController alloc]initWithRootViewController:vc];
self.navigationController=nc; // I have a property on the app delegate that references the root view controller, which is my navigation controller.
[nc release];
[vc release];
//show them
[self.window addSubview:nc.view];
[self.window makeKeyAndVisible];
return YES;
}
Now, an UIWindow has also a rootViewController property. When running the previous snippet of code, does that property gets populated with the rvcController or do I have to set it explicity?
You have to set it explicitly, and if you do, you can remove the addSubview line, because that's handled automatically when you set a root view controller.
Then, in a UINavigationController it is possible to set a RVC that is different from the previous RVC set for the entry point.
Of course, a navigation controller's root view controller has nothing to do with that of the window.
In this case, the first time I add a controller to the navigationController stack (pushing a new controller on it), does the framework set that controller as the RVC for the navigationController or do I have to set it explicity through initWithRootViewController method?
initWithRootViewController is just a shortcut for initializing an empty navigation controller and pushing the first (root) view controller onto the stack. Note that rootViewController is not a property of UINavigationController, you would access it via [navController.viewControllers objectAtIndex:0].
firstly you can create A empty project in Xcode. after you add the new file on objectivec class view controller with xiv. now you can add to this code in appdeligate.m
and set the rootviewcontroller in appdeligate
NOTE:- ViewController.h import to the appdeligate.m
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
ViewController *viewcontroller =[[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController= viewcontroller;
[self.window makeKeyAndVisible];
return YES;
}
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
ViewController *viewcontroller =[[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController= viewcontroller;
[self.window makeKeyAndVisible];
return YES;
}