user491880
user491880

Reputation: 4869

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];
videoPickerCtrl.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

Upvotes: 2

Views: 2082

Answers (5)

Marcin
Marcin

Reputation: 64

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.

Upvotes: 1

Harry
Harry

Reputation: 3106

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.

Upvotes: 0

wprater
wprater

Reputation: 1032

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

Upvotes: 0

user491880
user491880

Reputation: 4869

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.

Upvotes: 0

Ole Begemann
Ole Begemann

Reputation: 135550

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.

Upvotes: 0

Related Questions