Bronze and Silver Challenges


#1

This was a tough one for me, so I’m offering my version, which uses a programmatic addition of the segmental selector to the HypnosisView. It’s not beautiful code, but it works and critique on how to do it better is welcome. I used a setter for setColorIndex because for some reason I wasn’t getting it right with @synthesize and @property.

HypnoAppDelegate.h:

[code]#import <UIKit/UIKit.h>

@interface HypnoAppDelegate : UIResponder

@property (strong, nonatomic) UIWindow *window;

@end
[/code]

HypnoAppDelegate.m:

[code]#import “HypnoAppDelegate.h”
#import “HypnosisViewController.h”
#import “TimeViewController.h”
#import “MapViewController.h”

@implementation HypnoAppDelegate

@synthesize window = _window;

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    HypnosisViewController *hvc = [[HypnosisViewController alloc] init];

    TimeViewController *tvc = [[TimeViewController alloc] init];

    MapViewController *mvc = [[MapViewController alloc] init];

    UITabBarController *tabBarController = [[UITabBarController alloc] init];

    NSArray *viewControllers = [NSArray arrayWithObjects: hvc, tvc, mvc, nil];
    [tabBarController setViewControllers:viewControllers];

    [[self window] setRootViewController:tabBarController];

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
    }

  • (void)applicationWillResignActive:(UIApplication *)application
    {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }

  • (void)applicationDidEnterBackground:(UIApplication *)application
    {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

  • (void)applicationWillEnterForeground:(UIApplication *)application
    {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }

  • (void)applicationDidBecomeActive:(UIApplication *)application
    {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

  • (void)applicationWillTerminate:(UIApplication *)application
    {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }

@end
[/code]
MapViewController.h

[code]#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
//#import <CoreLocation/CoreLocation.h>

@interface MapViewController : UIViewController

{
IBOutlet MKMapView *worldView;
}

@end
[/code]

MapViewController.m

#import "MapViewController.h"
@implementation MapViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)bundle
{
    //Call the superclass init
    self = [super initWithNibName:nil bundle:nil];
    
    if (self) {
        // Get the tab bar item
        UITabBarItem *tbi = [self tabBarItem];
        
        // Give it to a label
        [tbi setTitle:@"Map"];
    }
    return self;
}

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    CLLocationCoordinate2D loc = [userLocation coordinate];
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 1000, 1000);
    [worldView setRegion:region animated:YES];
}

- (void)viewDidLoad
{
    [worldView setShowsUserLocation:YES];
}


@end

MapViewController.xib
File’s owner connections:
Outlets:
view – View
worldView – MapView
Referencing Outlets:
delegate – MapView

HypnosisView.h

#import <Foundation/Foundation.h>

@interface HypnosisView : UIView

{ 
    float colorIndex;
}
-(void)changeColor;

-(void)setColorIndex:(float)index;
@end

HypnosisView.m

[code] #import “HypnosisView.h”

@implementation HypnosisView

  • (void)setColorIndex:(float)index
    {
    colorIndex = index;
    NSLog(@“HV:colorIndex set to %f”,colorIndex);
    }

  • (void)changeColor
    {
    [self setNeedsDisplay];
    }

  • (void)drawRect:(CGRect)dirtyRect
    {
    NSLog(@“HypnosisView - DrawRect called”);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGRect bounds = [self bounds];
    CGPoint center;
    center.x = bounds.origin.x + bounds.size.width /2.0;
    center.y = bounds.origin.y + bounds.size.height/2.0;
    NSLog(@“bounds x:%f, y:%f”,center.x, center.y);

    // the radius of the circle should be nearly as big as the view
    float maxRadius = hypot(bounds.size.width, bounds.size.height) /2.0;

    // the thickness of the line should be 10 points wide:
    CGContextSetLineWidth(ctx, 10);

    // add shadow to subsequent drawing
    CGSize offset = CGSizeMake(3,2);
    CGColorRef color = [[UIColor darkGrayColor] CGColor];
    CGContextSetShadowWithColor(ctx, offset, 2.0, color);

    // adjust the colors so they appear as shades of just red, green or blue by zeroing out the other colors.
    float cutRed = 1;
    float cutGreen = 1;
    float cutBlue = 1;

    if (colorIndex == 1) {
    cutGreen = 0;
    cutBlue = 0;
    }

    if (colorIndex == 2) {
    cutRed = 0;
    cutBlue = 0;
    }

    if (colorIndex == 3) {
    cutRed = 0;
    cutGreen = 0;
    }

    for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20)
    { // make the circles different colors:
    CGFloat red = arc4random() % 10000001 * 0.0000001 * cutRed;
    CGFloat green = arc4random() % 10000001 * 0.0000001 * cutGreen;
    CGFloat blue = arc4random() % 10000001 * 0.0000001 * cutBlue;

      // NSLog(@"RGB: %f, %f, %f", red, green, blue);
      [[UIColor colorWithRed:red green:green blue:blue alpha:1.0] setStroke];
       CGContextAddArc(ctx, center.x, center.y, currentRadius, 0.0, M_PI * 2, YES);
      CGContextStrokePath(ctx);   
    

    }

    /* // Add the text
    NSString *text = @“You are getting sleepy.”;
    //UIFont *font = [UIFont boldSystemFontOfSize:28];
    CGRect textRect;
    textRect.size = [text sizeWithFont:font];
    textRect.origin.x = center.x - textRect.size.width /2.0;
    textRect.origin.y = center.y - textRect.size.height /2.0;

    [[UIColor blackColor] setFill];
    CGSize offset = CGSizeMake(4, 3);
    CGColorRef color = [[UIColor darkGrayColor] CGColor];

    CGContextSetShadowWithColor(ctx, offset, 2.0, color);
    [text drawInRect:textRect withFont:font];
    */

}

  • (id)initWithFrame:(CGRect)frame
    {
    NSLog(@“HypnosisView initialized with frame.”);
    self = [super initWithFrame:frame];
    if (self) {
    [self setBackgroundColor:[UIColor clearColor]];
    // [self setCircleColor:[UIColor greenColor]];
    }
    return self;
    }
    @end
    [/code]

