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

Tips and tricks for working with Frank iOS acceptance testing framework

4 Comments

Learned a lot this week from former colleague Pete Hodgson on Frank (thank you).

Along the way I’ve picked up a few learning that make working with Frank easier.

How to programmatically add a accessibility label to a UIElement

Do this so Frank can easily find your element. Then add this line to your cellForRowAtIndexPath after your cell is created.

    cell.accessibilityLabel = [NSString stringWithFormat:@"CustomCell%d", indexPath.row]; // FOR FRANK!

How to select a customer UIElement

    touch( "view:'CustomTableCell' marked:'CustomTableCell0'" )

That’s all I’ve got for now. More later.

Links that help

http://testingwithfrank.com/supplied_steps.html

Frank Google Group

Getting started with Frank

1 Comment

Peter Hodgson has an excellent write up on how to get started with Frank.

http://blog.thepete.net/blog/2012/06/24/writing-your-first-frank-test/

He also has a short screen cast here to help get you going.

http://blog.thepete.net/blog/2012/06/16/lowering-franks-barrier-to-entry/

I am new to Frank. But by following these intros I was able to get the Olympic test app up and running. So start there, and things will probably work.

If however you do run into complications, see if any of these workarounds work for you.

“Error: No developer directory found at /Developer”?

If you get this error, run this command from the command line:

sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer

Unable to activate rspec-2.9.0, … conflicts with rspec-expectations (Gem::LoadError)

You get this error if you’be been doing some rails development and you have a different version of rspec setup in your rvm.

You can resolve this by creating a new gemset and then installing the latest rspec version as follows:

> rvm gemset list
> rvm gemset create frank
> rvm 1.9.3@frank
> gem install rspec

Note: you will have to substitute 1.9.3 with whatever version of ruby you are currently on:

> ruby -v

Make your app accessible

Follow these instructions here:

Can read about how to do that here.

Error loading /Library/ScriptingAdditions/Adobe Unit Types.osax

Follow Solution 1 on this page.

http://helpx.adobe.com/photoshop/kb/unit-type-conversion-error-applescript.html

More info

No architectures to compile for (ARCHS=armv6 armv7, VALID_ARCHS=i386)

I got this error when we made a lot of changes to our project file, and it wouldn’t work out of the box with the nice >frank build command :(

To work around this I had to manually frankify my app, and then run a build script as shown below:

1. Manually frankifying your app

Follow these instructions:

https://github.com/moredip/Frank/wiki/Frankifying-your-app-in-XCode-4

2. Add and run this build script.

Follow this instructions:

How to add a run build script to Xcode project

To run this build script:

echo APP_BUNDLE_PATH=\"$BUILT_PRODUCTS_DIR/$EXECUTABLE_NAME.app\" > $SRCROOT/Frank/features/support/bundle_path.rb

If everything works you should see your app on Symbiote here:

http://localhost:37265/

And you should be able to run the sample cucumber test from the command line like this:

cucumber Frank/features/my_first.feature

Discussion on Frank google group

Symbiote

Older Entries Newer Entries

Follow

Get every new post delivered to your Inbox.

Join 287 other followers

%d bloggers like this: