How to UIImagePickerController iPhone

Leave a comment

These are notes from Lecture 16 of Paul Hegarty’s iPhone App Dev course.

UIImagePickerController is just a modal view controller to get media from your camera of photo library.
Modal means you put it up with presentViewController:animated:completion

Usage
1. Create it with alloc/init and set delegate.
2. Configure it (source, kind of media, user editability).
3. Present it.
4. Respond to delegate method when user done picking media.

In this example we are going to allow ourselves to take a picture and put it in our KitchenSink view.

Create a button to trigger taking a picture with an outlet called addImage.

ViewController.m

- (IBAction)addImage:(UIBarButtonItem *)sender {

}

Then import the MobileCoreService library (click here for reminder on how to do this).

Note: UIImagePickerController is in fact a UINavigationController. And it has a property called delegate as well. It’s overloaded.

So when we implement the UIPickerControllerDelegate we also have to say we are a UINavigationControllerDelegate.

@interface KitchenSinkViewController()

Now do all the work to figure out what kind of device you are on, and what kind of pictures you can take:

- (IBAction)addImage:(UIBarButtonItem *)sender {
    
    // figure out our media types
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        NSArray *mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
        if ([mediaTypes containsObject:(NSString *)kUTTypeImage]) {
            // create our image picker
            UIImagePickerController *picker = [[UIImagePickerController alloc] init];
            picker.delegate = self;
            picker.sourceType = UIImagePickerControllerSourceTypeCamera;
            picker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeImage];
            picker.allowsEditing = YES;
            [self presentModalViewController:picker animated:YES];
        }
    }
}

Now need to implement the image picker delegate methods:

#define MAX_IMAGE_WIDTH 200

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
    if (!image) image = [info objectForKey:UIImagePickerControllerOriginalImage];
    if (image) {
        // keep the image view reasonable - reduce size
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        CGRect frame = imageView.frame;
        while (frame.size.width > MAX_IMAGE_WIDTH) {
            frame.size.width /= 2;
            frame.size.height /= 2;
        }
        imageView.frame = frame;
        [self setRandomLocationForView:imageView];
        [self.kitchenSink addSubview:imageView];
    }
    [self dismissImagePicker];
}

Can’t demo this because I build project against iPad and for some reason doesn’t work when I deploy to my iPhone.

How to import external libraries in XCode

10 Comments

Sometimes we need external libraries that don’t come by default with XCode (MobileCoreServices, MAPKit).

Here’s how to import them using XCode 4.2.1.

Click the blue folder at the top of your project:

Click the build phases tab button near the top

Expand the Link Binary with Libraries

Then add the libraries you want

Voila!

How to add an action sheet to your iPhone application

5 Comments

These are personal notes take from Lecture 16 of Paul Hegarty’s iPhone App Dev course.

Create a button and an outlet to your view controller to trigger the popping up of your action sheet.

Create a property for the action sheet (you pretty much want to do this for any popover as you will need to track whether it has already been created).

@property (weak, nonatomic) UIActionSheet *actionSheet;
@synthesize actionSheet = _actionSheet;

You can make the reference weak (would normally be strong for an outlet) because you only care if it exists or not.

Then in your button target create the action sheet, handling the case where the action sheet might already be created.

#define DO_SOMETHING_ELSE @"Do something else"

- (IBAction)clickActionSheet:(UIBarButtonItem *)sender {
    if (self.actionSheet) {
        // do nothing
    } else {
        UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Action sheet demo" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Do something else" otherButtonTitles:DO_SOMETHING_ELSE, nil];
        [actionSheet showFromBarButtonItem:sender animated:YES];
    }   
}

Implement the UIActionSheet interface:

@interface KitchenSinkViewController()  <UIActionSheetDelegate>

Then react on action sheet button click:

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSString *choice = [actionSheet buttonTitleAtIndex:buttonIndex];
    if (buttonIndex == [actionSheet destructiveButtonIndex]) {
        // destroy something
        NSLog(@"Destroy");
    } else if ([choice isEqualToString:DO_SOMETHING_ELSE]){
        // do something else
        NSLog(@"Do something else");
    }
}

Voila!

Couple of things to note about UIActionSheet

Special popover considerations: no cancel button
- action sheet in popover does not show cancel button
- doesn’t need because clicking outside popover dismisses it

Special popover considerations: the popovers passthrougViews
- if you showFromBarButtonItem:animated it adds the toolbar to popover’s passthroughViews
- this is annoying because repeated touches on the bar give multiple action sheets!
- something you just have to handle

