обнаружение, когда пользователь начинает запись видео - PullRequest
4 голосов
/ 17 июня 2011

У меня есть UIImagePickerController, который я использую для записи видео. Теперь я хочу определить, когда пользователь нажимает кнопку записи. делегат не предоставляет такого обратного вызова.

Есть ли какой-нибудь хороший способ узнать, когда видео записывается?

Ответы [ 4 ]

6 голосов
/ 17 июня 2011

Вы правы: делегат не получает никакой информации о том, когда происходит захват видео.Простое решение - просто свернуть собственные элементы управления камерой - установить для свойства showsCameraControls значение NO и предоставить собственные элементы управления в настраиваемом представлении со свойством cameraOverlayView - и заставить вашу кнопку «захвата» вызывать контроллер средства выбора изображений.-startVideoCapture и -stopVideoCapture, наряду с предоставлением любых необходимых уведомлений другим частям приложения.

2 голосов
/ 22 февраля 2014

В идеальном мире вы бы хотели, чтобы Apple предоставила всего пару делегатов, которые сделали бы это.Например:

  • (void) imagePickerControllerDidStartVideoCapturing: (UIImagePickerController *) средство выбора
  • (void) imagePickerControllerDidStopVideoCapturing: (UIImagePickerCerroller) * 100 * * 100 *согласно документации Apple):
    • Протокол для UIImagePickerController слишком прост, чтобы сделать это
    • Этот класс предназначен для использования как есть и не поддерживает подклассы
    • Иерархия представлений для этого класса является закрытой и не подлежит изменению.

    В документации также говорится: «Вы можете назначить настраиваемое представление свойству cameraOverlayView и использовать это представление для представления дополнительной информации или управлениявзаимодействие между интерфейсом камеры и вашим кодом ".

    В моем приложении мне нужно было представить" UIProgressView ", чтобы указать, как долго видео может быть записано.Для этого мне нужно было определить момент начала захвата видео.

    Я не хотел отключать встроенные элементы управления камерой, потому что они крутые, и я ленив, так что я не хотел тратить много времени на изобретение колеса.Просто все, что мне было нужно, это зафиксировать тот факт, что большая КРАСНАЯ кнопка была нажата, чтобы начать или остановить запись.

    Мое решение состояло в том, чтобы "накрыть" исходную кнопку начала / остановки записи пользовательским представлением и включить взаимодействие с пользователем.для этого представления следующим образом:

    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;
    

    Затем я определил обработчик событий для крана следующим образом:

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

    Возможно, вы заметили, что я скрываю cameraOverlayView, как только захват видео остановлен.

    [pickerController.cameraOverlayView setHidden:YES];
    

    Это позволяет "Retake / Play and Use" nativeуправляет работой после записи видео.

0 голосов
/ 03 ноября 2016

Вот быстрый класс 3 UIImagePickerController, который позволит пользователю захватывать видео, только если камера находится в альбомной ориентации.

Когда вы создаете свой UIImagePickerController:

var imagePicker = CameraVideo_ViewController()

Вам необходимо иметь это также и в вашем делегате:

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

И это класс.У меня нет времени, чтобы привести его в порядок, и это действительно грязно ... Я создал этот класс, пытаясь сделать все возможное, не удаляя стандартные элементы управления.Была использована куча хаков, поэтому будьте очень осторожны при удалении переменных, потому что большинство из них считается, даже если они кажутся бесполезными. Я попытаюсь опубликовать более чистую версию, когда найду время, но до тех пор, если кто-то сможет опубликовать более чистый и более новичокдружественная версия этого I / мы будем благодарны.

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.
}
*/

}

Удачи!

0 голосов
/ 17 июня 2011

Согласно ios api, метод "startVideoCapture" возвращает логическое возвращаемое значение

Да означает, что запись

Нет означает одно из следующих:

  1. Захват фильма уже выполняется
  2. Устройство не поддерживает фильм
  3. capture На устройстве недостаточно дискового пространства

[Ссылка: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html]

Так что, пока startRecording возвращает Yes, вы почти наверняка можете сказать, что это запись. Конечно, чтобы проверить это, вы всегда можете вручную определить свой собственный обратный вызов с помощью NSTimer (хотя ранее было много горячих споров о его точности).

...