7
\$\begingroup\$

I'm learning how to develop for iOS and I want to begin with nib's before storyboard to get a better understanding.

I just set up 3 view controllers:

  • HomeVIewController

  • StackTableViewController

  • CreateViewController

The HomeVIewController.h:

#import <UIKit/UIKit.h>

@interface HomeViewController : UIViewController

- (IBAction)goToStack:(id)sender;

- (IBAction)goToCreate:(id)sender;

@end

The HomeVIewController.m:

#import "HomeViewController.h"
#import "StackTableViewController.h"
#import "CreateViewController.h"

@interface HomeViewController ()

@property (strong, nonatomic) UINavigationController *navigationController;

@end

@implementation HomeViewController

- (id)init {

    self = [super initWithNibName:@"HomeViewController" bundle:nil];
    if (self) {
        // Do something
    }
    return self;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (IBAction)goToStack:(id)sender {

    StackTableViewController *stackViewController = [[StackTableViewController alloc] initWithNibName:@"StackTableViewController" bundle:nil];
    self.navigationController = [[UINavigationController alloc] initWithRootViewController:stackViewController];
    [self presentViewController:self.navigationController animated:YES completion:nil];
}


- (IBAction)goToCreate:(id)sender {

    CreateViewController *createViewController = [[CreateViewController alloc]initWithNibName:@"CreateViewController" bundle:nil];
    self.navigationController = [[UINavigationController alloc]initWithRootViewController:createViewController];
    [self presentViewController:self.navigationController animated:YES completion:nil];
}

This view controller has a nib file with a view and 3 buttons at the bottom of the page that I use to navigate.

The StackTableViewController.h:

#import <UIKit/UIKit.h>

@interface StackTableViewController : UITableViewController

@end

The StackTableViewController.m:

#import "StackTableViewController.h"

@interface StackTableViewController ()

@property (strong, nonatomic) UINavigationController *navBar;

@end

@implementation StackTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(cancel)];
    self.navigationItem.leftBarButtonItem = anotherButton;
}

- (void)cancel {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

This table view controller have a nib file with a table view.

The CreateViewController.h:

#import <UIKit/UIKit.h>

@interface CreateViewController : UIViewController

@end

The CreateViewController.m:

#import "CreateViewController.h"

@interface CreateViewController ()

@property (strong, nonatomic) IBOutlet UITextView *myTextView;

@end

@implementation CreateViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(cancel)];
    self.navigationItem.leftBarButtonItem = cancelButton;
}

- (void)viewWillAppear:(BOOL)animated {

    [self.myTextView becomeFirstResponder];
}


- (void)cancel {

    [self.myTextView resignFirstResponder];

    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

This view controller also have a nib file with a UITextView and a cancel button to go back to the homepage.

I didn't use any data yet, and just wanted to know if I'm doing the transitions right since I'm very new to this.

Something feels weird about the way I use the navigation controller.

I'd love to get some feedback on this, especially some stuff in the view hierarchy of the UIViewController lifecycle.

nib screenshots:

home:

enter image description here

stack:

enter image description here

create:

enter image description here

\$\endgroup\$
8
  • 1
    \$\begingroup\$ I will review this when I get a chance, but for what it's worth, I'd strongly recommend doing storyboards first, as they're easier, and then come back and learn NIBs as necessity demands. \$\endgroup\$
    – nhgrif
    Dec 12, 2014 at 21:53
  • \$\begingroup\$ Should the title say "model" instead? Or am I just trying to see model-view-controller everywhere? \$\endgroup\$
    – Brythan
    Dec 12, 2014 at 21:57
  • \$\begingroup\$ @nhgrif thanks man! this is actually what I did..I build something in storyboard and now I'm transferring it to nib files \$\endgroup\$ Dec 12, 2014 at 21:59
  • \$\begingroup\$ @Brythan Well, I don't think the title should say modal, as there are no modal transitions here. \$\endgroup\$
    – nhgrif
    Dec 12, 2014 at 21:59
  • \$\begingroup\$ Actually, I take that back... this is a modal transition... although it's not called "modal" any more. \$\endgroup\$
    – nhgrif
    Dec 12, 2014 at 22:03

2 Answers 2

2
\$\begingroup\$

Your code is fine. I understand that it feels strange to instantiate UINvagationController where you only need custom view controller, but this is how it's done in UIKit.

You actually don't need to keep a reference to this navigation controller in your HomeViewController. You better keep reference to StackTable and Create view controllers.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ thanks allot for your comment! can you please tell me why I should use property's for the homeviewcontroller and the stacktableviewcontroller? \$\endgroup\$ Dec 12, 2014 at 22:27
  • 1
    \$\begingroup\$ Because they hold some logical value for your comparing to generic navigation controller that you don't need. I mean if you want to keep a reference to child controllers - do that, otherwise you can remove it altogether. \$\endgroup\$
    – sha
    Dec 12, 2014 at 22:34
2
\$\begingroup\$
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

This is repeated through all of your view controllers. I don't blame you too terribly much, because I know it's something that Xcode pre-populates whenever you create a new UIViewController subclass, and it's extraordinarily silly. I've never written a single line of code in this method. I know what it is, and what it's for, but for a lot of development, you'll never need this method, so I'm not sure why Apple insists on generating it for you.

Anyway, any time you have a method, and within the method, the only thing that happens is a call to the super implementation of that method, we can simply not include the method. So, let's just delete it. And get in the habit of deleting this one every time.

It doesn't effect performance to delete it or leave it... it just clutters up the source file. And with that said, we can also delete viewDidLoad from the first class (it just calls to [super viewDidLoad];).


@interface HomeViewController : UIViewController

- (IBAction)goToStack:(id)sender;

- (IBAction)goToCreate:(id)sender;

@end

We've exposed these IBAction methods in our .h file. There's really no reason to do this at all. No other class should be calling these methods. These methods should only be called by the action we've hooked up to it (i.e. pressing a button). Likely, you created these via Ctrl+Dragging from your NIB file to the .h file. We can instead change the assistant editor to show us the .m file rather than the .h and Ctrl+Drag from the button to the @implementation section to just create the method straight in the .m without exposing it.


@interface HomeViewController ()

@property (strong, nonatomic) UINavigationController *navigationController;

@end

The UIViewController class already has this exact property actually, so we don't need this at all.


[super initWithNibName:@"HomeViewController" bundle:nil];

We should specify the bundle we're loading the nib from. By passing nil to this method, the implementation assumes your nib is in the main bundle. Most of this time, this will probably be the case, but passing nil is just lazy. However, what should always be the case is that our nib should be in the same bundle as our .h and .m files for that class. So instead of passing nil, we should do this:

NSBundle *bundle = [NSBundle bundleForClass:[self class]];
[super initWithNibName:@"HomeViewController" bundle:bundle];

Now, as to your primary concern of the navigation controller. What we're doing is indeed a little strange.

A UINavigationController object is a subclass of UIViewController. One of the properties of a UINavigationController is the viewControllers property. This is its navigation stack essentially.

We have two actions with a navigation controller--pushing and popping.

We start with a navigation controller and its root view controller. We can add to the navigation controller's navigation stack by "pushing" new view controllers onto the stack. The navigation controller keeps track of every view controller that's on the stack though in its viewControllers property. The key here is that we can then "pop" one of these view controllers off of the stack, and the navigation controller takes care of presenting the next highest view controller remaining in the stack.

Think of it like a deck of cards. We start with the AceOfSpadesViewController card face up on the table. We push a button on that view and that pushes a new card, the KingOfDiamondsViewController onto the stack. Then we push another button on this view controller in order to go back to where we came from, and the KingOfDiamondsViewController is "popped" off the stack.

So for your setup, realistically, it is the HomeViewController which should be the root view controller of a navigation controller, then, in order to push a new view controller onto the navigation stack, we'd rewrite goToCreate: to look something like this:

- (IBAction)goToCreate:(id)sender {
    // self.navigationController will already be a reference to the nav controller we're embedded in
    NSBundle *createBundle = [NSBundle bundleForClass:[CreateViewController class]];
    CreateViewController *createViewController = [[CreateViewController alloc]initWithNibName:@"CreateViewController" bundle: createBundle];
    [self.navigationController pushViewController:createViewController animated:YES];
}  

And in the CreateViewController, when we're done there and ready to "pop" back to whatever previous screen we came from:

- (void)cancel {
    [self.view endEditing:YES];
    [self.navigationController popViewControllerAnimated:YES];
}

There are two other ways of popping backward on a navigation controller as well. The above method just removes the top-most view controller from the navigation stack.

We can also call the method popToRootViewControllerAnimated: which will remove everything except the root view from the navigation stack, which is very useful if we need to quickly get back all the way to the "home" screen.

And you might rarely need to use popToViewController:animated:, which accepts a UIViewController reference, and if that UIViewController object is in the stack, the navigation controller removes everything that is on top of the top-most instance of this object from the navigation stack.


As one final note, you'll notice I changed:

[self.myTextView resignFirstResponder];

to

[self.view endEditing:YES];

If we ever have more than one view that is editable, our method will quickly become clogged up with a lot of resignFirstResponder calls. endEditing: recursively calls resignFirstResponder on all of the subviews (and subviews' subviews).

But I'm pretty sure this isn't actually needed either way. When the view controller is dismissed, then any of its subviews can no longer be first responder any more and the keyboard should go away on its own.

\$\endgroup\$
3
  • \$\begingroup\$ Hi man thanks so much about your comment :)! it helps allot. few things: isn't the pushViewController method will do a push transition? I want modal...**the code for goToCreate docent work for me, you wrote "self.navigationController will already be a reference to the nav controller we're embedded in" . at this point we'r not embedded in any and controller.. \$\endgroup\$ Dec 12, 2014 at 23:14
  • \$\begingroup\$ the only time i use a navigation controller is when I modal to stack table view controller or to create view controller...and it feels like i'm doing it wrong with the nav bars controllers :/ I know you guys said it's kind of weird in the beginning \$\endgroup\$ Dec 12, 2014 at 23:21
  • \$\begingroup\$ My point is that I can't see why a modal transition is preferred. And I don't see why we want a navigation controller if the root view of that navigation controller doesn't push any other controllers onto the navigation stack... and the root view controller uses a navigation bar back button to emulate going back in a navigation stack (even though in this navigation controller, there's no where to go back to). It doesn't make sense. \$\endgroup\$
    – nhgrif
    Dec 13, 2014 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.