10-3-31
转:http://www.codza.com/custom-uiimagepickercontroller-camera-viewJanuary 12, 2009
WARNING: While there are many apps (including some of mine) that use this technique, you should know some new apps and updates to existing apps have been rejected recently (april/2009). Please read the comments. So far I don’t know of any instances where the developer successfully argued Apple’s decision if the app was rejected.
With all that said, there’s still an outpour of apps that use this technique… so the decision is your.
Here is some information about inspecting and customizing the UIImagePickerController camera view. You can download the working xcode project with all the source code here: customImagePicker
I wanted to remove the top part of the interface (gray bacground and “Take Photo” label) for Mean Photo and Nice Photo (version 1.2+). There is also a very annoying image shift between the camera view and the preview that I thought would be nice to fix.
You can download the customImagePicker xcode project with all the source code that demonstrates these techniques. Run it on your iPhone in debug mode and check the log output for information.
CustomImagePicker is a subclass of UIImagePickerController. I override theviewDidLoad: method. When this method is called the view is ready to be customized (this approach was suggested in netsharc’s post here.)
-(void) viewDidAppear: (BOOL)animated {
// make sure to call the same method on the super class
[super viewDidAppear:animated];
/* ... customize view here ... */
}
The inspectView:: method to recursively walks the view hierarchy and prints information about the views to the log: the class descriptions, the position and size and the path (talk about the path in a bit.) If you are interested in other information about the views, you can extend this method to print them. Here’s how this method is invoked:
[self inspectView:self.view depth:0 path:@""];
You can uncomment this line in viewDidLoad:, but it’s also called from previewCheckevery five seconds, so you can monitor the view hierarchy as it changes in the different states. So for example if you are interested what the view hierarchy is like in the preview mode, just take a photo and wait until the next dump appears in the log. The output looks something like this:
.description: UILayoutContainerView: 0x125b90
.frame: 0, 0, 320, 460
.subviews: 2
--subview-- /0
.description: UITransitionView: 0x1205a0
.frame: 0, 0, 320, 460
.subviews: 1
--subview-- /0/0
.description: UIView: 0x125da0
.frame: 0, 0, 320, 460
.subviews: 1
--subview-- /0/0/0
.description: PLCameraView: 0x125e50
.frame: 0, 0, 320, 460
.subviews: 4
--subview-- /0/0/0/0
.description: UIView: 0x1264c0
.frame: 0, 0, 320, 427
.subviews: 0
--subview-- /0/0/0/1
.description: UIImageView: 0x128850
.class: UIImageView
.frame: 10000, 10000, 320, 480
.subviews: 0
--subview-- /0/0/0/2
.description: UIView: 0x11b200
.frame: 0, 0, 320, 33
.subviews: 0
--subview-- /0/0/0/3
.description: PLCropOverlay: 0x127dc0
.frame: 0, 0, 320, 460
.subviews: 3
--subview-- /0/0/0/3/0
.description: UIImageView: 0x12b2f0
.class: UIImageView
.frame: 0, 0, 320, 96
.subviews: 0
--subview-- /0/0/0/3/1
.description: PLCropLCDLayer: 0x12b350
.frame: 0, 0, 320, 96
.subviews: 0
--subview-- /0/0/0/3/2
.description: TPBottomDualButtonBar: 0x12b5b0
.frame: 0, 0, 320, 96
.subviews: 2
--subview-- /0/0/0/3/2/0
.description: TPPushButton: 0x12ba40
.frame: 22, 22, 128, 47
.subviews: 0
--subview-- /0/0/0/3/2/1
.description: TPCameraPushButton: 0x12df10
.frame: 170, 170, 128, 47
.subviews: 1
--subview-- /0/0/0/3/2/1/0
.description: UIImageView: 0x12e960
.class: UIImageView
.frame: 51, 51, 26, 19
.subviews: 0
--subview-- /1
.description: UINavigationBar: 0x125a30
.frame: 0, 0, 320, 44
.subviews: 1
--subview-- /1/0
.description: UINavigationItemView: 0x11abe0
.frame: 160, 160, 0, 27
.subviews: 0
Confusing? It’s actually pretty simple (although not very pretty.)
Next to each subview you can see it’s path. For example /0/0/0/3 means that it is subview #3 of subview #0 of subview #0 of subview #0. To look it up in the view hierarchy, just do this:
UIView *theView = [[[[[[[[self.view subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:3];
See the numbers next to objectAtIndex:? It’s /0/0/0/3… the path… So now you can look up any UIView from the hierarchy and modify it!
Even if the description shows that the class is part of the private iPhone libraries (eg.PLCropLCDLayer), it must be a subclass of UIView to be in the hierarchy. We don’t know (or at least not supposed to know) what methods the private library classes have, but they do have all the methods and properties of UIView. So we can maketheView transparent or hidden like this (this is actually the path for the UI above the camera preview):
[theView setAlpha:0.5]; // semi transparent
[theView setHidden:YES]; // hidden
I wanted to get rid of the gray bar and “Take Photo” label on top. The path for the gray background is /0/0/0/3/0, the label is /0/0/0/3/1. I look these up and animate their opacity (alpha) to 0:
UIImageView *overlay = [[[[[[[[[[self.view subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:3]
subviews] objectAtIndex:0];
UIView *label = [[[[[[[[[[self.view subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:3]
subviews] objectAtIndex:1];
// animate their visibility (alpha) to 0 with a simple UIView animation
//
[UIView beginAnimations:nil context:nil];
[overlay setAlpha:0.0];
[label setAlpha:0.0];
[UIView commitAnimations];
Here’s what it looks like in Nice Photo. The LOVE graphics is added in a separate view on top of the camera view.
Nice Photo App with Camera UI tweaked
The camera view is /0/0/0/0. To make it semi transparent (not sure why you would do this, but shows how to look it up):
UIView *cameraView = [[[[[[[[self.view subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:0];
[cameraView setAlpha:0.5];
You might be interested in button push events. The buttons are subclasses ofUIControl, so you can easily add an action to them. For example to add an action to the camera button (TPCameraPushButton at /0/0/0/3/2/1):
UIControl *captureButton = [[[[[[[[[[[[self.view subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:0]
subviews] objectAtIndex:3]
subviews] objectAtIndex:2]
subviews] objectAtIndex:1];
[captureButton addTarget:self action:@selector(captureButtonAction:)
forControlEvents:UIControlEventTouchUpInside];
(The captureButtonAction: method prints a message to the log when you tap the camera button in customImagePicker.)
I wanted to fix the shift between the preview and the camera view. To do this, I have a timer calling the previewCheck method (I’m pretty sure there’s more elegant ways… but come ont, timers are cool!). The preview will be added to the UIView at /0/0/0/2. By default this view has no subviews, but subviews are added when in preview mode. Then I modify the transform of /0/0/0/2/0/0/0 like such (btw this only makes sense in portrait mode):
[previewImage setTransform:CGAffineTransformTranslate(
CGAffineTransformMakeScale(320.0/1200, 320.0/1200),
-12.0*1200/320,-17.0*1200/320)];
(Now you probably know why I was so interested in the view sizes… the view frames for the preview image are completely bizarre… hence the image shift. Are people allowed to drink on the job at Apple?)
You can experiment with other views. The inspectView method gives you a pretty good map of the hierarchy and it also works for other source types (like picking an image from the photo library.) Please look at the customImagePicker xcode project, I added lots of comments (more than what I retyped here ;).
As always, feel free to contact me… and please check out www.meanvsnice.com for some shots taken with Mean Photo and Nice Photo.
+++++