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).

Older Entries

Follow

Get every new post delivered to your Inbox.

Join 294 other followers

%d bloggers like this: