How to draw circles where ever someone taps your screen – iPhone, iPad, iOS

6 Comments

Here’s a quick example of how to detect where a user taps your screen, and then draw a circle at the point.

Step 1: Detect the touch event.

To detect touch events we are going to need our own UIView. We do all our work here and don’t need to touch the ViewController.

Drag out a generic UIView onto your ViewController.

Create a UIView subclass (i.e. MyView).

Associate the UIView with the subclass.

Then use this code to detect the touch event (you don’t need all this code, but I wanted you to see also how to detect double tap).

MyView.m

//---fired when the user finger(s) touches the screen---
-(void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
    //---get all touches on the screen---
    NSSet *allTouches = [event allTouches];

    //---compare the number of touches on the screen---
    switch ([allTouches count])
    {
        //---single touch---
        case 1: {
            
            //---get info of the touch---
            UITouch *touch = [[allTouches allObjects] objectAtIndex:0];
            CGPoint point = [touch locationInView:self];
            NSLog(@"x=%f", point.x);
            NSLog(@"y=%f", point.y);

            //---compare the touches---
            switch ([touch tapCount])
            {
                //---single tap---
                case 1: {
                    NSLog(@"Single tap");
                } break;
 
                //---double tap---
                case 2: {
                    NSLog(@"Double tap");

                } break;
                    
            }
            
        }  break;
    }
}

If you run this, you should now see output in the console showing you where you’ve tapped.

Step 2: Draw the circle.

To draw a circle we will create a property for the tap point (myPoint) and then trigger a redraw by calling setNeedsDisplay when the property value changes.

MyView.m

@interface MyView()
@property (nonatomic) CGPoint myPoint;
@end

@implementation MyView

@synthesize myPoint = _myPoint;

- (void)setMyPoint:(CGPoint)myPoint
{
    _myPoint = myPoint;
    [self setNeedsDisplay];
}

This is important because we never want to call drawRect directly (there’s stuff that needs to be coordinated and handled with a redraw and it’s better to let Cocoa handle this).

Then we just need to set our property when based on where they tapped.

...
NSLog(@"x=%f", point.x);
NSLog(@"y=%f", point.y);
self.myPoint = point;
...

Now all we need is a drawRect method to draw a circle at our point.

- (void)drawRect:(CGRect)rect
{
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(contextRef, 2.0);
    CGContextSetRGBFillColor(contextRef, 0, 0, 1.0, 1.0);
    CGContextSetRGBStrokeColor(contextRef, 0, 0, 1.0, 1.0);
    CGRect circlePoint = (CGRectMake(touchPos.x, touchPos.y, 10.0, 10.0));

    CGContextFillEllipseInRect(contextRef, circlePoint);
}

Run that – and voila. Lovely blue circles appearing where you user clicks.

Here is the code in it’s entirety (it’s just one class – MyView).

MyView.m

#import "MyView.h"

@interface MyView()
@property (nonatomic) CGPoint myPoint;
@end

@implementation MyView

@synthesize myPoint = _myPoint;

- (void)setMyPoint:(CGPoint)myPoint
{
    _myPoint = myPoint;
    [self setNeedsDisplay];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

//---fired when the user finger(s) touches the screen---
-(void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
    //---get all touches on the screen---
    NSSet *allTouches = [event allTouches];
    
    //---compare the number of touches on the screen---
    switch ([allTouches count])
    {
            //---single touch---
        case 1: {
            
            //---get info of the touch---
            UITouch *touch = [[allTouches allObjects] objectAtIndex:0];
            CGPoint point = [touch locationInView:self];
            NSLog(@"x=%f", point.x);
            NSLog(@"y=%f", point.y);
            
            self.myPoint = point;
            
            //---compare the touches---
            switch ([touch tapCount])
            {
                    //---single tap---
                case 1: {
                    NSLog(@"Single tap");
                } break;
                    
                    //---double tap---
                case 2: {
                    NSLog(@"Double tap");
                    
                } break;
                    
            }
            
        }  break;
    }
}

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(ctx, 2.0);
    CGContextSetRGBFillColor(ctx, 0, 0, 1.0, 1.0);
    CGContextSetRGBStrokeColor(ctx, 0, 0, 1.0, 1.0);
    CGRect circlePoint = CGRectMake(self.myPoint.x, self.myPoint.y, 50.0, 50.0);
    
    CGContextFillEllipseInRect(ctx, circlePoint);
}

