2

I'm just getting started with Objective-C programming and I'm a little bit confused about where to declare a UISegmentedController instance as a "subview" of my viewController root view.

I've been experimenting with the code and it seems to work regardless of whether its created in "loadView", viewDidLoad, or initWithNibName: bundle: and I'm wondering why this is so, and also where the correct place to create it would be.

All the views in the hierarchy were created programmatically.

Code:

UISegmentedControl code that I'm unsure where to place:

self.segCon = [[UISegmentedControl alloc] 
initWithItems:(NSArray *)@[@"Red",@"Green", @"Blue"]];

self.segCon.frame = CGRectMake(35, 200, 250, 50);

[self.segCon addTarget:self
                action:@selector(changeColor:)
      forControlEvents:UIControlEventValueChanged];
[self.view addSubview:self.segCon];

BNRHypnosisViewController.m:

#import "BNRHypnosisViewController.h"
#import "BNRHypnosisView.h"

@interface BNRHypnosisViewController()

@property (strong, nonatomic) UISegmentedControl *segCon;

- (void)changeColor:(id)sender;

@end

@implementation BNRHypnosisViewController

-(void)loadView
{
    //create a view
    BNRHypnosisView *backgroundView = [[BNRHypnosisView alloc] init];

    //set it as *the* view of this view controller
    self.view = backgroundView;

Do I place the UISegmentedControl code here?

}

-(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {
        //Set the tab bar items title
        self.tabBarItem.title = @"Hypnotize";

        //Create a UIImage from the file
        // This will use Hypno@2x.png on retina devices
        UIImage *i = [UIImage imageNamed:@"Hypno.png"];

        //put the image on the tab bar
        self.tabBarItem.image = i;

or here?

    }

    return self;
}    

-(void)viewDidLoad
{
    //Always call the super implementation of viewdidload
    [super viewDidLoad];

    NSLog(@"BNRHypnosisViewController loaded its view");

or here?

}

- (void)changeColor:(id)sender
{
    NSLog(@"The Segment controller was touched %d", self.segCon.selectedSegmentIndex);
    if(self.segCon.selectedSegmentIndex == 0){
    ((BNRHypnosisView *)self.view).circleColor = [UIColor redColor];
    }
    if(self.segCon.selectedSegmentIndex == 1){
    ((BNRHypnosisView *)self.view).circleColor = [UIColor greenColor];
    }
    if(self.segCon.selectedSegmentIndex == 2){
    ((BNRHypnosisView *)self.view).circleColor = [UIColor blueColor];
    }

}


@end    

Any help would be greatly appreciated, thank you in advance for your feedback!

Tulon
  • 4,011
  • 6
  • 36
  • 56
iCode101
  • 395
  • 2
  • 17
  • Side note - If you are doing everything programmatically (which I do myself) then why did you implement the `initWithNibName:bundle:` method? Just implement `init`. – rmaddy May 18 '14 at 16:16
  • 1
    Thats a good question. I'm actually following the book 'iOS programming - the Big Nerd Ranch guide' and this is what it instructs. In the appDelegate where I create this view controller, I use init. I think that because initwithNibName... is the designated initialiser, it is called regardless and the arguments are nil for nibName and for bundle. That being said, I guess I could have just overridden init instead which would implicitly call initWithNibName... – iCode101 May 18 '14 at 16:23
  • Yeah, since you aren't using a nib, just override `init`. It's simpler and cleaner. – rmaddy May 18 '14 at 16:27
  • I used a previous edition of this book, and that had me using the interface builder (.xib files) to create some of the view controllers. – Andrew May 18 '14 at 16:41
  • @SantaClaus Yep, 2 of the 3 viewControllers use interface builder. However the one i've posted doesn't. Maybe it was written as such for consistency..? – iCode101 May 18 '14 at 16:46
  • @iCode101 Well, this view controller does't need a .xib because its view is just an instance of the already completed `BNRHypnosisView` – Andrew May 18 '14 at 16:54

2 Answers2

3

I normally put such code in viewDidLoad. But since you have a need to implement loadView, you can setup the segmented control there.

Don't do it in the init... method because then you end up needlessly loading the view from the init... method.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Thanks for your response (can't up vote you since I need 15 reputation, but as soon as i get, i will!). Would you also recommend that i put `if (self) { self.tabBarItem.title = @"Hypnotize"; UIImage *i = [UIImage imageNamed:@"Hypno.png"]; self.tabBarItem.image = i; ` in the loadView or viewDidLoad method? Why do you think the book has instructed me to put this in the init method? – iCode101 May 18 '14 at 16:27
  • 1
    Setting up the `tabBarItem` in `init` is fine. Just avoid `self.view` in the `init` method. – rmaddy May 18 '14 at 16:29
  • Ok, thanks. Is it considered best practice to set up tabBarItem and similar set ups in the init method? – iCode101 May 18 '14 at 16:31
  • Not really. Setting up the `tabBarItem` can be done in `init` or `viewDidLoad` or even the calling code creating the view controller. In this case, the answer is "it depends". – rmaddy May 18 '14 at 16:33
  • What would it depend on? would there be a difference in executing the code if it was setup in init or viewDidLoad? Thanks again – iCode101 May 18 '14 at 16:36
  • It would only matter if some other class was setting up the `tabBarItem`. – rmaddy May 18 '14 at 16:40
0

I think the best practice is to conserve the memory your application uses at any given time by not instantiating anything that is not needed. In other words, as @rmaddy said, you don't want to call a method in the init method of your view controller that would prematurely create a view the user doesn't need and is therefor taking up valuable memory on the heap. following that premise, you shouldn't go wrong. Nice post btw. I'm following the same book by Big Nerd Ranch and it is a great learning tool for any beginners viewing this! A side note, I see you changed the UIColor circleColor; from a class extension to a public property in BNRHypnosisView.h. you can keep it a class extension and still set the _circleColor instance variable in your - (void)changeColor:(id)sender method by using key value coding like this:

    - (void)changeColor:(id)sender
{
    NSLog(@"The segment controller was touched %d", self.colorSwitch.selectedSegmentIndex);
    if (self.colorSwitch.selectedSegmentIndex == 0) {
        [self.view setValue:[UIColor redColor] forKeyPath:@"circleColor"];
    } else if (self.colorSwitch.selectedSegmentIndex == 1) {
        [self.view setValue:[UIColor greenColor] forKeyPath:@"circleColor"];
    } else if (self.colorSwitch.selectedSegmentIndex == 2) {
        [self.view setValue:[UIColor blueColor] forKeyPath:@"circleColor"];
   }
}