检测用户何时开始录像

我有一个UIImagePickerController,我用它来loggingvideo。 现在我想检测用户点击录制button。 代表不提供任何这样的callback。

有没有什么好的方法可以找出录像的时间?

你是对的:代表没有收到有关何时发生video捕获的任何信息。 简单的解决scheme是简单地滚动自己的摄像头控制 – 设置showsCameraControls属性为NO并使用showsCameraControls属性在自定义视图中提供自己的控件,并让您的“捕获”button调用图像select器控制器的-startVideoCapture-stopVideoCapture方法,同时提供你需要的任何通知给应用程序的其他部分。

在一个完美的世界里,你会希望苹果只提供一些能够做到这一点的代表。 例如 :

  • (void)imagePickerControllerDidStartVideoCapturing:(UIImagePickerController *)picker
  • (void)imagePickerControllerDidStopVideoCapturing:(UIImagePickerController *)picker

然而,现实(根据苹果文档)是这样的:

  • UIImagePickerController的协议太基本了
  • 这个类的目的是按原样使用,不支持子类
  • 这个类的视图层次是私有的,不能被修改

文档还指出:“您可以将自定义视图分配给cameraOverlayView属性,并使用该视图呈现附加信息或pipe理摄像头界面和代码之间的交互。”

在我的应用程序中,我需要展示“UIProgressView”来表示video可以被logging多长时间。 为了做到这一点,我需要能够检测到video捕捉开始的时刻。

我不想禁用原生摄像头控制,因为它们很酷,而且我很懒,所以我不想花太多时间重新发明轮子。 简单地说,我所需要的就是捕捉到一个大的REDbutton被启动或停止录制的事实。

我的解决scheme是用自定义视图“覆盖”原始的“开始/停止”录制button,并为该视图启用用户交互,如下所示:

 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; 

然后我定义了一个tap的事件处理程序,如下所示:

 -(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; } } 

接口文件的完整代码:

 #import <UIKit/UIKit.h> #import <MobileCoreServices/MobileCoreServices.h> @interface ViewController : UIViewController <UIImagePickerControllerDelegate> - (IBAction)openCamera:(id)sender; @end 

实施文件:

 #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 

您可能已经注意到,一旦停止video捕捉,我就隐藏了cameraOverlayView。

 [pickerController.cameraOverlayView setHidden:YES]; 

录制video后,允许“重播/播放和使用”本机控制function正常工作。

根据ios api,“startVideoCapture”方法给出了一个布尔返回值

是的意思是录音

没有意思是以下之一:

  1. 电影拍摄已在进行中
  2. 该设备不支持电影
  3. 捕获设备磁盘空间不足