@end

How to pass data from one MVC to another using Objective-C protocols

1 Comment

Here I’ve got a MVC on the left, wanting to know the value of the selected date from the MVC on the right.

Here’s how you create/define a protocol (DatePicker) for the MVC on the right, and then assign yourself as the delegate for the MVC on the left.

Define the protocol and create a delegate property for it:

DatePickerViewController.h

#import "ViewController.h"

@protocol DatePickerDelegate <NSObject>
- (void)didPickDateWithSelectedDate:(NSDate *)selectedDate;
@end

@interface DatePickerViewController : UIViewController
@property (nonatomic, weak) id <DatePickerDelegate> delegate;
@end

Synthesize your delegate, and then call it when you want the call back to happen.

DatePickerViewController.m

@synthesize delegate = _delegate;

- (IBAction)datePicked:(UIDatePicker *)sender
{
    [self.delegate didPickDateWithSelectedDate:sender.date];
}

With our protocol/delegate created and designed, we are now ready to switch the the other side, and register ourselves for the callback for the MVC on the left.

First we need to say we implement the protocol interface.

ViewController.m

@interface ViewController () <DatePickerDelegate>

Then we need to actually implement it.

- (void)didPickDateWithSelectedDate:(NSDate *)selectedDate
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"HH:mm"];
    NSString *formattedDateString = [dateFormatter stringFromDate:selectedDate];
    
    self.dateLabel.text = formattedDateString;
    NSLog(@"didPickAlarmTimeWithSelectedDate: %@", formattedDateString);
}

Then we need to register ourselves to act as the delegate for the DatePickerMVC on the right. A good place to do this is in the prepareForSegue method.

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"dateSegue"]) {
        UIViewController *newController = segue.destinationViewController;
        DatePickerViewController *dateVC = (DatePickerViewController *) newController;
        dateVC.delegate = self;
    }
}

Note for this to work the segue needs an id “dateSegue” which you set by selecting the segue in your story board and assigning it the value.

Run it. And Voila! You got one MVC passing data to another via protocols.

Here is the code in it’s entirety.

DatePickerViewController.h

#import "ViewController.h"

@protocol DatePickerDelegate <NSObject>
- (void)didPickDateWithSelectedDate:(NSDate *)selectedDate;
@end

@interface DatePickerViewController : UIViewController
@property (nonatomic, weak) id <DatePickerDelegate> delegate;
@end

DatePickerViewController.m

#import "DatePickerViewController.h"

@interface DatePickerViewController ()

@end

@implementation DatePickerViewController

@synthesize delegate = _delegate;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (IBAction)datePicked:(UIDatePicker *)sender
{
    [self.delegate didPickDateWithSelectedDate:sender.date];
    NSLog(@"datePicked: %@", sender.date);
}

@end

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UILabel *dateLabel;
@end

ViewController.m

#import "ViewController.h"
#import "DatePickerViewController.h"

@interface ViewController () <DatePickerDelegate>

@end

@implementation ViewController
@synthesize dateLabel;

- (void)viewDidLoad
{
    [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setDateLabel:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (void)didPickDateWithSelectedDate:(NSDate *)selectedDate
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"HH:mm"];
    NSString *formattedDateString = [dateFormatter stringFromDate:selectedDate];
    
    self.dateLabel.text = formattedDateString;
    NSLog(@"didPickAlarmTimeWithSelectedDate: %@", formattedDateString);
}

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"dateSegue"]) {
        UIViewController *newController = segue.destinationViewController;
        DatePickerViewController *dateVC = (DatePickerViewController *) newController;
        dateVC.delegate = self;
    }
}

