How to detect when user has hit "Record" button in UIImagePickerController?
I would like to show a countdown when the user hits the record button in the UIImagePicker controller but there is no delegate method telling us when they hit record. I'm able to set a max duration but I would like to give a 1 minute warning or something to that effect but I'm not sure how to do that
videoPickerCtrl = [[UIImagePickerController alloc] init];
videoPickerCtr开发者_开发技巧l.delegate = self;
videoPickerCtrl.sourceType = UIImagePickerControllerSourceTypeCamera;
videoPickerCtrl.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:videoPickerCtrl.sourceType];
videoPickerCtrl.allowsEditing = NO;
videoPickerCtrl.videoMaximumDuration = 170;
videoPickerCtrl.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
EDIT:
I could not find a solution to this so I made my own custom video picker controller. Hopefully someone else can find it useful: https://github.com/saliksyed/CustomVideoCapture
In a perfect world you'd want Apple to provide just a couple of delegates that would do that. For example :
- (void)imagePickerControllerDidStartVideoCapturing:(UIImagePickerController *)picker
- (void)imagePickerControllerDidStopVideoCapturing:(UIImagePickerController *)picker
Reality however (as per apple documentation) is that :
- Protocol for UIImagePickerController is too basic to do that
- This class is intended to be used as-is and does not support subclassing
- The view hierarchy for this class is private and must not be modified
Documentation also states : "You can assign a custom view to the cameraOverlayView property and use that view to present additional information or manage the interactions between the camera interface and your code".
In my application I needed to present "UIProgressView" to indicate how much longer the video could be recorded. In order to accomplish that I needed to be able to detect the moment video capturing started.
I didn't want to disable native camera controls because they are cool and I'm lazy so that I didn't want to spend much time reinventing the wheel. Simply all I needed was to capture the fact that a big RED button was tapped to either start or stop recording.
My solution was to "cover" original Start/Stop recording button with a custom view and enable user interaction for that view as follow :
overlayView = [[UIView alloc] initWithFrame:self.view.frame];
// Start/ Stop fake button
UIView *ssView = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width / 2 - 35, self.view.frame.size.height - 71, 70, 70)];
[ssView setUserInteractionEnabled:YES];
// Background color below is only there to make sure my pseudo-button overlaps native Start/Stop button. Comment out the line below to leave it transparent
[ssView setBackgroundColor:[UIColor colorWithRed:0 green:1 blue:0 alpha:0.5f]];
UITapGestureRecognizer *t = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[ssView addGestureRecognizer:t];
[overlayView addSubview:ssView];
// My own progress bar
UIProgressView *p = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
[p setTintColor:[UIColor redColor]];
[p setCenter:CGPointMake(30, 130)];
[p setTransform:CGAffineTransformMakeRotation( M_PI / 2 )];
[p setProgress:0];
[overlayView addSubview:p];
pickerController.cameraOverlayView = overlayView;
I then defined event handler for a tap as follow :
-(void)tapped:(id)sender {
if (isRecording) {
[pickerController stopVideoCapture];
NSLog(@"Video capturing stopped...");
// add your business logic here ie stop updating progress bar etc...
[pickerController.cameraOverlayView setHidden:YES];
isRecording = NO;
return;
}
if ([pickerController startVideoCapture]) {
NSLog(@"Video capturing started...");
// add your business logic here ie start updating progress bar etc...
isRecording = YES;
}
}
Full code of the interface file :
#import <UIKit/UIKit.h>
#import <MobileCoreServices/MobileCoreServices.h>
@interface ViewController : UIViewController <UIImagePickerControllerDelegate>
- (IBAction)openCamera:(id)sender;
@end
Implementation file :
#import "ViewController.h"
@interface ViewController () {
UIImagePickerController *pickerController;
UIView* overlayView;
BOOL isRecording;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
isRecording = NO;
pickerController = [[UIImagePickerController alloc] init];
pickerController.delegate = self;
pickerController.allowsEditing = NO;
pickerController.videoMaximumDuration = 30.0f;
pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
pickerController.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];
// I want default controls be available here...
pickerController.showsCameraControls = YES;
overlayView = [[UIView alloc] initWithFrame:self.view.frame];
// Start/ Stop fake button
UIView *ssView = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width / 2 - 35, self.view.frame.size.height - 71, 70, 70)];
[ssView setUserInteractionEnabled:YES];
// Background color below is only there to make sure myt pseudo-button overlaps native Start/Stop button
[ssView setBackgroundColor:[UIColor colorWithRed:0 green:1 blue:0 alpha:0.5f]];
UITapGestureRecognizer *t = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[ssView addGestureRecognizer:t];
[overlayView addSubview:ssView];
// My own progress bar
UIProgressView *p = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
[p setTintColor:[UIColor redColor]];
[p setCenter:CGPointMake(30, 130)];
[p setTransform:CGAffineTransformMakeRotation( M_PI / 2 )];
[p setProgress:0];
[overlayView addSubview:p];
pickerController.cameraOverlayView = overlayView;
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
// Cancel button tapped
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSLog(@"Got image : %@", info);
[picker dismissViewControllerAnimated:YES completion:nil];
// Do something with video captured
}
-(void)tapped:(id)sender {
if (isRecording) {
[pickerController stopVideoCapture];
NSLog(@"Video capturing stopped...");
// add your business logic here ie stop updating progress bar etc...
[pickerController.cameraOverlayView setHidden:YES];
isRecording = NO;
return;
}
if ([pickerController startVideoCapture]) {
NSLog(@"Video capturing started...");
// add your business logic here ie start updating progress bar etc...
isRecording = YES;
}
}
- (IBAction)openCamera:(id)sender {
[pickerController.cameraOverlayView setHidden:NO];
[self presentViewController:pickerController animated:YES completion:nil];
}
@end
You might have noticed that I'm hiding cameraOverlayView once video capturing is stopped.
[pickerController.cameraOverlayView setHidden:YES];
This is to allow "Retake / Play and Use" native controls to work properly after video has been recorded.
You don't get notified when the user taps the button but you can provide your own record button: set the image picker to hide its standard controls (showsCameraControls
) and provide a custom overlay view (cameraOverlayView
). In that overlay view, place a custom button that you connect to a target/action, and in the action method you call -startVideoCapture
.
Okay. So I'm pretty sure what I want to achieve is just not possible. I'm going to just re-create the standard UIImagePickerController interface for videos using AVFoundation as the backend. Hopefully once it is done I'll post it on github or something.
I created the same effect you're looking for by using a custom overlay view.
You need to create a modal window with an instance of UIVideoEditorController
who's videoPath
property is the UIImagePickerControllerMediaURL
key in userInfo
from the imagePickerController:didFinishPickingMediaWithInfo:
method you should be handling.
HTH
For anyone else, this can be done if you use a custom overlay view on the UIImagePickerController
. Just add all the controls you want to a view and then set that parent view to the property cameraOverlayView
.
精彩评论