Official (secret) iOS sales figures: iPhone, iPodTouch, iPad

One of the side-effects of lititgation – especially over patents – is that parties are often required to make info public on how much they “may have infringed”. i.e. … exact sales figures. For most companies, that’s fine – this is info they were already sharing / boasting about publically. Apple is way too secretive to do that normally, so the figures from Apple vs Samsung (April 2011) are particularly revealing.

As of March 2011, according to Apple’s legal filings:

  • iPhone: 108m sold
  • iPod Touch: 60m sold
  • iPad: 19m sold

The linked article goes into a great blow-by-blow analysis of the lawsuit, including compare/contrast examples of the icons that Apple is claiming are trademark infringement.

Mac OS X: custom background for your app-window

I found this neat little technique: http://parmanoir.com/Custom_NSThemeFrame … for drawing a custom image to the background of your Application Window.

Here’s a screenshot of the technique applied to Regular Expression Helper (currently in submission, waiting for Apple to approve it for the App Store):

Things of note:

  1. The author was inspired by Apple’s own apps – recently, Apple has been doing this more and more, even though I’m sure it’s against their own Human Interface Guidelines … so I’m assuming they’ll have no complaints about you doing this in the App Store
  2. This covers your ENTIRE background – it overwrites the default titlebar, overwrites the default bottom bar, everything
  3. The author uses method swizzling to make this work *without* creating custom classes, and *without* altering Apple’s own system classes. That’s very neat: you can comment out a single line of code to enable/disable the customization

For my last OS X app – Regular Expressions Helper – Brett made an excellent icon. As required by Apple’s App Store policies, we’ve got it in all sizes up to 512×512. So, I wanted to use that as the background to the app. This needed some tweaks.

Also, the Parmanoir site skipped over some minor points (all obvious when you read the source code they provided), but for future reference, here’s my changes:

Code requirements

You need to import objc/runtime.h for method swizzling to work – a little surprising, considering you’d expect this to be part of the base stuff pre-imported by NSObject et al. No matter.

Complication: Xcode4 auto-complete is buggy on the objc directory – it displays it as a file, and attempts to autocomplete:

#import  // FAIL...

So, just remember to type it manually (or copy/paste this):

#import  // Correct

Loading your application icons (.icns file) as a background image

If you load an NSImage directly, using the standard method from iOS:

	NSImage* backgroundImage = [NSImage imageNamed:@"image-name"]; // DONT DO THIS

…then you’ll discover a slightly irritating feature of OS X v10.6: it loads a 32×32 pixel version of the image *no matter how big the image is*. This is well documented, but it’s easy to overlook, especially coming from iOS, where “imageNamed” is the method you *always* use, to take advantage of it’s built-in caching.

Instead, if you simply use a different init method, you get the “maximal resolution” version of the image:

	NSImage* backgroundImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Regular Expressions Icons" ofType:@"icns" ]];

(note: this is loading an .icns file – the one that Apple requires you to create, and requires you to put a 512×512 image into – rather than, say, a PNG file. That makes things easier to maintain: just one image file to update ;) )

Copy/pasteable final custom drawRect method

For sheer convenience, here’s the default basic method for doing background-image rendering from icns, with the alpha scaled down so you can see the image more clearly:

-(void) customDrawRect:(NSRect) rect
{
	// Call original drawing method
	[self drawRectOriginal:rect];
		
	//
	// Build clipping path : intersection of frame clip (bezier path with rounded corners) and rect argument
	//
	NSRect windowRect = [[self window] frame];
	windowRect.origin = NSMakePoint(0, 0);
	
	//
	// Draw background image (extend drawing rect : biggest rect dimension become's rect size)
	//
	NSRect imageRect = windowRect;
	if (imageRect.size.width > imageRect.size.height)
	{
		imageRect.origin.y = -(imageRect.size.width-imageRect.size.height)/2;
		imageRect.size.height = imageRect.size.width;
	}
	else
	{
		imageRect.origin.x = -(imageRect.size.height-imageRect.size.width)/2;
		imageRect.size.width = imageRect.size.height;
	}
	
	NSImage* backgroundImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Regular Expressions Icons" ofType:@"icns" ]];
	
	[backgroundImage drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeSourceAtop fraction:0.55];
}