@end

How to create an Objective-C category

1 Comment

What

Categories are objective-c’s way of allowing you to add methods to an existing class without having to touch it’s source.

For example, say we wished had a method on NSString that decorated our string with some pretty output.

- (NSString *) decorate
{
    return [NSString stringWithFormat:@"=== %@ ===", self];;
}

And we wished we could call it directly on NSString like this:

NSLog(@"%@", @"Booya".decorate);

Outputs:

=== Booya ===

Categories allow us to do that. Here’s how.

How

The format of a category is the ClassName you are extending, followed by the name you want to give your category (i.e. “Utils”).

#import "ClassName.h"

@interface ClassName ( CategoryName )
// method declarations
@end

To make a new category in Xcode we basically create a new class. Go:

New File (Command + N).
Select ‘Objective-C category’.

Specify the class you want to add a category onto (i.e. NSString).

Then add your category method to your .h file.

#import <Foundation/Foundation.h>

@interface NSString (Utils)
- (NSString *) decorate;
@end

And your implementation to you .m file.

#import "NSString+Utils.h"

@implementation NSString (Utils)
- (NSString *) decorate
{
    return [NSString stringWithFormat:@"=== %@ ===", self];;
}
@end

Now you can go to the class where you want to use the extension, import your category header and make your method call.

#import "NSString+Utils.h"

- (IBAction)tapMePressed:(id)sender
{
    NSLog(@"%@", @"Booya".decorate);
}

That’s it!

Links that help

Apple documentation on category

NSPredicate example

Leave a comment

Say I’ve got an NSArray of Reservations objects and I would like to filter out all the ones with a certain property, like

reservationType = Event;

Here’s how you’d do it using an NSPredicate:

NSPredicate *eventPredicate = [NSPredicate predicateWithFormat:@"promoType = 'Event'"];
NSArray *collector = [self.reservations filteredArrayUsingPredicate:eventPredicate];

Voila! All your reservations should now only be those of type ‘Event’.

Note 1: Single quotes around the word ‘Event’ in the predicate.

Note 2: If you pull the keyword out like so:

NSString *const kPromoTypeEvent = @"Event";

remove the single quotes in the predicate like so:

[NSPredicate predicateWithFormat:@"promoType = %@", kPromoTypeEvent]

More predicate examples:

@"salary >= 100000"
@"name BEGINSWITH 'T'"

https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html

How to add a custom UITableViewCell to a xib file objective-c

14 Comments

Sometimes the standard UITableViewCells don’t cut it and you need to create your own.

Here’s how you can create your own UITableViewCell in Xcode and format it however your like.

1. Create UITableView xib file.

After creating a xib project, and dragging in a UITableView

create a new xib file for your UITableViewCell

and call it CustomCell.xib.

Delete the view that it gives you and instead drag out a ‘Table View Cell’ from the utilities area.

Now, it’s in here where we are free to do whatever kind of layout we want. In this case I am going to layout two labels in the middle on top of each other, like this:

2. Create a custom class.

I am going to need some code to back this xib file up, so next we create an objective-c class that extends UITableViewCell.

and I am going to give it the same name as my xib – CustomCell.

3. Tie class to xib.

Now to we need to connect this class to the xib. We do by clicking on our cell xib Identity Inspector and typing in our class name as below:

4. Create some properties.

With those two connected, we now need to create some properties for the elements in our UITableViewCell xib. Do this like you would any other property, by control dragging the elements from the xib to the custom class.

Note: I also added a reuseIdentifier class method here with an implementation that looks like this:

CustomCell.m

+ (NSString *)reuseIdentifier {    
    return @"CustomCellIdentifier";
}

We’ll use this later.

5. Tie it to the parent class.

Now we need to associate this xib with the parent VC xib that is going to use it. Do that by clicking the yellow cube on the left called ‘Files Owner’, the ‘Inspector’ tab in utilities, and then typing the VC file name (in this case I just used the default VC creating for me with the initial project).

