Silver challenge: solution without instance variables


#1

Solution of the silver challenge without adding any instance variable to HypnosisViewController.m.

In HypnosisViewController.m, in loadView, create a UISegmentedControl with 3 items, give it a frame and make it a subview of HypnosisView. Also create the target-acion method:

- (void)loadView
{
    // Create a HypnosisView view
    CGRect frame = [[UIScreen mainScreen] bounds];
    HypnosisView *v = [[HypnosisView alloc] initWithFrame:frame];
    // Set it as *the* view of this view controller
    [self setView:v];
    
    // CREATE AND ADD A SEGMENTED CONTROL as a subview of HypnosisView
    
    NSArray *array = [[NSArray alloc] initWithObjects:@"Red", @"Green", @"Blue", nil];
    
    UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:array];
    
    // Set Frame
    [segmentedControl setFrame:CGRectMake(34, 50, 250, 50)];
    
    // Add method to call every time user changes segment
    [segmentedControl addTarget:self
                         action:@selector(changeColor)
               forControlEvents:UIControlEventValueChanged];
        
    [v addSubview:segmentedControl];
}

Then implement the target-action method of the Segmented Control (again in HypnosisViewController.m):

- (void)changeColor
{
    NSArray *subViews = [[self view] subviews]; 
    // NSLog(@"%@", subViews);
    
    NSInteger segmentNumber = [[subViews objectAtIndex:0] selectedSegmentIndex]; // There is only one subview (the segmented control)
    NSLog(@"%d", segmentNumber);
    
    switch (segmentNumber) {
        case 0:
            [(id)self.view setCircleColor:[UIColor redColor]];
            break;
        case 1:
            [(id)self.view setCircleColor:[UIColor greenColor]];
            break;
        case 2:
            [(id)self.view setCircleColor:[UIColor blueColor]];
            break;
        default:
            break;
    }
}

The trick is the (id) in front of self.view, which allows us to reach the HypnosisView without any instance variable, and send him setCircleColor.

I took the idea of (id) from this post: http://forums.bignerdranch.com/viewtopic.php?f=219&t=6175

Hope this helps.
Albert


#2

I love this solution.

I took your code and modified it a little to make the UISegmentControl… nicer to look at, and set it towards the top of the screen.

Nice idea with ID though…


#3

The cast is a nice solution although I did differently.
I used something like this:

id myView = [self view];
[myView setCircleColor:[UIColor whateverColor];

The myView variable is local to the method.

In the HypnoViewController.h I declared the action like this:

- (void)changeColor:(id)sender

Then I think it’s easier to implement the UISegmentedControl --> HypnoViewController relation by simply using the “sender” argument.

- (void)changeColor:(id)sender
{
    id myView = [self view];
    switch ([sender selectedSegmentIndex]) {
        case 0:
            [myView setCircleColor:[UIColor redColor]];
            break;
        case 1:
            [myView setCircleColor:[UIColor greenColor]];
            break;
        case 2:
            [myView setCircleColor:[UIColor blueColor]];
            break;
        default:
            break;
    }
}

#4

i think this wud be more appropriate. I downcast self.view (which is originally of type UIView) to HypnosisView on every step. Like this—

-(void)pressSegment:(UISegmentedControl *)sender{
    switch ([sender selectedSegmentIndex]) {
        case 0:
            [(HypnosisView *)self.view setCircleColor:UIColor.redColor];
            break;
        case 1:
            [(HypnosisView *)self.view setCircleColor:UIColor.greenColor];
            break;
        case 2:
            [(HypnosisView *)self.view setCircleColor:UIColor.blueColor];
            break;
        default:
            [(HypnosisView *)self.view setCircleColor:UIColor.lightGrayColor];
            break;
    }
    [(HypnosisView *)self.view setNeedsDisplay];
    
}

And one more thing. Out of curiosity shouldn’t you be sending setNeedsDisplay to the Hypnosis view to call drawRect: internally and set up the colors?? When I commented this line, the colors would not change after clicking each segment. This should be because the entire settings for the circles is done in drawRect method in HypnosisView.m file. So unless we are able to call in drawRect: each time, the circles wud not adjust to the new colour each time we select one despite having ‘setCircleColor’. Pls tell me wat you think…