iOS4: Take photos with live video preview using AVFoundation

I’m writing this because – as of April 2011 – Apple’s official documentation is badly wrong. Some of their source code won’t even compile (typos that are obvious if they’d checked them), and some of their instructions are hugely over-complicated and yet simply don’t work.

This is a step-by-step guide to taking photos with live image preview. It’s also a good starting point for doing much more advanced video and image capture on iOS 4.

What are we trying to do?

It’s very easy to write an app that takes photos. It’s quite a lot of code, but it’s been built-in to iOS/iPhone OS for a few years now – and it still works.

But … with iOS 4, the new “AV Foundation” library offers a much more powerful way of taking photos, which lets you put the camera view inside your own app. So, for instance, you can make an app that looks like this:

Gotchas

0. Requires a 3GS, iPod Touch 3, or better…

The entire AV Foundation library is not available on the oldest iPhone and iPod Touch devices. I believe this is because Apple is doing a lot of the work in hardware, making use of features that didn’t exist in the original iPhone chips, and the 3G chips.

Interestingly, the AV Foundation library *is* available on the Simulator – which suggest that Apple certainly *could* have implemented AV F for older phones, but they decided not to. It’s very useful that you can test most of your AV F app on the Simulator (so long as you copy/paste some videos into the Simulator to work with).

1. Apple doesn’t tell you the necessary Frameworks

You need *all* the following frameworks (all come with Xcode, but you have to manually add them to your project):

  1. CoreVideo
  2. CoreMedia
  3. AVFoundation (of course…)
  4. ImageIO
  5. QuartzCore (maybe)

How do we: get live video from camera straight onto the screen?

Create a new UIViewController, add its view to the screen (either in IB or through code – if you don’t know how to add a ViewController’s view, you need to do some much more basic iPhone tutorials first).

Add a UIView object to the NIB (or as a subview), and create a @property in your controller:

@property(nonatomic, retain) IBOutlet UIView *vImagePreview;

Connect the UIView to the outlet above in IB, or assign it directly if you’re using code instead of a NIB.

Then edit your UIViewController, and give it the following viewDidAppear method:

-(void) viewDidAppear:(BOOL)animated
{
	AVCaptureSession *session = [[AVCaptureSession alloc] init];
	session.sessionPreset = AVCaptureSessionPresetMedium;
	
	CALayer *viewLayer = self.vImagePreview.layer;
	NSLog(@"viewLayer = %@", viewLayer);
	
	AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
	
	captureVideoPreviewLayer.frame = self.vImagePreview.bounds;
	[self.vImagePreview.layer addSublayer:captureVideoPreviewLayer];
	
	AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
	
	NSError *error = nil;
	AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
	if (!input) {
		// Handle the error appropriately.
		NSLog(@"ERROR: trying to open camera: %@", error);
	}
	[session addInput:input];
		
	[session startRunning];
}

Run your app on a device (NB: this will NOT run on Simulator – Apple doesn’t support cameras on the simulator (yet)), and … you should see the live camera view appearing in your subview

Gotchas

2. Apple’s example code for live-video doesn’t work

In the AVFoundation docs, Apple has a whole section on trying to do what we did above. Here’s a link: AV Foundation Programming Guide – Video Preview. But it doesn’t work.

UPDATE: c.f. Robert’s comment below. This method does work, you just have to use it in a different way.

“The method “imageFromSampleBuffer” does work when you send a sample buffer from “AVCaptureVideoDataOutput” which is “32BGRA”. You tried to send a sample buffer from “AVCaptureStillImageOutput” which is “AVVideoCodecJPEG”.”

(more details + source code in Robert’s comment at the end of this post)

If you look in the docs for AVCaptureVideoPreviewLayer, you’ll find a *different* source code example, which works without having to change codecs:

captureVideoPreviewLayer.frame = self.vImagePreview.bounds;
[self.vImagePreview.layer addSublayer:captureVideoPreviewLayer];

3. Apple’s image-capture docs are also wrong

In the AV Foundation docs, there’s also a section on how to get Images from the camera. This is mostly correct, and then at the last minute it goes horribly wrong.