6. Create a property for the cell in the parent.

In the ViewController with the UITableView that is going to be using this cell, add property pointing to it like so:

ViewController.h

#import <UIKit/UIKit.h>
#import "CustomCell.h"

@interface ViewController : UIViewController
@property (assign, nonatomic) IBOutlet CustomCell *customCell;

@end

ViewController.m

@synthesize customCell = _customCell;

Do this by bringing up your xib and the assistant for it, and dragging the cell over like any other control.

7. Connect this property to the cell.

This part is a bit weird, but now we go back to our custom cell xib, and connect our cell to the parent property in the View Controller.

Do this by clicking that ‘Files Owner’ yellow cube on the side, and then the ‘Connections Inspector’ circle with an arrow in it on the right. You should now see the customCell property we just defined in the parent VC.

Click on the little grey circle beside the customCell in the outlets section, and drag it to the cell. This connects the cell to the parent property.

8. Add a UITableView property to VC.

ViewController.h

@property (strong, nonatomic) IBOutlet UITableView *tableView;

ViewController.m

@synthesize tableView = _tableView;

9. Hook up UITableView datasource and delegate.

Last but not least we need to tell our UITableView that our ViewController is going to act as it’s datasource and delegate.

Go back to the ViewController xib, select the UITableView element, and from the ‘Connections Inspector’ drag those little circles out again beside dataSource and delegate to the yellow cube ‘Files Owner’ (not the UITableView!).


You will know you have it hooked up right when you see ‘File’s Owner’ beside the dataSource delegate outlets.

10. Implement the datasource delegate methods.

In your ViewController, implement the necessary UITableView datasource delegate methods.

ViewController.m

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    
    CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:[CustomCell reuseIdentifier]];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
        cell = _customCell;
        _customCell = nil;
    }        
    
    cell.topLabel.text = @"I am on top";
    cell.bottomLabel.text = @"and I'm on the bottom";
    
    return cell;     
}

10.b. Set cell reuse identifier

Update

As Ryan pointed out down in the comments section, we need to set the reuse identifier on our UITableViewCell, so it will get pooled and reused propertly in the pool.

Click on your table view cells, go to the attributes inspector and make sure the text you enter there matches the identifier in your UITableCell class. Else as Eric points out, our cells won’t get reused properly.

11. Fire it up!

Run the app in the simulator and you should now see something like this:



I also added one line to the viewDidLoad() to adjust for row height:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // make row height same as our xib to get 
    // rid of the appearance of multpile rows
    self.tableView.rowHeight = 125;
}

Big thanks to Jeremy Gale for showing me the in’s and out’s of hooking up UITableViewCells to xibs.

Here’s a complete listing of files:

CustomCell.h

#import <UIKit/UIKit.h>

@interface CustomCell : UITableViewCell
+ (NSString *)reuseIdentifier;
@property (strong, nonatomic) IBOutlet UILabel *topLabel;
@property (strong, nonatomic) IBOutlet UILabel *bottomLabel;
@end

CustomCell.m

#import "CustomCell.h"

@implementation CustomCell
@synthesize topLabel;
@synthesize bottomLabel;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

+ (NSString *)reuseIdentifier {    
    return @"CustomCellIdentifier";
}

@end

ViewController.h

#import "CustomCell.h"

@implementation CustomCell
@synthesize topLabel;
@synthesize bottomLabel;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

+ (NSString *)reuseIdentifier {    
    return @"CustomCellIdentifier";
}

@end

ViewController.m


#import "ViewController.h"
#import "CustomCell.h"

@interface ViewController ()

@end

@implementation ViewController

@synthesize customCell = _customCell;
@synthesize tableView = _tableView;

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    
    CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:[CustomCell reuseIdentifier]];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
        cell = _customCell;
        _customCell = nil;
    }        
    
    cell.topLabel.text = @"I am on top";
    cell.bottomLabel.text = @"and I'm on the bottom";
    
    return cell;     
}