Special popover considerations: bar button item handling
- have a weak @property in your class that oints to the UIActionSheet
- set it right after you show the action sheet
- Check that @property at the start of your bar button item’s action method.
- if it’s not-nil (since it is weak, it will only be non-nil if it’s still on screen), just dismiss it
- if it is nil, prepare and show your action sheet

Segue delegation example using iPhone – KitchenSink

Leave a comment

These are some notes on the KitchenSink example covered in Lecture 15 of Paul Hegarty’s iPhone course at Stanford. Go there to watch the video for the full story.

What’s going on

This example let’s a user click a button called ‘Add Label’ which then smooshes them off to a modal view controller where they are asked to enter the name of a label they would like to see appear.

This example shows how to make a model view controller (which is just a regular view controller with the transition set to ‘modal’ rather than ‘push’).

The more interesting bit is how the segue and delegate work.

Basically the ‘prepare for segue’ set’s things up that the view you are transitioning to needs. In our case we setup the question, and some answer text we’d like to display in the initial textfield box.

KitchenSinkViewController.m

-(void) prepareForSegue:(UIStoryboardPopoverSegue *)segue sender:(id)sender
{
    if ([segue.identifier hasPrefix:@"Create Label"]) {
        AskerViewController *asker = (AskerViewController *) segue.destinationViewController;
        asker.question = @"What do you want your label to say?";
        asker.answer = @"Label text";
        asker.delegate = self;
    }
}

Then in the view controller we are transitioning to, we read it off:

AskerViewController.m

-(void)viewDidLoad
{
    [super viewDidLoad];
    self.questionLabel.text = self.question;
    self.answerTextField.placeholder = self.answer; // preset in segue
    self.answerTextField.delegate = self;
}

To communicate back to the calling view controller we came from we use delegates. Asker sets up a protocol, which KitchenSink implements, and this is how to tell the caller what text the user entered in the textfield.

Then when the user finishing typing (or the textfield loses the caret) this gets called (because we implement UITextFieldDelegate).

-(void)textFieldDidEndEditing:(UITextField *)textField
{
    self.answer = textField.text;
    if (![textField.text length]) {
        [[self presentingViewController] dismissModalViewControllerAnimated:YES];
    } else {
        // need to communicate!
        [self.delegate askerViewController:self didAskQuestion:self.question andGotAnswer:self.answer];
    }
}

If the textfield is blank, just dismiss the dialog. Else send our delegate a message containing the text the user entered and they will dismiss the dialogue.

KitchenSinkViewController.m

-(void)askerViewController:(AskerViewController *)sender didAskQuestion:(NSString *)question andGotAnswer:(NSString *)answer
{
    // the user has typed something in the user view controller, got the answer, now we want to create our label
    [self addLabel:answer];
    [self dismissModalViewControllerAnimated:YES];
}

Here is the protocol we implement. And after grabbing the answer and creating a label, we dismiss the view controller ourselves.

That’s it! Great example of how to use delegates with model view controllers with a modal dialog thrown in.

How to add a spinner for long operation on iPhone

Leave a comment

Say you’ve got an operation that takes a long time and you don’t want to block the main thread.

Here’s how you can add a spinner to show the user an update is happening.

Hook up the refresh button

Control drag the refresh button into your custom view.

Then just hook up as follows:

ViewController.m

- (IBAction)refresh:(id)sender {

    // replace right bar button 'refresh' with spinner
    UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [spinner startAnimating];
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:spinner];

    // how we stop refresh from freezing the main UI thread
    dispatch_queue_t downloadQueue = dispatch_queue_create("flickr downloader", NULL);
    dispatch_async(downloadQueue, ^{
        NSArray *photos = [FlickrFetcher recentGeoreferencedPhotos];

        // do any UI stuff on the main UI thread
        dispatch_async(dispatch_get_main_queue(), ^{
            // put rightbar button back
            self.navigationItem.rightBarButtonItem = sender; // sender because that's the element that called us by clicking refresh
            self.photos = photos;
        });

    });
    dispatch_release(downloadQueue);

}

For the complete story on this demo checkout Lecture 10 52min mark of Paul Hegarty’s iPhone App Dev course.

iOS Objective-C Grand Central Dispatch

Leave a comment

These are personal notes from Lecture 10 of Paul Hegarty’s excellent iPhone App Dev course. Go there for the full monty on GCD you will want to go there.

What is Grand Central Dispatch.

Is a C API.
Basic idea is you have queues of operations.
Operations are specificed using blocks.
Most queues run serially (a true queue).

The operations are going to be pulled off the queue and run in some other thread.