Apple provides a link to another part of the docs, with the following source code:

{
    ...
    UIImage* image = imageFromSampleBuffer(imageSampleBuffer);
    ...
}

UIImage *imageFromSampleBuffer(CMSampleBufferRef sampleBuffer)
{	
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    // Lock the base address of the pixel buffer.
    CVPixelBufferLockBaseAddress(imageBuffer,0);
	
    // Get the number of bytes per row for the pixel buffer.
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
    // Get the pixel buffer width and height.
    size_t width = CVPixelBufferGetWidth(imageBuffer);
    size_t height = CVPixelBufferGetHeight(imageBuffer);
	
    // Create a device-dependent RGB color space.
    static CGColorSpaceRef colorSpace = NULL;
    if (colorSpace == NULL) {
        colorSpace = CGColorSpaceCreateDeviceRGB();
		if (colorSpace == NULL) {
            // Handle the error appropriately.
            return nil;
        }
    }
	
    // Get the base address of the pixel buffer.
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    // Get the data size for contiguous planes of the pixel buffer.
    size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer);
	
    // Create a Quartz direct-access data provider that uses data we supply.
    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, NULL);
    // Create a bitmap image from data supplied by the data provider.
    CGImageRef cgImage = CGImageCreate(width, height, 8, 32, bytesPerRow, colorSpace, kCGImageAlphaNoneSkipFirst | 
kCGBitmapByteOrder32Little, dataProvider, NULL, true, kCGRenderingIntentDefault);
    CGDataProviderRelease(dataProvider);
	
    // Create and return an image object to represent the Quartz image.
    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);
	
    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
	
    return image;
}

This code has never worked for me – it always returns an empty 0×0 image, which is useless. That’s 45 lines of useless code, that everyone is required to re-implement in every app they write.

Or maybe not.

Instead, if you look at the WWDC videos, you find an alternate approach, that takes just two lines of source code:

NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];

Even better … this actually works!

How do we: take a photo of what’s in the live video feed?

There’s two halves to this. Obviously, we’ll need a button to capture a photo, and a UIImageView to display it. Less obviously, we’ll have to alter our existing camera-setup routine.

To make this work, we have to create an “output source” for the camera when we start it, and then later on when we want to take a photo we ask that “output” object to give us a single image.

Part 1: Add buttons and views and image-capture routine

So, create a new @property to hold a reference to our output object:

@property(nonatomic, retain) AVCaptureStillImageOutput *stillImageOutput;

Then make a UIImageView where we’ll display the captured photo. Add this to your NIB, or programmatically.

Hook it up to another @property, or assign it manually, e.g.;

@property(nonatomic, retain) IBOutlet UIImageView *vImage;

Finally, create a UIButton, so that you can take the photo.

Again, add it to your NIB (or programmatically to your screen), and hook it up to the following method:

-(IBAction) captureNow
{
	AVCaptureConnection *videoConnection = nil;
	for (AVCaptureConnection *connection in stillImageOutput.connections)
	{
		for (AVCaptureInputPort *port in [connection inputPorts])
		{
			if ([[port mediaType] isEqual:AVMediaTypeVideo] )
			{
				videoConnection = connection;
				break;
			}
		}
		if (videoConnection) { break; }
	}
	
	NSLog(@"about to request a capture from: %@", stillImageOutput);
	[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
	{
		 CFDictionaryRef exifAttachments = CMGetAttachment( imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
		 if (exifAttachments)
		 {
			// Do something with the attachments.
			NSLog(@"attachements: %@", exifAttachments);
		 }
		else
			NSLog(@"no attachments");
		
		NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
		UIImage *image = [[UIImage alloc] initWithData:imageData];

		self.vImage.image = image;
	 }];
}

Part 2: modify the camera-setup routine

Go back to the viewDidAppear method you created at the start of this post. The very last line must REMAIN the last line, so we’ll insert the new code immediately above it. Here’s the new code to insert:

stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];
[stillImageOutput setOutputSettings:outputSettings];

[session addOutput:stillImageOutput];

Run the app, and you should get something like the image I showed at the start, where the part on the left is a live-preview from the camera, and the part on the right updates each time you click the “take photo” button: