Connect together: AppDelegate, ViewController and XIB - ios

I am new to the iOS programming and got stuck with one problem.
What was done:
I have created iOS application.
Initially it had main.m, AppDelegate.h, AppDelegate.m and some other supporting files (not with code) that are usually created.
Then I manually created XIB file with interface (LoginView.xib) and manually added LoginViewController.h and LoginViewController.m to control XIB.
Added outlets to LoginViewController.h.
After all I set up file's owner class (LoginViewController) and made connections between XIB and LoginViewController.h.
Problem description:
I need to show instantiate login view controller and show login view immediately at the application's start.
I tried several attempts and read a dozen of forum threads, but no way. Nothing is shown except white window background.
How can I do it correct?
Reference code:
LoginViewController.h
#import <UIKit/UIKit.h>
#interface LoginViewController : UIViewController
{
IBOutlet UIView *_loginView;
IBOutlet UITextField *_tfLogin;
IBOutlet UITextField *_tfPassword;
IBOutlet UIButton *_btnLoginToSystem;
}
- (IBAction)attemptLogin:(id)sender;
#end
LoginViewController.m
#import "LoginViewController.h"
#interface LoginViewController ()
#end
#implementation LoginViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)attemptLogin:(id)sender
{
}
#end
AppDelegate.h
#import <UIKit/UIKit.h>
#import "LoginViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) LoginViewController *loginViewController;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (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];
self.loginViewController = [[LoginViewController alloc] initWithNibName:#"LoginView" bundle:nil];
[self.window.rootViewController presentModalViewController:self.loginViewController animated:NO];
[self.window makeKeyAndVisible];
return YES;
}
UPDATE!!!
Guys, thanks to you all I've found another issue. Once I got your approve that my code (after appropriate edition, of course) is correct, I started simulator and saw that my app crashes with the following exception:
NSInternalInconsistencyException with the reason [UIViewController _loadViewFromNibNamed:bundle:] loaded the ... nib but the view outlet was not set.
Thanks to StackOverflow I fixed it.
Loaded nib but the view outlet was not set - new to InterfaceBuilder
Quotation of Josh Justice comment from the topic I provided link to:
Open the XIB file causing problems
Click on file's owner icon on the left bar (top one, looks like a yellow outlined box)
If you don't see the right-hand sidebar, click on the third icon above "view" in your toolbar. This will show the right-hand sidebar
In the right-hand sidebar, click on the third tab--the one that looks a bit like a newspaper
Under "Custom Class" at the top, make sure Class is the name of the ViewController that should correspond to this view. If not, enter it
In the right-hand sidebar, click on the last tab--the one that looks like a circle with an arrow in it
You should see "outlets" with "view" under it. Drag the circle next to it over to the "view" icon on the left bar (bottom one, looks like a white square with a thick gray outline
Save the xib and re-run
Probably this information aggregated into one point will help other newcomers to pass the way I passed faster.

Set the LoginViewController as your root view controller in your application:didFinishLaunchingWithOptions: method.
self.window.rootViewController = self.loginViewController;
remove this line:
[self.window.rootViewController presentModalViewController:self.loginViewController animated:NO];

You have to set the rootViewController like this in your AppDelegate.m
self.window.rootViewController = self.loginViewController;

Have you worked with creating your interface with storyboards yet? (Storyboards are new as of iOS 5) They are basically a way of laying out all your view controllers in one file and visually setting connections between them. What you are wanting to do is really easy with storyboards. When you set up view controllers on your storyboard file you will see an arrow pointing to the first storyboard you set up. That means when you run your app that is the initial view that will load. If you prefer a different view to load first simply drag that arrow to a different view controller.

Related

How can I prevent ViewControllers from being deallocated after I inject them with a dependency in application:DidFinishLaunchingWithOptions:

I am trying to inject a dependency (to a data store) into all the viewControllers in my tabbed app, from AppDelegate, rather than access the datastore by reaching back into the appDelegate. I am using a storyboard.
I do this in application:didFinishLaunchingWithOptions:, and the code executes without errors.
However, when any of the viewControllers is presented, the property into which I have injected the datastore contains nil. I was expecting it to have a reference to the datastore.
I thought maybe my datastore went out of scope after application:didFinishLaunchingWithOptions returns and caused the datastore to become nil. But to my knowledge ARC should prevent that.
I started to suspect that maybe the VCs might go out of existence after application:didFinishLaunchingWithOptions: finishes running. So I added a dealloc method to the view controllers to see if it gets called, and lo-and-behold, it did. That explains why the dependency I injected previously is no longer there.
Now I am stuck, as I don't know how else to inject the dependency into the view controllers. The only idea I have left is to add properties to my AppDelegate and use them to retain the view controllers, but that feels a bit dangerous cause I'm now interfering with iOS management of view controllers.
Here is the code in AppDelegate:
//AppDelegate.h
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
//AppDelegate.m
#import "AppDelegate.h"
#import "InjectedViewController.h"
#import "InjectedDataStore.h"
#interface AppDelegate ()
#property (strong, nonatomic) InjectedDataStore *myDataStore;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UIStoryboard *storyBoard;
storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
if (!_myDataStore) {
self.myDataStore = [[InjectedDataStore alloc]init];
NSLog(#"alloc inited %#", self.myDataStore);
}
UITabBarController *tabBarController = (UITabBarController *)initViewController;
for (InjectedViewController *ivc in tabBarController.viewControllers) {
ivc.dataStore = self.myDataStore;
NSLog(#"dataStore injected into ivc: %#", ivc.dataStore);
}
NSLog(#"application:didFinishLaunching... done");
return YES;
}
#end
here is my view controller subclass with the property into which I want to inject:
//InjectedViewController.h
#import <UIKit/UIKit.h>
#import "InjectedDataStore.h"
NS_ASSUME_NONNULL_BEGIN
#interface InjectedViewController : UIViewController
#property (strong, nonatomic) InjectedDataStore *dataStore;
#end
NS_ASSUME_NONNULL_END
InjectedViewController.m is boilerplate and otherwise empty.
InjectedDataStore.m and .h are a boilerplate Cocoa Touch class without any properties or methods.
and here is one of the viewcontrollers - it is embedded in a tab view. (The other view controller for the other tab is identical.
//FirstViewController.h
#import <UIKit/UIKit.h>
#import "InjectedViewController.h"
#interface FirstViewController : InjectedViewController
#end
//FirstViewController.m
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"data store for FirstVC: %#", self.dataStore);
}
- (void) dealloc {
NSLog(#"First VC dealloc called");
}
#end
and finally, the console output:
alloc inited <InjectedDataStore: 0x600001d483a0>
dataStore injected into ivc: <InjectedDataStore: 0x600001d483a0>
dataStore injected into ivc: <InjectedDataStore: 0x600001d483a0>
application:didFinishLaunching... done
First VC dealloc called ///this is what causes the injected element to disappear.
Second VC dealloc called ///causes the injected element to disappear.
data store for FirstVC: (null)
data store for SecondVC: (null)
(I would eventually implement a protocol, but for now since I am stuck at getting the injection to work I have left that out.)
It seems to me what I have done here is very similar to #juanignaciosi 's answer to this question:
Appreciate any feedback, I am a relative newbie to iOS.
Your's window property is nil.
Since iOS 13 system is using window property from scene delegate.
Just remove scene delegate and UIApplicationSceneManifest from plist if you don't need it.

Pushing view controller is not working properly in ios

I am passing textfield data between two view controllers. i have two view controllers Firstviewcontroller, Secondviewcontroller. i have embed them in navigation but when i click the button on first view controller, second view controller is not showing only black screen is showing.below is my code.
First view controller.h
#import <UIKit/UIKit.h>
#import "secondViewController.h"
#interface FirstViewController : UIViewController<SecondViewControllerDelegate>
#property(nonatomic) secondViewController *secondview;
- (IBAction)btnclick:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *textfield1;
#end
First view controller.m
- (IBAction)btnclick:(id)sender {
secondViewController *SecondViewController= [[secondViewController alloc]init];
SecondViewController.data=self.textfield1.text;
[self.navigationController pushViewController:SecondViewController animated:YES];
}
- (void)dataFromController:(NSString *)data
{
[self.textfield1 setText:[NSString stringWithFormat:#"%#",data]];
}
Seconviewcontroller.h
#protocol SecondViewControllerDelegate <NSObject>
#required
- (void)dataFromController:(NSString *)data;
#end
#interface secondViewController : UIViewController
#property (nonatomic, retain) NSString *data;
#property(nonatomic,weak)id<SecondViewControllerDelegate> _delegate;
#property (strong, nonatomic) IBOutlet UITextField *textfield2;
#end
Seconviewcontroller.m
- (void)viewDidLoad {
[super viewDidLoad];
self.textfield2.text=[NSString stringWithFormat:#"%#",_data];
// Do any additional setup after loading the view.
}
whats the reason ??
You have to either use storyboard and let it intanciate the view controller for you:
[self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"]
Or you can use xib files and tell the view controller which one to load:
[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil]
second view controller is not showing only black screen is showing
Typically, this means that the view controller has no view. Your code does not explain where secondViewController is expected to get its view from. Is there a secondViewController.xib? If there is, is it correctly configured with a view outlet? If not, what does this view controller do in order to get its view?
Note that you have done a very odd thing with capitalization: you have given your class a small letter (secondViewController), while your instance has a capital latter (SecondViewController). This is backwards and a big mistake, and you should correct it immediately. Perhaps the problem is just a capitalization problem; if you call your class secondViewController and your xib file SecondViewController, that is not a match and they won't find each other.
I see "IBOutlet" in your code, it means you created the user interface of secondviewcontroller on storyboard. I guess [[secondViewController alloc]init] does not get UI element from storyboard then the black screen is shown. You have to create instance of secondViewController by loading from storyboard file.
Basically:
you can get storyboard via storyboard file name
on storyboard, Secondviewcontroller has an storyboard id which is set in Identity Inspector and you can use it to get instance of Secondviewcontroller
You can refer this link for more detail
loading iOS viewcontroller from storyboard
Hope this can help!

IOS dev. Tap button, screen does not call a new view

Goal: to switch views when you click the button.
What I did:
Open a single view application.
File -> New -> File -> Cocoa Touch & Objective -C. Create
PlayViewController and GMViewController, both subclass
UIViewController.
Went to mainstoryboard and from the object library, I dragged two
View Controllers, with PLayViewController and GMViewController as
their STORYBOARD IDs.
IN ViewController.h I have these codes:
.
#import <UIKit/UIKit.h>
#class PlayViewController;
#class GMViewController;
#interface ViewController : UIViewController
#property (strong, nonatomic) PlayViewController *playViewController;
#property (strong, nonatomic) GMViewController *gmViewController;
- (IBAction)toPlay:(id)sender;
- (IBAction)toGM:(id)sender;
#end
Basically, here I created two buttons. one will go to PlayView, the other to GMView.
5. In my ViewCotroller.m, I have these:
#import "ViewController.h"
#import "PLayViewController.h"
#import "GMViewController.h"
- (IBAction)toPlay:(id)sender {
UIStoryboard *storyboard =[UIStoryboard storyboardWithName:#"Main" bundle:nil];
PlayViewController *playController =[storyboard instantiateViewControllerWithIdentifier:#"PlayViewController"];
self.playViewController = playController;
[self.view removeFromSuperview];
[self.view insertSubview: self.playViewController.view atIndex:0];
}
- (IBAction)toGM:(id)sender {
//still blank, waiting to fix the play button.
}
Problem is when I click the play button the screen goes black. Please tell me what I missed. Thanks
As per your code, you are first removing self.view and then you are trying to insertSubview on self.view thats way it is not working for you so change your code with
first insret self.playViewController.view to self.view and then it remove from super view such like,
[self.view insertSubview: self.playViewController.view atIndex:0];
[self.view removeFromSuperview];
Hope that my suggestion will be work for you.

Why can't I set the background color of my custom UIView in Interface Builder?

I'm diving into iOS development and I'm starting with a very simple example to understand using IB to create a custom view. I have a simple view controller...
#interface MyViewController ()
#property (weak, nonatomic) IBOutlet MyView *myView;
#end
#implementation MyViewController
//Boilerplate code...
And my custom view...
#implementation MyView
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
and in the app delegate, I set the root view controller to MyViewController...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
MyViewController *myVC = [[MyViewController alloc] init];
self.window.rootViewController = myVC;
[self.window makeKeyAndVisible];
return YES;
}
So I have a nib for MyViewController and a nib for MyView. In the nib for MyViewController, I dragged a UIView into the center of it and set the class for that view to MyView. When I run the app, I see the MyView view with the red background color. Here's my question: when I comment out the line of code in the MyView implementation file that sets the background color to red, and instead use the nib for MyView to set the background color to some other color in interface builder, the background color of the view is white. Why can't I set the background color of MyView in Interface Builder? Why does it only get set if I set it programmatically?
Thanks in advance for your wisdom!
It's ultimately very simple, yet a bit tricky.
You need to realise one important fact. Things you see in Interface Builder are "frozen instances" of objects. They are awaken during initialisation by respective owner classes.
Thouroughly think about the relationships you have set up and the steps that happen during initialisation.
Your VC gets set up as root view controller. That means, on your screen you see the content of VCs nib file, that is two views (UIview class instances)
1) ViewController's view
2) another UIview (you have set it's class to MyView)..
Now if you override initWithCoder in your MYview.m, of course it is executed and the color is set to red.It happens because you have set the 2) (up there) instance of UIview in you VCs nib is of custom class i.e.MyView.
On the other hand if you go to the MYView nib file ..you can set anything you want here but you won't see it on screen. Why? Because THIS instance is never awaken. It's simply a nib file that sits there in the project and is not used. The instance you see on screen is the instance that is coming from the main VC's view (it is its subview).
Basically you have 1 interface file MyView.h, 1 implementation file MYView.m and two instances of this class. Both are frozen in nibs. One is in VC's nib, the other one is in MyView.nib. You just do not use the MyView.nib.
The solution to make this work is out of scope of your question.

Ios simulator giving me a black screen when trying to load storyboard?

I have been trying to solve this problem for over 14 hours now...I am really hoping that someone could please help me load my storyboard. Everytime I try it non-programmatically, it decides it wants to keep it as a black screen (and its not the loading screen. I checked to see if it was in the initial view controller. Yet nothing. So I figure, maybe I need to do it programmatically. I've synthesized them properly as you can see. Still nothing. What am I doing wrong (there is no xcode error output).
Appdelegate.h
#import <UIKit/UIKit.h>
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
UIWindow *window;
UIViewController *viewController;
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *viewController;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController.h"
#implementation AppDelegate
#synthesize window;
#synthesize viewController=_viewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc]
initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Save data if appropriate
}
If you are using a Storyboard, you should remove the code you have written in the app delegate method; change it to look like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
Next look at your build target properties (in your project navigator, click on the name of your project at the very top). Is your storyboard selected in the "Main Storyboard" section of the "Summary" pane?
Finally go to the storyboard and make sure your view controller is selected as the initial view controller. Select the view controller scene and verify that the "Initial Scene: Is Initial View Controller" box in the attributes inspector pane is checked.
If all of these steps have been verified and you're still seeing a blank screen, it's possible that there is a bug in your view controller. Try dragging a brand new view controller to the storyboard, add a label, and set it as the initial view controller. If you are able to see that scene launch, there is a problem with your original view controller (rather than it being an issue with the launching of your storyboard).
First of all your viewController property is just a nil, it's not initialized anywhere. With this self.window.rootViewController = viewController; you just pass nil to rootViewController.
In Interface Builder you should have arrow indicating your initial view controller - as seen on this picture or in Attributes Inspector as seen here
You may also read excellent tutorial on storyboards here

Resources