Silver challenge MVC solution


#1

Here is my solution to the silver challenge.
The advantage of this solution compared to some other posted on this forum is that it follows the MVC pattern.

The objective was to not create the UISegmentedControl in the controller. When I tried to create it in the view, I put the creation code in drawRect: but each time the view is redrawn, another UISegmentedControl is created :unamused:

I therefore found the following solution :

[ol][li]Create a xib file BNRHypnosisViewController.xib[/li]
[li]In interface builder, add a UIView[/li]
[li]In the inspector, change its class to BNRHypnosisView[/li]
[li]Change the File’s Owner class to BNRHypnosisViewController[/li]
[li]Connect the view and the File’s Owner (in both ways)[/li]
[li]Add a UISegmentedView with 3 segments named Red-Green-Blue, none of them selected[/li]
[li]Modify BNRHypnosisViewController to declare the outlet and action :

[code]#import “BNRHypnosisView.h”

@interface BNRHypnosisViewController : UIViewController

@property (nonatomic, weak) IBOutlet BNRHypnosisView* hypnosisView;

  • (IBAction)changeColor:(UISegmentedControl *)sender;

@end[/code]
[/li]
[li]Connect the action and the outlet in Interface Builder[/li]
[li]Modify BNRHypnosisView and put circleColor property in the header :

[code]@interface BNRHypnosisView : UIView

@property (strong, nonatomic) UIColor *circleColor;

@end[/code]
[/li]
[li]Modify BNRHypnosisViewController implementation : remove loadView: and implement the action :

[code]+ (NSArray *)colors
{
return @[[UIColor redColor], [UIColor greenColor], [UIColor blueColor]];
}

  • (IBAction)changeColor:(UISegmentedControl *)sender
    {
    self.hypnosisView.circleColor = [BNRHypnosisViewController colors][sender.selectedSegmentIndex];
    }[/code][/li][/ol]

That’s it !


#2

This is good. But after build and run, the BNRHypnosisView.circleColor is black. Some how BNRHypnosisView.initWithFrame: did not start BNRHypnosisView.circleColor with lightGrayColor. I tried to override BNRHypnosisView.init: but it did not work.

How do I get the XIB file to obey my initializer, to start with a gray circles?


#3

I got it. I have to use initWithCoder: because this is what the NIB uses to initialize an instance of BNRHypnosisView.

Add this code to BNRHypnosisView.m

- (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder]; if (self) { self.circleColor = [UIColor lightGrayColor]; } return self; }


#4

[quote=“AaronJonAlex”]I got it. I have to use initWithCoder: because this is what the NIB uses to initialize an instance of BNRHypnosisView.

Add this code to BNRHypnosisView.m

- (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder]; if (self) { self.circleColor = [UIColor lightGrayColor]; } return self; } [/quote]

I ended up with a slightly different approach. Since the controller is handling the colors, I simply made the controller fire the event for when the value of the segmented control changes:

-(void) viewWillAppear:(BOOL)animated { [self.colorPicker sendActionsForControlEvents:UIControlEventValueChanged]; }

Not sure if one way is better than the other, just a different approach.


#5

Ive been trying to get this to work but whenever I click the UISegment control it doesn’t update everything on the screen. I can see that setCircleColour is being called however [self setNeedsDisplay ]; is not making the screen update its content. Any help would be much appreciated

Nvm I figured it out I was missing the connection for the HypnosisView


#6

[quote=“domico”]Here is my solution to the silver challenge.
[list=1][]…
[
]Connect the view and the File’s Owner (in both ways)[/quote]

I can’t connect in both ways, only from File’s Owner to the view. When I ctrl-drag from the view to the File’s Owner, nothing appear on the File’s Owner as it should… why ?


#7

Adding XIB file makes everything easier BUT how do you connect the View with its controller? What domino hinted was very help but wasn’t clear explained. After you added XIB, you have three components: XIB, View, and Controller. To merge XIB and Controller together as one, click on custom class and select your controller.

Now add a view to your XIB’s canvas. Then you need to merge this view and your XIB as one component. So you specify your own custom class view

Also add the Segmented Control object to your view as well. Now you can map the canvas components to your code. Control+drag each component to your controller, such as the view, and the segmented control. The view has to be in @interface block while the other one can be in your normal @implementation block.

At the end, you should have Outlets properties from the File’s owner showing all the connections between the XIB, and the controller and the view.

You also need to move the circleColor from .m to .h because it’s like a private vs public kinda thing.