- (void)viewDidLoad
{
    [super viewDidLoad];
    // make row height same as our xib to get 
    // rid of the appearance of multpile rows
    self.tableView.rowHeight = 125;
}

- (void)viewDidUnload
{
    [self setTableView:nil];
    [super viewDidUnload];
}

@end

Bonus – how to create a new xib and ViewController

1. Create new xib.

2. Create new ViewController class.

3. Drag out display elements onto xib.

4. Connect the File Owner to the view.

We want the File Owner view outlet to be connected to the xib view.
Do this by clicking view owner, clicking the grey circle beside view, and then dragging the circle to the view on screen.

5. Connect display elements to File’s Owner

You need to connect each display element in your xib to your parent ViewController.

This is done by clicking the yellow cube ‘File’s Owner’ icon and dragging little grey circle beside it’s property to the yellow cube line.

When you are done you should be able to mouse over each outlet and see the corresponding element light up in your xib.

How to pragmatically segue from one view to another with button press from storyboard iOS

1 Comment

Usually in iOS we setup buttons in our ViewControllers, and then segue to new ViewControllers by clicking and control dragging the button to the new ViewController we want to transition to.

But what if you add a custom button to your UI, and you no longer have the the ability to control drag to your new ViewController. How do you tell the button which view to pop up?

Answer: Instantiate the view from the story board, and pragmatically pop your view controller from there.

Here’s where I manually at my own button to the NavBar:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:@"Settings" style:UIBarButtonItemStylePlain target:self action:@selector(gotoAlarmSettings)];
    self.navigationItem.rightBarButtonItem = anotherButton;

}

And then here is how you instantiate a view from the storyboard:

- (IBAction)gotoAlarmSettings {
    AlarmViewController *alarmView = [self.storyboard instantiateViewControllerWithIdentifier:@"foobar"];
    [self.navigationController pushViewController:alarmView animated:YES];
}

Note: You set the identifier for your viewController in the story board (else they won’t connect).

 

If you don’t do it this way, and you instead just popup it up regularly like this:

    AlarmViewController *alarmView = [[AlarmViewController alloc] init];

The transition will work, but you will get a black screen for your view.

Simple objective-c inheritance example

Leave a comment

Here a simple example of how inheritance works in objective-c (based on this older example).

1. Define the parent class.

Rectangle.h

#import <Foundation/NSObject.h>

@interface Rectangle: NSObject 

@property (nonatomic) int width;
@property (nonatomic) int height;

-(Rectangle*) initWithWidth: (int) w height: (int) h;
-(void) setWidth: (int) w height: (int) h;
-(void) print;

@end

Rectangle.m

#import "Rectangle.h"
#import <stdio.h>

@implementation Rectangle

@synthesize width = _width;
@synthesize height = _height;

-(Rectangle*) initWithWidth: (int) w height: (int) h {
    self = [super init];
    
    if ( self ) {
        [self setWidth: w height: h];
    }
    
    return self;
}

-(void) setWidth: (int) w height: (int) h {
    self.width = w;
    self.height = h;
}

-(void) print {
    printf( "width = %i, height = %i", self.width, self.height );
}

@end

2. Inherit from the parent.

Here we inherit our behavior from the parent with this line:

@interface Square: Rectangle


<strong>Square.h</strong>


#import "Rectangle.h"

@interface Square: Rectangle
-(Square*) initWithSize: (int) s;
-(void) setSize: (int) s;
-(int) size;
@end

And add our own customer behaviour:

Square.m

#import "Square.h"

@implementation Square
-(Square*) initWithSize: (int) s {
    self = [super init];
    
    if ( self ) {
        [self setSize: s];
    }
    
    return self;
}

-(void) setSize: (int) s {
    self.width = s;
    self.height = s;
}

-(int) size {
    return self.width;
}

@end

You can see here we inherit the Rectangle’s setWidth setHeight and print methods while providing our own setSize implementation.

3. Run it.

SpikeInheritanceTests.m

#import "SpikeInheritanceTests.h"
#import "Square.h"
#import "Rectangle.h"
#import <stdio.h>

