Learning iOS via the Robots And Pencils Academy

3 Comments

Hi all,

I am very excited to announce the launch of a new service me and my friend Michael have been working on.

It’s called The Robots and Pencils Academy and it’s basically screencasts for people who wanting to learn iOS.

As a recent student of iOS, I know how challenging it can be to get started.

New language.
New tools.
New technology.

It can be overwhelming.

This is the website I wish existed when I got started, and because we love sharing what we’ve learned with others, these screencasts are the result.

Getting started

If you are looking for a gentle introduction to Xcode or iOS development in general checkout:



If you just want to see what Objective-C looks like watch:



and a some others we recently created can be found here.

Training

We will also be offering two day bootcamps through the site. Next one is here in Calgary at the end of the month and another coming up in Winnipeg. You can read more about those courses here:

http://academy.robotsandpencils.com/training

Thank you for listening. Please share this with anyone you think may be interested.

Sincerely,

Jonathan & Michael

Objective-C ternary operator

1 Comment

int velocity;
if (isCar)
   velocity = 100;
else
   velocity = 10;

Is the same as:

int velocity = isCar ? 100 : 10;

Another handy NSLog:

NSLog(@"mondaySwitch: %@", (onOff ? @"ON" :@"OFF"));

How to create a new nib and ViewController

Leave a comment

1. Create a new xib.

2. Dress it up.

3. Create a new ViewController.

Make sure it’s a UIViewController.

4. Associate the view’s file owner with the new ViewController.

Click on the Yellow Cube File’s Owner.
Enter your new ViewController name on Identify Inspector.

5. Set File’s Owner outlet to View.

Click File’s Owner.
Click ‘Connections inspector’.
Drag ‘view’ to the white view square on left hand side.

6. Update AppDelegate.

Do this if you want to use this new xib as your root controller.

Here I changed the default ViewController to UIViewController.
And then set the xib name to the new xib ‘BarView’.

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UIViewController *viewController;
@end

AppDelegate.m

#import "BarViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.viewController = [[BarViewController alloc] initWithNibName:@"BarView" bundle:nil];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

Hit Run and Voila!

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

3 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

Leave a 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

How to UIWebView iOS

2 Comments

Here’s a simple walk through of how to display a web page in iOS.

Drag out a WebView onto your ViewController.

Create a property.

ViewController.h

@interface ViewController : UIViewController 
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end

Implement the UIWebViewDelegate

ViewController.h

@interface ViewController : UIViewController <UIWebViewDelegate>

Fix the autogenerated synthesize:

ViewController.m

@synthesize webView = _webView;

Make yourself the delegate and display your web page.

ViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.webView.delegate = self;
        
    NSURL *url = [NSURL URLWithString:@"http://google.com"];
    NSURLRequest *requestURL = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:requestURL];
}

Hit Run.

Can optionally add the following delegate methods if you want to kick it up a notch.

ViewController.m

#pragma mark - Optional UIWebViewDelegate delegate methods
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    return YES;
}

- (void)webViewDidStartLoad:(UIWebView *)webView
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

Here’s the source.

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UIWebViewDelegate>
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

@synthesize webView = _webView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.webView.delegate = self;
    
    NSURL *url = [NSURL URLWithString:@"http://google.com"];
    NSURLRequest *requestURL = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:requestURL];
}

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

#pragma mark - Optional UIWebViewDelegate delegate methods
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    return YES;
}

- (void)webViewDidStartLoad:(UIWebView *)webView
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

@end

Older Entries Newer Entries

Follow

Get every new post delivered to your Inbox.

Join 296 other followers

%d bloggers like this: