Programming iOS: clarifications about Root View Controller - objective-c

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;
}

Related

Objective c: iOS 13+ programmatically setting initially root view controller

I am trying to set an initial root view controller programmatically using SceneDelegate in iOS 14 (no storyboard at all). I have followed https://medium.com/#sapa.tech/xcode-11-3-remove-storyboard-from-project-6c48c9d11123 to remove storyboard from my app, and have seen many answers to this question in swift in Why is manually setup root view controller showing black screen?, and Set root view controller iOS 13 and iOS 13: Swift - 'Set application root view controller programmatically' does not work. However, I am looking for tips on how to do it in objective C instead. So far, I have this in my scene delegate, where ViewController is the class I am trying to set as the root view controller:
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
UIWindowScene * winscene = [[UIWindowScene alloc]initWithSession:session connectionOptions:connectionOptions];
[self.window setWindowScene:winscene];
ViewController * viewController = [[ViewController alloc]init];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
}
And in my AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
return YES;
}
I have tried to translate the Swift answers I found to objective c but I still end up with a black screen on the simulator. The app builds fine with no error, but I just cannot get the ViewController to show up instead of a black screen. Does anyone know what I am missing?
You're not supposed to create a UIWindowScene yourself, but to use the one given as parameter:
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
ViewController * viewController = [[ViewController alloc]init];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
}

Make UITabBarController the root view controller

I made an empty application, then later I and added a UITabBarController in the Storyboard. Then I checked it as the initial viewcontroller and connected two NavigationControllers to represent the tabs.
But if I run the app now it says that "Application windows are expected to have a root viewcontroller at the end of application launch." and shows a white screen.
My AppDelegate looks like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
I have to add the TabBarController as the main window, but how to do this?
If TabBarController was made programmatically I would have figured it but how to when it's created in storyboard? Can I make a property for it in AppDelegate? How to hook it in storyboard in that case?
You can hook storyboard inside your target settings Then make sure that your UITabBarController inside of your storyboard is initial UIViewController . You can also use this code:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *initialViewController = (UIViewController *)[storyboard instantiateViewControllerWithIdentifier:#"IdentifierOfYourViewController"];
self.window.rootViewController = initialViewController;

Converting a category on UIViewController into a custom subclass

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.

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.

Push NavigationController from UIScrollView

how can I push a new view onto the stack of a NavigationController from a UIScrollView?
I tried
[self.navigationController pushViewController:myNewViewController animated:YES];
but get "navigationController not in structure or union".
regards
You don't have a navigation controller in your app. You need to create one. Something like:
In your appDelegate create a UINavigationController instance variable and then use your existing viewController as the rootViewController of the navigation controller.
e.g. in pure code using a UITableViewController (you can use xibs as well which your template app probably does).
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create root view and navigation controller
UITableViewController *rootViewController = [[[UITableViewController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];
self.navigationController = [[[UINavigationController alloc] initWithRootViewController:rootViewController] autorelease];
// Not necessary if you're using xibs
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Add the nav controller's root view to the window
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
Then you can push/pop new views in the way you're attempting.