There is no guarantee about where and when is will happen.
Good thing is that if your operation blocks, only that queue will block.
Other queues (like the main UI queue) will continue to run.

So we are going to queue up blocks things that take a long time to run in other threads, and from those threads we’ll queue up blocks back on the main ui thread to update the ui.

Example assume we fetched an image from the network:

-(void)viewWillAppear:(BOOL) animated
{
   NSData *imageData = [NSData dataWithContentsOfURL:networkURL]; // long time!
   UIImage *image = [UIImage imageWithData:imageData];
   self.imageView.image = image;
   self.imageView.frame = CGRectMake(0,0, image.size.width, image.size.height);
   self.scrollView.contentSize = image.size;
}

We don’t want the fetching of that image happening on the main queue. UI will be non-responsive.

And any rendering of the main ui, needs to be done on the main ui thread.

So we are going to need x2 queues.
One to do the long work of getting the image.
Another to update the UI.

-(void)viewWillAppear:(BOOL) animated
{

   dispatch_queue_t downloadQueue = dispatch_queue_create(“image loader”, NULL);
   dispatch_async(downloadQueue, ^{	
      NSData *imageData = [NSData dataWithContentsOfURL:networkURL]; // long time!
      dispatch_async(dispatch_get_main_queue(), ^ {
         UIImage *image = [UIImage imageWithData:imageData];
         self.imageView.image = image;
         self.imageView.frame = CGRectMake(0,0, image.size.width, image.size.height);
         self.scrollView.contentSize = image.size;
      });
   });
   dispatch_release(downloadQueue);
}

This is the power of GCD. You can think linearly about your program right in time. Run times linear here.

iOS Objective-C blocks

Leave a comment

These are some notes I took from Lecture 10 of Paul Hegarty’s excellent iPhone App Dev course. Go there for the full story.

What is a block?

Is a chunk of code that is usually inlined, but can be passed around like an argument to methods.
It’s very smart about local variables, referenced objects.

What does it look like?

Here’s a method I want to pass a block to:

