Another Silver Challenge Solution


#1

My solution is a little bit different than the others, so I thought I would share it. I have only included files that I made changes to from the chapter project.

Any comments appreciated!

BNRHypnosisView.h

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

@interface BNRHypnosisView : UIView

@property (nonatomic, strong) UIColor *circleColor;

@end
[/code]
I made circleColor available to other classes to use here.

BNRHypnosisView.m

[code]#import “BNRHypnosisView.h”

@interface BNRHypnosisView ()

@end

@implementation BNRHypnosisView

@synthesize circleColor;

  • (id)initWithFrame:(CGRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {
    // Initialization code
    self.backgroundColor = [UIColor clearColor];
    self.circleColor = [UIColor lightGrayColor];
    }
    return self;
    }

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.

  • (void)drawRect:(CGRect)rect
    {

    CGRect bounds = self.bounds;

    // Figure out the center of the bounds rectangle
    CGPoint center;
    center.x = bounds.origin.x + bounds.size.width / 2.0;
    center.y = bounds.origin.y + bounds.size.height / 2.0;

    // The largest circle will circumscribe the view
    float maxRadius = hypot(bounds.size.width, bounds.size.height) / 2.0;

    UIBezierPath *path = [[UIBezierPath alloc] init];

    for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) {

      [path moveToPoint:CGPointMake(center.x + currentRadius, center.y)];
      
      [path addArcWithCenter:center
                      radius:currentRadius
                  startAngle:0.0
                    endAngle:M_PI * 2.0
                   clockwise:YES];
    

    }

    // Specify line width for circle
    path.lineWidth = 10;

    // Specify color of the stroked line
    [self.circleColor setStroke];

    // Draw the line
    [path stroke];

    // Save the graphics state
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);

    // Create the triangle path
    UIBezierPath *trianglePath = [[UIBezierPath alloc] init];

    [trianglePath moveToPoint:CGPointMake(center.x, bounds.size.height / 5.0)];
    [trianglePath addLineToPoint:CGPointMake(bounds.size.width / 5.0, bounds.size.height / 1.2)];
    [trianglePath addLineToPoint:CGPointMake(bounds.size.width / 1.2, bounds.size.height / 1.2)];
    [trianglePath closePath];

    // Install trianglePath as a clipping path
    [trianglePath addClip];

    CGFloat locations[2] = { 0.0, 0.1 };
    CGFloat components[8] = { 0.0, 1.0, 0.0, 1.0,
    1.0, 1.0, 0.0, 1.0 };

    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorspace, components, locations, 2);

    CGPoint startPoint = CGPointMake(center.x, bounds.size.height / 5.0);
    CGPoint endPoint = CGPointMake(center.x, bounds.size.height / 0.2);
    CGContextDrawLinearGradient(currentContext, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorspace);

    CGContextRestoreGState(currentContext);

    // Create the drop shadow effect

    CGContextSetShadow(currentContext, CGSizeMake(8, 10), 3);

    // Draw the image
    UIImage *image = [UIImage imageNamed:@“Hypno@2x.png”];

    [image drawInRect:rect];

}

  • (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    NSLog(@"%@ was touched", self);

    // Get three random numbers between 0 and 1
    float red = (arc4random() % 100) / 100.0;
    float green = (arc4random() % 100) / 100.0;
    float blue = (arc4random() % 100) / 100.0;

    UIColor *randomColor = [UIColor colorWithRed:red
    green:green
    blue:blue
    alpha:1.0];

    self.circleColor = randomColor;
    [self setNeedsDisplay];
    }

@end
[/code]
Removed the setter for circleColor and synthesized it. I made sure to move [self setNeedsDisplay] into the touchesBegan method after changing circleColor’s value with the randomly generated value so it would be redrawn.

BNRHypnosisViewController.m

[code]#import “BNRHypnosisViewController.h”
#import “BNRHypnosisView.h”

@implementation BNRHypnosisViewController {
BNRHypnosisView *backgroundView;
}

  • (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 = @“Hypnotize”;

      // Create a UIImage from 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;
    

    }

    return self;

}

  • (void)loadView
    {
    // Create a view
    backgroundView = [[BNRHypnosisView alloc] init];

    // Set it as the view of this view controller
    self.view = backgroundView;
    }

  • (void)viewDidAppear:(BOOL)animated
    {
    UISegmentedControl *sControl = [[UISegmentedControl alloc] initWithItems:@[@“Red”, @“Green”, @“Blue”]];
    sControl.frame = CGRectMake(0, 400, 320, 31);
    sControl.backgroundColor = [UIColor whiteColor];
    [self.view addSubview];

    [sControl addTarget:self action:@selector(changeCircleColor:) forControlEvents:UIControlEventValueChanged];
    }

  • (IBAction)changeCircleColor:(UISegmentedControl *)segmentControl
    {
    if (segmentControl.selectedSegmentIndex == 0) {
    backgroundView.circleColor = [UIColor redColor];
    [backgroundView setNeedsDisplay];
    } else if (segmentControl.selectedSegmentIndex == 1) {
    backgroundView.circleColor = [UIColor greenColor];
    [backgroundView setNeedsDisplay];
    } else if (segmentControl.selectedSegmentIndex == 2) {
    backgroundView.circleColor = [UIColor blueColor];
    [backgroundView setNeedsDisplay];
    }
    }

@end[/code]
I made backgroundView an instance variable so that I could access it to change circleColor in the changeCircleColor method I created for use with the segmented control. I also created the segmented control here as well.


#2

Thanks for posting your solution. Mine was similar but didn’t quite work!


#3

Thanks to you I managed to do this challenge and more importantly, learned a lot, much appreciated. :smiley:


#4

What is the point of (IBAction) in changeColor method in BNRHypnosisViewController.m? I’m still a noob but I thought IBAction means it’s an action that you have to assign in Interface Builder but this class doesn’t have one and you made UISegmentedControl programmatically.