HypnosisViewController.h

#import <Foundation/Foundation.h>
#import "HypnosisView.h"

@interface HypnosisViewController : UIViewController

{
    UISegmentedControl *segment;
    HypnosisView *currentView;
}

@end

HypnosisViewController.m

#import "HypnosisViewController.h"
#import "HypnosisView.h"

@implementation HypnosisViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)bundle
{
    //Call the superclass init
    self = [super initWithNibName:nil bundle:nil];
    
    if (self) {
        // Get the tab bar item
        UITabBarItem *tbi = [self tabBarItem];
        
        // Give it to a label
        [tbi setTitle:@"Hypnosis"];
        
        //Create a UIImage from a file
        // This will use Hypno@2x.png on retina display devices
        UIImage *i = [UIImage imageNamed:@"Hypno.png"];
        [tbi setImage:i];
        

    }
    return self;
}

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

}

- (void)loadView
{ // Create a view
    CGRect frame = [[UIScreen mainScreen] bounds];
    HypnosisView *v = [[HypnosisView alloc] initWithFrame:frame];
    currentView = v; // sets the current HypnosisView as an instance variable. 

    // Set it as "the" view of this view controller
    
    [self setView:v];
    
    // Create segmented control with a frame and relative location within the bounds:
    segment = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects: @"0",@"R", @"G", @"B", nil]];
    CGRect bounds = [v bounds];
    
    CGPoint origin; // this becomes the upper left corner of the frame within which the segmented selector appears.
   origin.x = .25 * bounds.size.width;
   origin.y = .7 * bounds.size.height;
    float frameWidth = bounds.size.width/2;
    float frameHeight = bounds.size.height/10;

    
    [segment setFrame:CGRectMake(origin.x, origin.y, frameWidth, frameHeight)];
    
    // indicate the method that should be called when the segmented selector is changed.
    
    [segment addTarget:self
                action:@selector(segmentValueChanged)
      forControlEvents:UIControlEventValueChanged];
    
    // Add the segmented selector to the view
    [v addSubview:segment];
}

- (void)segmentValueChanged
{   // When the segmented selector is changed, this method is called.
    // It get the segmented selector's index (0-3) and passes it to the HypnosisView view
    // via the setColorIndex setter, then calls changeColor to start a redraw of the view.
    float segmentIndex = [segment selectedSegmentIndex];
    [currentView setColorIndex:segmentIndex];
    [currentView changeColor];

}


- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake) {
        NSLog(@"Device shaken.");
        
        [currentView changeColor];
        
    } 
}
@end

TimeViewController.h

#import <Foundation/Foundation.h>

@interface TimeViewController : UIViewController

{
    __weak IBOutlet UILabel *timeLabel;
}

-(IBAction)showCurrentTime:(id)sender;

@end

TimeViewController.m

#import "TimeViewController.h"

@implementation TimeViewController

-(IBAction)showCurrentTime:(id)sender
{
    NSDate *now = [NSDate date];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setTimeStyle:NSDateFormatterMediumStyle];
    
    [timeLabel setText:[formatter stringFromDate:now]];
    
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)bundle
{
// Get a pointer to the application bundle object
    NSBundle *appBundle = [NSBundle mainBundle];
    
    self = [super initWithNibName:@"TimeViewController" bundle:appBundle];
 

if (self) {
    // Get the tab bar item
    UITabBarItem *tbi = [self tabBarItem];
    
    // Give it to a label
    [tbi setTitle:@"Time"];
    
    UIImage *i = [UIImage imageNamed:@"Time.png"];
    [tbi setImage:i];
    

    }
    
    return self;
}

- (void)viewDidLoad
{
    // always call the super implementation of viewDidLoad
    [super viewDidLoad];
    
    NSLog(@"TimeViewController loaded its view.");
    
    [[self view] setBackgroundColor:[UIColor greenColor]];
}

- (void)viewWillAppear:(BOOL)animated
{
    NSLog(@"Current TimeViewController will appear.");
    [super viewWillAppear];
    [self showCurrentTime:nil];
    
}

- (void)viewWillDisappear:(BOOL)animated
{
    NSLog(@"Current TimeViewController will DISappear");
    [super viewWillDisappear];
}

@end

#2

Thanks a lot for posting your solution. I had really hard time with the Bronze challenge, and you are the only one who mentioned the connections in the XIB file.