[aDictionary enumerateKeysAndObjectUsingBlock:^(id key, id value, BOOL *stop) {
  NSLog(@"value for key %@ is %@", key, value);
  if (([@"ENOUGH" isEqualToString:key]) {
    *stop = YES;
  }
}];

This method exists in NSDictionary. It basically enumerates through each element in a Dictionary and allows you to pass it a block with will be applied on each element.

Block starts with magical caret ^. Means a block.

Can use local variables declared before the block inside the block

double stopValue = 53.5;
[aDictionary enumerateKeysAndObjectUsingBlock:^(id key, id value, BOOL *stop) {
  NSLog(@"value for key %@ is %@", key, value);
  if (([@"ENOUGH" isEqualToString:key] || ([value doubleValue] == stopValue) {
    *stop = YES;
  }
}];

But they are read only.
Unless you put underbar underbar – then it becomes read/write.

__block BOOL stoppedEarly = NO;
double stopValue = 53.5;
[aDictionary enumerateKeysAndObjectUsingBlock:^(id key, id value, BOOL *stop) {
    stoppedEarly = YES; // this is legal now
}];

What about objects messaged inside the block?

NSString *stopkey = [@"Enough uppercaseString];
...
if ([stopKey isEqualToString: key] ||

Any outside pointers are held onto strong by the block until the block goes off the heap.
So if the block needs it, it keeps it.

typedefs

To make our lives easier we sometimes user typedefs with blocks.

typedef double (^unary_operation_t)(double op);

_t is convention for telling us it’s a type (takes a double returns a double).

Now we can declare a variable of that type called square:

unary_operation_t square;
square = ^(double operand) { // value of square is a block
   return operand * operand;
}

Now we can use:

double sequareOfFive = square(5.0);

We could then use the unary_operation_t to define a method

@property (nonatomic, strong) NSMutableDictionary *unaryOperations;
typedef double (^unary_operation_t)(double op);
-(void) addUnaryOperation: (NSString *)op whichExecutesBlock:(unary_operation_t)opBlock {
   [self.unaryOperations setObject:opBlock forKey:op];
}

What this does is associate a block with an operation and store it in a dictionary.

Look at how beautifully these reads though:

addUnaryOperation (a string called op) whichExecutesBlock: unary_operation_t

Really nice syntax the way you can define variables right beside the variable declarations themselves.

Blocks are managed in iOS5 by ARC (so you don’t have to worry about memory management around these).

Then we could use this later on like:

-(double)performOperation:(NSString *)operation
{
   unary_operation_t unaryOp = [self.unaryOperations objectForKey:operation];
   if (unaryOp) {
      self.operand = unaryOp(self.operand);
   }
}

We don’t always typedef

When a block is an argument to a method and is used immediately, often there is no typedef. We just inline it.

-(void)enerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block;

Here block is the local variable name.

Some shorthand allowed when defining a block

1. You do not have to declare the return type if it can be inferred.
2. If there are no args don’t need parentheses.

NSNumber *secret = [NSNumber numberWithDouble:42.0];
[brain addUnaryOperation:@”MoLtUaE” whichExecutesBlock:^(double operand) {
   return operand * [secret doubleValue];
}];

And

[UIView animateWithDuration:5.0 animations:^{
   view.opactity = 0.5;
}];

No args to this block. No need to say ^() { …}

Memory Cycles (bad thing)

What if you had a block that referred to itself?

@property (nonatomic, strong) NSArray *myBlocks;

[self.myBlocks addObject:^() {
   [self doSomething];
}];

The block here has a strong pointer to itself.
But self has a strong point to block (through it’s property!).

This is a serious problem.

Neither self non block can ever escape the heap now because they will both be pointing to each other.
Called a memory “cycle”.

There is a magic underbar underbar weak.
Says this variable is weak.

__weak Myclass *weakSelf = self;
[self.myBlocks addObject:^() {
   [weakSelf doSomething];
}];

Solves the problem because now the block only has a weak pointer to self.
Self still has a strong point to the block, but that’s OK.

When do we use blocks in iOS?

Enumeration
View animations
Sorting
Notification (when something happens execute this block)
Error handlers
Completion handlers

But mostly it’s for multithreading. Do this using GCD (Grand Central Dispatch).

How to display image on iPhone using UIImageView and UIScrollView

14 Comments

These are abbreviated notes from the Lecture 8 Imaginarium demo of Paul Hegarty’s excellent iPhone course. For the full walk through go there.

After you’ve created a blank single view project with an ImaginariumViewController….

Drag an Image View into the ViewController view area.

Drag an image into your project.

Add image to display.

Note – it will automatically scale the image to fit the view (will change the aspect ratio).

Change to only show top left

Now the image aspect ratio is respected, but the image is so big we only start showing the top left. So now we’ll add a scroll bar.

Select the image view (use doc view pop out on right of story board if you need to make sure you have the right view).

And click Editor -> Embed In -> Scroll View

Note how imageView is now embedded in scrollView.

Our scroll view struts and springs are now out of wack.

Fix them so they expand and use the entire area.

If we run this now we’ll see our image but no scrolling.

ScrollView has to have it’s content size set.

This scroll view currently doesn’t know how big an area to scroll over. So we need to set it (set the content area, and then set the frame of the image view so it’s in the right spot).

Going to talk to these guys from our ViewController. Therefore we need outlets.

Note you can drag the outlets from the docView we saw earlier.
Call them imageView and scrollView and stick them in the private interface of the ViewController.

Note when you do this it will auto create the synthesize and it will also add itself to the ViewDidUnload() the low memory thing.

Now use viewDidLoad to set your content size (the size of the image) and it’s frame to be the full size.

ImaginariumViewController.m

-(void)viewDidLoad
{
    [super viewDidLoad];
    self.scrollView.contentSize = self.imageView.image.size;
    self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height);
}

Now when we run should work.

Bonus points – zoom

So how to we get this thing to zoom?

Set the min and max zoom on the scrollView.

Set the delegate.

@interface ImaginariumViewController()  

Implement the zoom method.

-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    // return which subview we want to zoom
    return self.imageView;
}

Now set ourselves as the delegate.

-(void)viewDidLoad
{
    [super viewDidLoad];
    self.scrollView.delegate = self;

Note you can also set the delegate entirely within the story board using the doc view by dragging the scrollView to the controller.

Should now be able to zoom.

Code in it’s entirety.

ImaginariumViewController.m

#import "ImaginariumViewController.h"

@interface ImaginariumViewController()  <UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@end

@implementation ImaginariumViewController
@synthesize imageView;
@synthesize scrollView;

-(void)viewDidLoad
{
    [super viewDidLoad];
    self.scrollView.delegate = self;
    self.scrollView.contentSize = self.imageView.image.size;
    self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height);
}

-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    // return which subview we want to zoom
    return self.imageView;
}

- (void)viewDidUnload {
    [self setImageView:nil];
    [self setScrollView:nil];
    [super viewDidUnload];
}
@end

How to iPhone protocol and delegate

1 Comment

This material is based on Paul Hegarty’s Lecture 5 and 6 iPhone course. For full explanation go there – these are abbreviated notes.

Say we have a View (FaceView) which knows how to draw a happy face but the level of it’s happiness (or smile) is currently hardwired to be 0 (not very happy).

And we’d like some externtal delegate to be able to set it for us.

Because Views can’t talk directly to controllers in iOS one way to do this is using protocols and delegates.

The view defines a protocol, which the controller implements thereby acting as the datasource for the view. Here’s how you hook it up.

1. Define the protocol and property.

FaceView.h

#import <UIKit/UIKit.h> 

@class FaceView;

@protocol FaceViewDataSource
- (float)smileForFaceView:(FaceView *)sender;
@end

@interface FaceView : UIView
@property (nonatomic, weak) IBOutlet id  dataSource;
@end

NB: Remember to synthesize the datasource in your .m file.

FaceView.m

@implementation FaceView
@synthesize dataSource = _dataSource;

What this basically says is create a protocol called smileForFaceView which returns a float (our smiliness) and takes as an input ourselves as the sender.

And then make that a publicly consumable property that others can register themselves for.

2. Use the delegate in your implementation.

For us our delegate is going to be used when we draw the smile in our drawRect function. So we replace the hard coded smile with:

FaceView.m

    //float smile = 100;
    float smile = [self.dataSource smileForFaceView:self];

3. Set controller as delegate.

So now FaceView is hooked up to use it’s delegate. Now we need to set our controller up to act as the delegate. This means having our controller say that they implement our FaceView protocol.

We could put in in our header file but because it’s private to our controller we stick it in our private interface.

HappinessViewController.m

@interface HappinessViewController() <FaceViewDataSource>

4. Do the implementation.

HappinessViewController.m

- (float)smileForFaceView:(FaceView *)sender {
    // set smile
}

5. Set delegate to be the controller.

Good time to do this is in the setter for our view in our controller.

HappinessViewController.m

-(void) setFaceView:(FaceView *)faceView
{
    _faceView = faceView;
    self.faceView.dataSource = self;
}

Voila! Now our controller is hooked up and ready to serve our view as it’s datasource.

Test

Add a button called refresh that when pressed sets the happiness.

- (IBAction)refresh:(id)sender {
    self.happiness = 100;
}

Should now see smile instead of frown (default of 0).

How segue in iOS and pass data from one ViewController to another

20 Comments

Say we have a ViewController (Doctor), and we want to segue to another (Happiness) when the user clicks a Happy or Sad button.

How could we do it?

Add segues.

While holding down the ‘control’ key we can drag from the buttons on the left MVC to the MVC on the right and select the ‘push’ model.

Give segues identifier names.

Assign each segue a name so we can tell one from the other (i.e Happy, Sad).

Add Navigation Controller.

If you ran this now it wouldn’t work. Segues only work when they are within some kind of navigation controller.

Click on the MVC on the left (the one that is going to start the application and go)

Editor -> Embed in -> Navigation Controller

If you run now you should see transitions when you click on Happy/Sad buttons.

Prepare for segue

To pass data to our new MVC we need to set stuff up in our prepareForSeque method.

DoctorViewController.m


#import "DoctorViewController.h"
#import "HappinessViewController.h"

@implementation DoctorViewController

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    NSLog(@"prepareForSegue: %@", segue.identifier);

    if ([segue.identifier isEqualToString:@"Happy"]) {
        [segue.destinationViewController setHappiness:100];
    } else if ([segue.identifier isEqualToString:@"Sad"]) {
        [segue.destinationViewController setHappiness:0];
    }
}

@end

In here the segue.destinationViewController points to the ViewController we are going to navigate to and it is of type ‘id’ (a special type in iOS objective C which could be any object).

Because we know where we are navigating to HappinessViewController we can treat segue.destinationViewController as if it were our HappinessViewController and call methods on it like setHappiness.

That’s how we pass data from one MVC to another. Through the segue by calling set property methods directly on our new ViewController.

And we can test that this gets passed by placing a log call in our HappinessViewController.

HappinessViewController.m

-(void)setHappiness:(int)happiness
{
    _happiness = happiness;
    NSString* result = [NSString stringWithFormat:@"%d", happiness];
    NSLog(@"Got happy %@", result);
    self.display.text = result; 
}

Now there’s one bug I haven’t been able to figure out yet. Which is how to refresh the view once it gets set here.

The self.display label is nil but once I figure it out I will come back up here and update.

For a much better walk through of who segues work check out Paul Hegarty’s Lecture 6 as part of the Stanford iPhone class series.

Older Entries

Follow

Get every new post delivered to your Inbox.

Join 323 other followers

%d bloggers like this: