最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

swift - Camera preview not visible in view controller - Stack Overflow

programmeradmin3浏览0评论

I am trying to get a camera preview to load with live video as soon as a view controller loads for a profile photo. However, the video is not displaying at first. This is with a real iPhone, not the simulator.

While the video does not display at first, if you open up the camera app and switch from Back Facing to Front Facing one or more times and then go back to the app, the video does display.

What am I missing to get it to display video immediately?

Swift camera code:

 @objc func showLivePreview(view: UIView) {
            var myPreviewLayer = AVCaptureVideoPreviewLayer()
            let captureSession = AVCaptureSession()
            var backCamera: AVCaptureDevice?
            var frontCamera: AVCaptureDevice?
            var currentCamera: AVCaptureDevice?
    
            var videoOutput = AVCaptureVideoDataOutput()
          
          
           // Step 1: Set up the preview layer
            myPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
           myPreviewLayer.videoGravity = .resizeAspectFill
           myPreviewLayer.connection?.videoOrientation = .portrait
            captureSession.sessionPreset = AVCaptureSession.Preset.photo
 let session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .front)
        
        let devices = session.devices
        currentCamera = devices.first
           
            do{
    
                   let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera!)
                   print(captureDeviceInput)
                   captureSession.addInput(captureDeviceInput)
                   
                   let videoOutput = AVCaptureMovieFileOutput()
                    
                   captureSession.addOutput(videoOutput)
                
    
               }catch{
    
                   return
               }
                
           view.layer.addSublayer(myPreviewLayer)
           
           // Make layer rounded
           view.layer.cornerRadius = view.bounds.width / 2
           view.layer.masksToBounds = true
           print("finished with layer. start camera")
           
            
            myPreviewLayer.session = captureSession
          
           let photoOutput = AVCapturePhotoOutput()
            if captureSession.canAddOutput(photoOutput ) {
                captureSession.addOutput(photoOutput )
           }
           
           // Position layer in UI
           myPreviewLayer.frame = view.frame//was bounds
           
           // Start the session in background thread as per Thread Performance Checker
           DispatchQueue.global().async {
               captureSession.startRunning() 
       }

The camera is called from a VC written in Objective-C that uses Storyboard. I have not yet tried to position the camera so the view self.view is just the main view of the storyboard.

objective-c

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[Utilities shared] showLivePreviewWithView:self.view];
}

I am trying to get a camera preview to load with live video as soon as a view controller loads for a profile photo. However, the video is not displaying at first. This is with a real iPhone, not the simulator.

While the video does not display at first, if you open up the camera app and switch from Back Facing to Front Facing one or more times and then go back to the app, the video does display.

What am I missing to get it to display video immediately?

Swift camera code:

 @objc func showLivePreview(view: UIView) {
            var myPreviewLayer = AVCaptureVideoPreviewLayer()
            let captureSession = AVCaptureSession()
            var backCamera: AVCaptureDevice?
            var frontCamera: AVCaptureDevice?
            var currentCamera: AVCaptureDevice?
    
            var videoOutput = AVCaptureVideoDataOutput()
          
          
           // Step 1: Set up the preview layer
            myPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
           myPreviewLayer.videoGravity = .resizeAspectFill
           myPreviewLayer.connection?.videoOrientation = .portrait
            captureSession.sessionPreset = AVCaptureSession.Preset.photo
 let session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .front)
        
        let devices = session.devices
        currentCamera = devices.first
           
            do{
    
                   let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera!)
                   print(captureDeviceInput)
                   captureSession.addInput(captureDeviceInput)
                   
                   let videoOutput = AVCaptureMovieFileOutput()
                    
                   captureSession.addOutput(videoOutput)
                
    
               }catch{
    
                   return
               }
                
           view.layer.addSublayer(myPreviewLayer)
           
           // Make layer rounded
           view.layer.cornerRadius = view.bounds.width / 2
           view.layer.masksToBounds = true
           print("finished with layer. start camera")
           
            
            myPreviewLayer.session = captureSession
          
           let photoOutput = AVCapturePhotoOutput()
            if captureSession.canAddOutput(photoOutput ) {
                captureSession.addOutput(photoOutput )
           }
           
           // Position layer in UI
           myPreviewLayer.frame = view.frame//was bounds
           
           // Start the session in background thread as per Thread Performance Checker
           DispatchQueue.global().async {
               captureSession.startRunning() 
       }

The camera is called from a VC written in Objective-C that uses Storyboard. I have not yet tried to position the camera so the view self.view is just the main view of the storyboard.

objective-c

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[Utilities shared] showLivePreviewWithView:self.view];
}
Share Improve this question edited Mar 14 at 19:31 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Mar 8 at 18:09 user6631314user6631314 1,9801 gold badge18 silver badges60 bronze badges 6
  • Can you please share the code of your controller? May be wrong frame handling – Kiryl Famin Commented Mar 8 at 19:02
  • 1 Your code runs perfectly on my device, but I'm running it programmatically with Swift. Try moving your showLivePreview call to viewDidLayoutSubviews instead of viewDidLoad – Kiryl Famin Commented Mar 9 at 10:04
  • That worked. If you would like to take the time to put it an an answer I will mark it as correct. Many thanks! – user6631314 Commented Mar 9 at 17:28
  • viewDidLayoutSubviews gets called many times. You end up adding more and more preview layers over time. Do not do that. viewDidLayoutSubviews should only adjust the size of the preview layer, that's it. – HangarRash Commented Mar 9 at 17:42
  • @HangarRash yes, I suggested this approach just to make sure that frame causes the issue – Kiryl Famin Commented Mar 9 at 17:51
 |  Show 1 more comment

1 Answer 1

Reset to default 0

As already discussed in comments, this happens because on viewDidLoad the frame of the controller is not calculated yet. The first method of UIViewController lifecycle where the frame is calculated is viewDidLayoutSubviews so adjusting the frame here solves the problem as controller's frame is already set up and can be used to layout other views correctly.

var previewLayer: AVCaptureVideoPreviewLayer?

func showLivePreview(view: UIView) -> AVCaptureVideoPreviewLayer {
   ...

   return previewLayer
}

override func viewDidLoad() {
    super.viewDidLoad()

    previewLayer = showLivePreview(view: view)
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    previewLayer?.frame = view.bounds
}

A better option is always overriding layoutSubviews (for UIView) / layoutSublayers (for CALayer) methods. This ensures that views and layers are always positioned correctly, even after orientation changes or other layout updates:

final class PreviewLayer: CALayer {
    
    override func layoutSublayers() {
        super.layoutSublayers()
        
        if let superlayer {
            self.frame = superlayer.bounds
        }
    }
}

This way your views and layers will automatically adapt to new sizes.

发布评论

评论列表(0)

  1. 暂无评论