My Silver Challenge Solution


#1

Here is the code I used for the Silver Challenge

I made the circleColor property Public so that it could be accessed outside of the class.

BNRHynosisterView.h

@interface BNRHynosisterView : UIView

@property (strong, nonatomic) UIColor *circleColor;

@end

BNRHypnosisViewController.m

#import "BNRHypnosisViewController.h"
#import "BNRHynosisterView.h"



@implementation BNRHypnosisViewController

-(void)loadView
{
    // Create a view
    BNRHynosisterView *backgroundView = [[BNRHynosisterView alloc]init];
    
    // Set is as *the* view of this view controller
    self.view = backgroundView;
    
   // backgroundView.setNeedsDisplay
}

Created an Action Method to be used by Segmented control. This method takes in an argument of UISegmentedControl.

When the user selects a segment the circleColor property is assigned a color, the view is updated, and a segment control is added to new view.

-(IBAction)selectorAction:(UISegmentedControl *)sc
{
    BNRHynosisterView *v = [[BNRHynosisterView alloc]init];
    
    if (sc.selectedSegmentIndex == 0){
        v.circleColor = [UIColor redColor];
        self.view = v;
        [v addSubview:sc];
    } else if (sc.selectedSegmentIndex == 1){
        v.circleColor = [UIColor blueColor];
        self.view = v;
        [v addSubview:sc];
    } else if (sc.selectedSegmentIndex == 2){
        v.circleColor = [UIColor greenColor];
        self.view = v;
        [v addSubview:sc];
    }
}
-(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil
                           bundle:nibBundleOrNil];
    
    if (self) {
        
        // Set the tab bar item's title
        self.tabBarItem.title = @"Hynotize";
        
        // Create a UIImage fram a file
        // This will use Hypno@2x.png on retina display devices
        UIImage *i = [UIImage imageNamed:@"Hypno.png"];
        
        // Put that image on the tab bar item
        self.tabBarItem.image = i;

I created the segmented control and initiated it with 3 segment titles.

I set the background color of segment to white

I setup the Target-Action methods

Added Segmented control to view.

set the frame size and location of SC

        // Create segmented controls with 3 segments
        UISegmentedControl *rgbSegment = [[UISegmentedControl alloc]initWithItems: @[@"Red", @"Blue", @"Green"]];
        rgbSegment.backgroundColor = [UIColor whiteColor];
        [rgbSegment addTarget:self action:@selector(selectorAction: ) forControlEvents:UIControlEventValueChanged];
        
        // Add SC to window
        [self.view addSubview:rgbSegment];
        
        // set frame size and location
        rgbSegment.frame = CGRectMake(100.0, 20.0, 120, 20);
        
    }
    
    return self;
}

-(void)viewDidLoad
{
    // Always call the super implementation of viewDidLoad
    [super viewDidLoad];
    
    NSLog(@"BNRHynosoViewControler loaded its view.");
}

@end

Please review and all comments are welcomed.


#2

This is a good start, but it would be much more efficient to use only one instance of BNRHypnosisterView instead of adding a new instance each time the UISegmentedControl is touched.
I recommend that you create a property for the one BNRHynosisterView, like so:

@interface BNRHypnosisViewController ()

@property (nonatomic, strong) BNRHynosisterView *backgroundView;

@end

Once you have this, you can update loadView to use the property instead of creating a local variable, like so:

- (void)loadView
{
    self.backgroundView = [[BNRHynosisterView alloc] init];
    self.view = self.backgroundView;

    // Here, you'd add your code for creating and adding the UISegmentedControl.
    ...
}

Finally, you’d need to update selectorAction: to use the new property, like so:

- (IBAction)selectorAction:(UISegmentedControl *)sc
{
    if (sc.selectedSegmentIndex == 0) {
        self.backgroundView.circleColor = [UIColor redColor];
    } else if (sc.selectedSegmentIndex == 1) {
        ...
    } else if (sc.selectedSegmentIndex == 2) {
        ...
    }
}

Because you’d be reusing the same BNRHypnosisView instance instead of creating it new each time, you’d also need to add a call to setNeedsDisplay inside of setCircleColor:.

Hope that helps.


#3

Loving your solution serpent5.
I have to admit though that I’m really unable at this point to think up something like this myself. I’m struggling right now, but I suppose that is to be expected. I went as far as chapter 8 and decided to start over to try and let it sink in better, but still really am struggling with this challenge.
Rehashing and redoing and rereading will eventually get me there I suppose, but wondering on how long you were working on getting your solution done for this chater?


#4

It’s hard to put a number on it because I went through the third edition of this book last year and now I’m making my way through this version, so I’m working off some experience I already had with these concepts. The important thing to remember is that both the silver and gold challenges are designed to push you and to make you think (and struggle!). There are many solutions to most of the challenges throughout the book and a lot of these will give you a hard time. Don’t be frustrated by feeling the need to start again - I read the third edition a couple of times to help it sink in. These forums are a great resource for learning and each time you see a solution you like, you will learn from it and take a step closer to figuring things out on your own.


#5

On my second full run through the book and I finally managed to do this challenge quite comfortably. Only thing I’m wondering about and not sure if anyone else can figure this one out.

The UISegmentedControl class scales it’s own size to the contents you provide it with, but the only way to change it’s origin settings is the change the entire frame property. When you try to change segControl.frame.origin it will give an error that this is not something you can alter.

So despite it’s own scaling, you have to pass your own values for the size as well to change it’s origin! Seems like a waste to me.