@implementation SpikeInheritanceTests

- (void)testExample
{
    Rectangle *rec = [[Rectangle alloc] initWithWidth: 10 height: 20];
    Square *sq = [[Square alloc] initWithSize: 15];
    
    // print em
    printf( "Rectangle: " );
    [rec print];
    printf( "\n" );
    
    printf( "Square: " );
    [sq print];
    printf( "\n" );
    
    // update square
    [sq setWidth: 20];
    printf( "Square after change: " );
    [sq print];
    printf( "\n" );
}

@end

If all goes well output should look something like this:

Simple objective-c protocol example

9 Comments

Wanting to understand how Java/C# interfaces are implemented in objective-c, I created this example based on the one I found here.

1. Create the protocol.

Interfaces in objective-c are called protocols. Here’s a protocol for printing.

Printing.h

#import <Foundation/Foundation.h>

@protocol Printing
-(void) print;
@end

@interface Printing : NSObject

@end

The protocol or interface (not be be confused with the other interface in this file) defines a single method – print.

2. Implement the protocol.

We’re going to implement this protocol two ways. One with a fraction and another with a complex number.

Fraction.h

#import <Foundation/NSObject.h>
#import "Printing.h"

@interface Fraction: NSObject <Printing> 

@property (nonatomic) int numerator;
@property (nonatomic) int denominator;

-(Fraction*) initWithNumerator: (int) n denominator: (int) d;

@end

To say that we implement the protocol we put the protocol in after the objective-c interface definition.

@interface Fraction: NSObject <Printing> 

Then we do the actual implementation in the .m file.

Fraction.m

#import "Fraction.h"
#import <stdio.h>

@implementation Fraction

@synthesize numerator = _numerator;
@synthesize denominator = _denominator;

-(Fraction*) initWithNumerator: (int) n denominator: (int) d {
    self = [super init];
    
    if ( self ) {
        self.numerator = n;
        self.denominator = d;
   }
    
    return self;
}

-(void) print {
    printf( "%i/%i", self.numerator, self.denominator );
}

@end

Complex.h

#import <Foundation/NSObject.h>
#import "Printing.h"

@interface Complex: NSObject <Printing> 

@property (nonatomic) double real;
@property (nonatomic) double imaginary;

-(Complex*) initWithReal: (double) r andImaginary: (double) i;

@end

Complex.m

#import "Complex.h"
#import <stdio.h>

@implementation Complex

@synthesize real = _real;
@synthesize imaginary = _imaginary;

-(Complex*) initWithReal: (double) r andImaginary: (double) i {
    self = [super init];
    
    if ( self ) {
        self.real = r;
        self.imaginary = i;
    }
    
    return self;
}

-(void) print {
    printf( "%_f + %_fi", self.real, self.imaginary );
}

@end

3. Test.

Then to use the protocol as a fraction or a complex number we do the following:

SpikeProtocolTests.m

#import "SpikeProtocolTests.h"
#import "Fraction.h"
#import "Complex.h"

@implementation SpikeProtocolTests

- (void)testExample
{
    Fraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
    Complex *comp = [[Complex alloc] initWithReal: 5 andImaginary: 15];
  
    id <Printing> printable;
    
    // print it
    printable = frac;
    printf( "The fraction is: " );
    [printable print];
    printf( "\n" );
    
    // print complex
    printable = comp;
    printf( "The complex number is: " );
    [printable print];
    printf( "\n" );
}

@end

The magic pretty much happens here:

    Fraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
    Complex *comp = [[Complex alloc] initWithReal: 5 andImaginary: 15];
  
    id <Printing> printable;

We define our objects (which implement the interface) and a variable id that implements the interface. The reason this object is of type id is so we can implement multiple interfaces (id is a reserved object type in objective-c meaning any object).

Then to switch implementations we just change implementations:

 printable = frac;
 printable = comp;

The output should look something like this:

For instructions on how to see the unit test output in Xcode look here.

%d bloggers like this: