Silver Challenge using Auto Layout (in iOS 6)


#1

Hello,

So I’m following along the third edition of the book and for the silver challenge in this chapter, I wanted to make use of iOS 6’s new Auto Layout feature to position the segmented control instead of using hardcoded layout positioning.

My first attempts to do this were unsuccessful so eventually, I decided to watch this year’s WWDC “Introduction to Auto Layout for iOS and OS X” session video. I quickly realized what I was doing wrong (hint: I wasn’t setting the segmented control’s translatesAutoresizingMaskIntoConstraints property to NO) and was then able to get it working with the following code in my HypnosisViewController’s viewDidLoad method:

- (void)viewDidLoad
{
    // Always call the super implementation of viewDidLoad
    [super viewDidLoad];
    
    UISegmentedControl *segmentedControl =
        [[UISegmentedControl alloc] initWithItems:@[@"Red", @"Green", @"Blue"]];
    
    [segmentedControl setTranslatesAutoresizingMaskIntoConstraints:NO];
    [segmentedControl addTarget:self
                         action:@selector(colorSegmentDidChange:)
               forControlEvents:UIControlEventValueChanged];
    
    UIView *superview = [self view];
    [superview addSubview:segmentedControl];
    
    NSLayoutConstraint *centerXConstraint =
        [NSLayoutConstraint constraintWithItem:segmentedControl
                                     attribute:NSLayoutAttributeCenterX
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:superview
                                     attribute:NSLayoutAttributeCenterX
                                    multiplier:1.0
                                      constant:0.0];
    NSLayoutConstraint *bottomPaddingConstraint =
        [NSLayoutConstraint constraintWithItem:segmentedControl
                                     attribute:NSLayoutAttributeBottom
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:superview
                                     attribute:NSLayoutAttributeBottom
                                    multiplier:1.0
                                      constant:-20.0];

    [superview addConstraints:@[centerXConstraint, bottomPaddingConstraint]];
    
    NSLog(@"HypnosisViewController loaded its view.");
}

This code is admittedly a bit lengthy (much more so than setting hardcoded positioning values), but it is also much more flexible in that the same code will work on any iOS device (iPhone, iPad), regardless of the device resolution (Retina or not) and of the screen orientation. Also, I’m using the sleek new array literals syntax to define the color list passed as the items property of the segmented control initializer.

If you’d like to understand what this code does and how it works, I highly recommend watching the above-mentioned WWDC session video.

For completion’s sake, here is the definition of the target / action method that is referenced in the above code block and which is used to handle the taps on the color segmented control:

- (void)colorSegmentDidChange:(id)sender
{
    NSArray *colorChoices = @[[UIColor redColor], [UIColor greenColor], [UIColor blueColor]];
    UIColor *selectedColor = [colorChoices objectAtIndex:[sender selectedSegmentIndex]];
    [(HypnosisView *)self.view setCircleColor:selectedColor];
}

Note: it is not necessary to add the declaration of this method in the HypnosisViewController’s header file, the method is not referenced in any other classes from the project and thus does not need to be publicly exposed.


#2

Thanks for this. I found the technique to dynamically place the UISegementedControl based on edges of the screen really interesting.

I am still new to objective-c syntax and am unfamiliar with this statement:
[(HypnosisView *)self.view setCircleColour:selectedColor];
Is this shorthand for a number of separate statements? If so, what would they be? I tried the following:
HypnosisView *myView = [self view];
[myView setCircleColour:selectedColor];
but that just gave me a warning saying ‘Incompatible pointer types initializing HypnosisView *__strong with an expression of type UIView’.


#3

I also received the “incompatible pointer types…” error on the following line:

HypnosView *circles = [self view];

even though the code underneath which uses the ‘circles’ variable to change the color works correctly:

switch (mapTypeIdx) {
        case 0:
            [circles setCircleColor:[UIColor redColor]];
            break;
            
        case 1:
            [circles setCircleColor:[UIColor greenColor]];
            break;
            
        case 2:
            [circles setCircleColor:[UIColor blueColor]];
            break;
            
        default:
            break;

Does anyone know why?


#4

No worries. I found the answer at viewtopic.php?f=219&t=4175