[参考:http://developer.apple.com/library/ios/#documentation/uikit/reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html]

所以只要'startRecording'返回一个是,你几乎可以肯定地说它是录音。 当然,为了检查,你可以随时用NSTimer手动定义你自己的callback(虽然以前有一个热门的辩论,但它的准确性有所提高)。

这里是一个快速的3 UIImagePickerController类,只允许用户捕捉video,如果相机在横向。

当你创build你的UIImagePickerController时:

 var imagePicker = CameraVideo_ViewController() 

你也需要在你的委托中有这个:

 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { self.imagePicker.isInFinalScreen = false } 

这是class级。 我真的没有时间清理它,这真的很混乱…我创build了这个类,通过尝试一切,我可以不删除默认控件。 使用了一堆黑客,所以在删除variables时要非常小心,因为即使它们看起来没有用处,它们大部分都会计数。我会尝试重新发布一个更干净的版本,我可以find一些时间,但在那之前,如果有人可以重新发布一个更清洁,更新手我/我们将不胜感激。

 import UIKit import AVFoundation; class CameraVideo_ViewController: UIImagePickerController { var viewMain:UIView! var lastOrientationWasLandscape:UIDeviceOrientation? var isForLibrary:Bool! = false var parentController:UIViewController! override func viewDidAppear(_ animated: Bool) { if isForLibrary == true { return } let orientation = UIDevice.current.orientation if orientation == .landscapeLeft || orientation == .landscapeRight { lastOrientationWasLandscape = orientation } if (self.isInFinalScreen == true){ self.recordBut.frame = CGRect(x: self.view.frame.size.width - 70 - 16, y: self.view.frame.size.height / 2 - 35, width: 70, height: 70) self.isInFinalScreen = false } recordBut.alpha = 1 recordBut.isUserInteractionEnabled = true retakeBut.alpha = 1 retakeBut.isUserInteractionEnabled = true UIDevice.current.beginGeneratingDeviceOrientationNotifications() NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged(_:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) let notif = Notification.init(name: NSNotification.Name.UIDeviceOrientationDidChange) orientationChanged(notif) } override func viewWillDisappear(_ animated: Bool) { viewMain.alpha = 0 viewMain.isUserInteractionEnabled = false lastOrientationWasLandscape = nil recordBut.alpha = 0 recordBut.isUserInteractionEnabled = false retakeBut.alpha = 0 retakeBut.isUserInteractionEnabled = false self.viewMain.alpha = 0 self.viewMain.isUserInteractionEnabled = false isInFinalScreenBool = false recordedThisSession = false if isForLibrary == true { return } print("hidingCameraView") NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) UIDevice.current.endGeneratingDeviceOrientationNotifications() } override func viewDidLoad() { super.viewDidLoad() if isForLibrary == true { return } viewMain = UIView() viewMain.frame = CGRect(x: view.frame.minX, y: view.frame.minY, width: view.frame.width, height: view.frame.height) viewMain.backgroundColor = UIColor.clear let viewBg = UIView() viewBg.frame = CGRect(x: view.frame.minX, y: view.frame.minY, width: view.frame.width, height: view.frame.height) viewBg.backgroundColor = UIColor.black viewBg.alpha = 0.5 let viewAlertBg = UIView() viewAlertBg.frame = CGRect(x: view.frame.width/2 - 250/2, y: view.frame.height/2 - 100/2 - 25, width: 250, height: 100) viewAlertBg.backgroundColor = UIColor.white viewAlertBg.alpha = 1 viewAlertBg.clipsToBounds = true; var path = UIBezierPath(roundedRect:viewAlertBg.bounds, byRoundingCorners:[.topLeft, .topRight], cornerRadii: CGSize(width: 25, height: 25)) var maskLayer = CAShapeLayer() maskLayer.path = path.cgPath viewAlertBg.layer.mask = maskLayer let viewAlertBgUnderline = UIView() viewAlertBgUnderline.frame = CGRect(x: view.frame.width/2 - 250/2, y: viewAlertBg.frame.maxY, width: 250, height: 1) viewAlertBgUnderline.backgroundColor = UIColor.lightGray viewAlertBgUnderline.alpha = 1 viewAlertBgUnderline.clipsToBounds = true; let viewAlertCancelBut = UIButton() viewAlertCancelBut.frame = CGRect(x: view.frame.width/2 - 250/2, y: viewAlertBgUnderline.frame.maxY, width: 250, height: 50) viewAlertCancelBut.backgroundColor = UIColor.white viewAlertCancelBut.setTitle("Back", for: .normal) viewAlertCancelBut.setTitleColor(UIColor.blue, for: .normal) path = UIBezierPath(roundedRect:viewAlertCancelBut.bounds, byRoundingCorners:[.bottomLeft, .bottomRight], cornerRadii: CGSize(width: 25, height: 25)) maskLayer = CAShapeLayer() maskLayer.path = path.cgPath viewAlertCancelBut.layer.mask = maskLayer viewAlertCancelBut.addTarget(self, action: #selector(onBack(_:)), for: .touchUpInside) let alertLabel = UILabel() alertLabel.numberOfLines = 4 alertLabel.frame = CGRect(x: 16, y: 16, width: 250 - 32, height: 100 - 32) alertLabel.textAlignment = NSTextAlignment.center alertLabel.adjustsFontSizeToFitWidth = true alertLabel.font = UIFont.systemFont(ofSize: 12) alertLabel.backgroundColor = UIColor.clear alertLabel.textColor = UIColor.black let boldText = "The video must be recorded in landscape mode.\n" let normalText = "Please hold your device in a horizontal position!" let attrs = [NSFontAttributeName : UIFont.boldSystemFont(ofSize: 13)] let boldString = NSMutableAttributedString(string:boldText, attributes:attrs) let attributedString = NSMutableAttributedString(string:"") attributedString.append(boldString) attributedString.append(NSMutableAttributedString(string:normalText, attributes:nil)) alertLabel.attributedText = attributedString viewAlertBg.addSubview(alertLabel) viewMain.addSubview(viewBg) viewMain.addSubview(viewAlertCancelBut) viewMain.addSubview(viewAlertBg) viewMain.addSubview(viewAlertBgUnderline) viewMain.alpha = 0 viewMain.isUserInteractionEnabled = false // Start/ Stop fake button recordBut = UIButton() if (UIDevice.current.userInterfaceIdiom == .phone){ recordBut.frame = CGRect(x: self.view.frame.size.width / 2 - 35, y: self.view.frame.size.height - 70 - 2, width: 70, height: 70) }else{ recordBut.frame = CGRect(x: self.view.frame.size.height - 70 - 16, y: self.view.frame.size.width/2 - 35, width: 70, height: 70) } recordBut.setTitle("", for: .normal) recordBut.setTitleColor(UIColor.blue, for: .normal) recordBut.isUserInteractionEnabled = true recordBut.backgroundColor = UIColor(red: 0, green: 1, blue: 0, alpha: 0.5) recordBut.addTarget(self, action: #selector(tapped(_:)), for: .touchUpInside) self.view.addSubview(recordBut) retakeBut = UIButton() if (UIDevice.current.userInterfaceIdiom == .phone){ retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.height - 70, width: 80, height: 70) }else{ retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.width - 70, width: 80, height: 70) } retakeBut.setTitle("", for: .normal) retakeBut.setTitleColor(UIColor.blue, for: .normal) retakeBut.isUserInteractionEnabled = true retakeBut.backgroundColor = UIColor(red: 0, green: 1, blue: 0, alpha: 0.5) retakeBut.addTarget(self, action: #selector(retake(_:)), for: .touchUpInside) self.view.addSubview(retakeBut) self.view.addSubview(viewMain) } override func viewDidLayoutSubviews() { if isForLibrary == true { return } self.adjustViews(for: UIDevice.current.orientation) } var t:UITapGestureRecognizer! var recordBut:UIButton! var retakeBut:UIButton! var isInFinalScreen:Bool = false var isRecording:Bool = false var isInFinalScreenBool:Bool = false var recordedThisSession:Bool = false func tapped(_ sender:UIButton){ if (isRecording == false && self.startVideoCapture()){ recordedThisSession = true isRecording = true retakeBut.alpha = 0 retakeBut.isUserInteractionEnabled = false }else{ retakeBut.alpha = 1 retakeBut.isUserInteractionEnabled = true recordBut.alpha = 0 recordBut.isUserInteractionEnabled = false self.stopVideoCapture() isRecording = false if (UIDevice.current.orientation != .portrait){ self.adjustViews(for: UIDevice.current.orientation) } isInFinalScreen = true return } } func retake(_ sender:UIButton){ if (recordedThisSession == false){ onBack(sender) } self.dismiss(animated: true, completion: { self.parentController.present((self.parentController as! AddVideo_ViewController).imagePicker, animated: true, completion: { }) }) } func onBack(_ sender:UIButton){ self.isInFinalScreen = false self.dismiss(animated: true, completion: { }) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func orientationChanged(_ notification: Notification) { self.adjustViews(for: UIDevice.current.orientation) } func adjustViews(for orient: UIDeviceOrientation) { var orientation = orient if (orientation.isLandscape == true) || (orientation.isFlat && lastOrientationWasLandscape?.isPortrait == false) { print(".....landscape.....") if (UIDevice.current.userInterfaceIdiom == .pad){ self.recordBut.frame = CGRect(x: self.view.frame.size.width - 70 - 16, y: self.view.frame.size.height/2 - 35, width: 70, height: 70) self.retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.height - 70, width: 80, height: 70) }else{ recordBut.frame = CGRect(x: self.view.frame.size.width / 2 - 35, y: self.view.frame.size.height - 70 - 2, width: 70, height: 70) retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.height - 70, width: 80, height: 70) } if (recordedThisSession == false){ if (UIDevice.current.userInterfaceIdiom == .pad){ self.retakeBut.frame = CGRect(x: self.view.frame.size.width - 100, y: self.view.frame.size.height - 70, width: 100, height: 70) }else{ retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.height - 70, width: 80, height: 70) } } if (self.isInFinalScreenBool == true){ return } if (self.isInFinalScreen == true){ self.isInFinalScreenBool = !isInFinalScreenBool self.isInFinalScreen = false return } if (self.isRecording){ return } self.viewMain.alpha = 0 self.viewMain.isUserInteractionEnabled = false self.lastOrientationWasLandscape = orientation } else { print(".....portrait.....") self.lastOrientationWasLandscape = UIDeviceOrientation(rawValue: UIDeviceOrientation.portrait.rawValue) if (UIDevice.current.userInterfaceIdiom == .pad){ self.recordBut.frame = CGRect(x: self.view.frame.size.width - 70 - 16, y: self.view.frame.size.height / 2 - 35, width: 70, height: 70) self.retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.height - 70, width: 80, height: 70) }else{ recordBut.frame = CGRect(x: self.view.frame.size.width / 2 - 35, y: self.view.frame.size.height - 70 - 2, width: 70, height: 70) retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.height - 70, width: 80, height: 70) } if (recordedThisSession == false){ if (UIDevice.current.userInterfaceIdiom == .pad){ self.retakeBut.frame = CGRect(x: self.view.frame.size.width - 100, y: self.view.frame.size.height - 70, width: 100, height: 70) }else{ retakeBut.frame = CGRect(x: 0, y: self.view.frame.size.height - 70, width: 80, height: 70) } } if (self.isInFinalScreenBool == true){ return } if (self.isInFinalScreen){ return } if (self.isRecording){ return } self.viewMain.alpha = 1 self.viewMain.isUserInteractionEnabled = true } } /* // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get the new view controller using segue.destinationViewController. // Pass the selected object to the new view controller. } */ } 

祝你好运!