Notice
Recent Posts
Recent Comments
Link
ยซ   2024/07   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
๊ด€๋ฆฌ ๋ฉ”๋‰ด

lgvv98

๐Ÿ“ธ ch18 FullScreen ์นด๋ฉ”๋ผ ์•ฑ ์ฝ”๋“œ๋ฆฌ๋ทฐ ๋ณธ๋ฌธ

โš ๏ธ deprecated โš ๏ธ/ํŒจ์บ (์˜ฌ์ธ์›)

๐Ÿ“ธ ch18 FullScreen ์นด๋ฉ”๋ผ ์•ฑ ์ฝ”๋“œ๋ฆฌ๋ทฐ

๐Ÿฅ• ์บ๋Ÿฟ๋งจ 2021. 7. 6. 16:14

โœ… ์ด๋ฒˆ์‹œ๊ฐ„์—๋Š” FullScreen ์นด๋ฉ”๋ผ ์•ฑ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณผ ์ƒ๊ฐ์ด์•ผ.

 

FullScreen ์นด๋ฉ”๋ผ๋Š” ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์ด ์–ด๋ ต๋‹ค.. ๊ธฐ์กด์— ์‹œ์Šคํ…œ ์นด๋ฉ”๋ผ๋ฅผ ์ด์šฉํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ๋„ ํ–ˆ์—ˆ๋Š”๋ฐ, ์นด๋ฉ”๋ผ ์ž์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ์“ฐ๋Š” ๊ฒƒ๋˜ํ•œ ์ฝ”๋“œ๊ฐ€ ์™„์ „ํžˆ ๋‹ฌ๋ผ, ์‰ฝ์ง€ ์•Š์€ ๊ณผ์ •์ด์—ˆ๋˜ ๊ฒƒ ๊ฐ™์•„.

 

โœŠ ๊ทธ๋ž˜์„œ ์ด๋ฒˆ์‹œ๊ฐ„์—๋Š” ๊ทธ ์–ด๋–ค ์‹œ๊ฐ„๋ณด๋‹ค ๋” ๊ผผ๊ผผํ•˜๊ฒŒ ํ•™์Šตํ•ด์„œ ๋‚˜๋งŒ์˜ ์นด๋ฉ”๋ผ ์•ฑ์„ ์‹œ์žฅ์— ๋‚ด๋†“์•„ ๋ณด์ž!!

 

์ฝ”๋“œ๋ฆฌ๋ทฐ์— ์‚ฌ์šฉํ•œ ์ฝ”๋“œ์ฃผ์†Œ

https://github.com/lgvv/fastCampus/tree/main/FullScreenCamera

 

lgvv/fastCampus

Contribute to lgvv/fastCampus development by creating an account on GitHub.

github.com

 

 

์šฐ์„  ์‹œ์ž‘ํ•˜๊ธฐ์— ์•ž์„œ

๋‹ค์ด์–ด๊ทธ๋žจ

๋‹ค์ด์–ด๊ทธ๋žจ์„ ์ดํ•ดํ•˜๊ธฐ ๋ณด๋‹ค ๊ฐ„๋‹จํ•˜๊ฒŒ๋งŒ ๋ณด๊ณ  ๋„˜์–ด๊ฐ€์ž!! ์ด๋Ÿฐ ๊ตฌ์กฐ๋ผ๋Š” ๊ฒƒ ์ •๋„๋งŒ! 

 

 

 

์ž ๊ทธ๋Ÿผ ์ฒœ์ฒœํžˆ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ์ง„ํ–‰ํ•ด ๋ณผ๊นŒ?

 

(๋ชฉ์ฐจ)

1. ์นด๋ฉ”๋ผ ์ดˆ๊ธฐ ์„ค์ •(์‹œ์Šคํ…œ ์นด๋ฉ”๋ผ X)

2. setupSession์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž 

 - โ—๏ธ์„ธ์…˜์„ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ๋„ ์žŠ์ง€๋ง์ž!

3. capturePhoto ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์‚ฌ์ง„์„ ์ฐ์–ด์•ผ๊ฒ ์ง€? ์ด๊ฑด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”๊ฑธ๊นŒ

4. ์‚ฌ์ง„์„ ์ฐ์—ˆ์œผ๋ฉด ํ›„๋ณด์ •? ๊ทธ๊ฑด ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฑด๋ฐ?

5. ์‚ฌ์ง„์ฐ๊ณ  ํ›„๋ณด์ •๋„ ํ–ˆ์–ด, ๊ทธ๋Ÿผ ์ด๋ฒˆ์—๋Š” ์‚ฌ์ง„์„ ์ €์žฅํ•ด๋ณผ๊นŒ?

6. ์ „๋ฉด, ํ›„๋ฉด ์นด๋ฉ”๋ผ ๋“ฑ ์ „ํ™˜ํ•  ๋•Œ์˜ ์ฝ”๋“œ

7. ์ฝ”๋“œ์— ๋ฆฌํ„ฐ๋Ÿด(literal)์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ• + setupUI()

8. preview.swift ํŒŒ์ผ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณผ๊นŒ?

 

 

โœ… 1. ์นด๋ฉ”๋ผ ์ดˆ๊ธฐ ์„ค์ •(์‹œ์Šคํ…œ ์นด๋ฉ”๋ผ X)

์‹œ์Šคํ…œ ์นด๋ฉ”๋ผ์™€ ๋ณ„๊ฐœ๋กœ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์—ฌ๊ธฐ์„œ๋Š” ํฐ ํ๋ฆ„์— ๋Œ€ํ•ด์„œ ํ•˜๋‚˜ํ•˜๋‚˜ ์งš์–ด๋ณผ ์ƒ๊ฐ์ด์•ผ.

     - CaptureSession

     - AVCaptureDeviceInput

     - AVCapturePhotoOutput

     - Queue

     - AVCaptureDeviceDiscoverySession

 

์ด๋ ‡๊ฒŒ ํฌ๊ฒŒ 5๊ฐ€์ง€ ํ๋ฆ„์„ ๊ฐ–๊ฒŒ ๋ผ.

์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ํ•œ๋ฒˆ ๋ด ๋ณผ๊นŒ?

    let captureSession = AVCaptureSession()
    var videoDeviceInput : AVCaptureDeviceInput! // ํ›„๋ฉด, ์ „๋ฉด์œผ๋กœ ์นด๋ฉ”๋ผ ์ „ํ™˜ํ• ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ๊นŒ
    let photoOutput = AVCapturePhotoOutput()
    
    let sessionQueue = DispatchQueue(label: "session Queue")
    let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInWideAngleCamera, .builtInTrueDepthCamera], mediaType: .video, position: .unspecified)
    // ๋””๋ฐ”์ด์Šค ํƒ€์ž…์—๋Š” ์•„์ดํฐ์ด ์นด๋ฉ”๋ผ ๊ฐฏ์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ๋˜๋ฉด์„œ ์ด๊ฑธ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์กฐ๊ธˆ์”ฉ ๋‹ฌ๋ผ์กŒ๋‹ค.

์„ธ์…˜์„ ๋งŒ๋“ค๊ณ  - ์ธํ’‹ ๋””๋ฐ”์ด์Šค๋ฅผ ๋งŒ๋“  ํ›„ - ์•„์›ƒํ’‹์— ๋Œ€ํ•œ ๊ฒƒ๋„ ๋งŒ๋“ ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์นด๋ฉ”๋ผ๋ฅผ ์‹คํ–‰ํ•  ํ๋ฅผ ๋”ฐ๋กœ ์„ค์ •ํ•ด์ค€ ๋’ค, ๋งˆ์ง€๋ง‰์œผ๋กœ ์‹ค์ œ ๊ธฐ๊ธฐ์— ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ์„ธํŒ…ํ•ด์ฃผ๋Š” ์ž‘์—…์œผ๋กœ ์ง„ํ–‰ ๋ผ.

 

 

โœ… ๊ทธ๋Ÿผ AVCaptureDeviceDiscoverySession์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋Œ€ํ•ด์„œ ํ•œ๋ฒˆ ์‚ดํŽด ๋ณผ๊นŒ?

- ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋Œ€ํ•œ ๋ถ€๋ถ„์€ ๋งจ ์•„๋ž˜ ์• ํ”Œ ๊ณต์‹๋ฌธ์„œ๋„ ํ•จ๊ป˜ ๋„ฃ์–ด๋‘์—ˆ์œผ๋‹ˆ ๊ผญ ์ฐธ๊ณ ํ•  ๊ฒƒ

๊ณต์‹ ๋ฌธ์„œ์— ๋‚˜์˜จ ์„ค๋ช…

๊ณต์‹ ๋ฌธ์„œ์˜ Overview ๋ถ€๋ถ„์„ ํ•œ๊ธ€๋กœ ๋ฒˆ์—ญํ•ด ๋ณด์ž๋ฉด 

 

" ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์žฅ์น˜ ์œ ํ˜• (์˜ˆ : ๋งˆ์ดํฌ ๋˜๋Š” ๊ด‘๊ฐ ์นด๋ฉ”๋ผ), ์บก์ฒ˜์— ์ง€์›๋˜๋Š” ๋ฏธ๋””์–ด ์œ ํ˜• (์˜ˆ : ์˜ค๋””์˜ค, ๋น„๋””์˜ค ๋˜๋Š” ๋‘˜ ๋‹ค) ๋ฐ ์œ„์น˜ (์ „๋ฉด ๋˜๋Š” ํ›„๋ฉด)์™€ ์ผ์น˜ํ•˜๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์บก์ฒ˜ ์žฅ์น˜๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ).

์žฅ์น˜ ๊ฒ€์ƒ‰ ์„ธ์…˜์„ ์ƒ์„ฑ ํ•œ ํ›„ devices์–ด๋ ˆ์ด๋ฅผ ๊ฒ€์‚ฌํ•˜์—ฌ ์บก์ฒ˜ ํ•  ์žฅ์น˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. "

(๊ตฌ๊ธ€ ๋ฒˆ์—ญ๊ธฐ)

 

 

1๏ธโƒฃ deviceType : ์•„์ดํฐ ํ›„๋ฉด์— ๋‹ฌ๋ฆฐ ์นด๋ฉ”๋ผ ์ค‘ ์–ด๋–ค ์นด๋ฉ”๋ผ ์“ธ๊ฑด์ง€ ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•ด์ค€๋‹ค.

์ด๊ฒŒ ๋ฌด์Šจ ๋ง์ด๋ƒ๋ฉด, ์•„์ดํฐ์ด ์นด๋ฉ”๋ผ๋ฅผ 1๊ฐœ๊ฐ€ ์•„๋‹Œ ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ๊ฐ–๊ฒŒ ๋˜๋ฉด์„œ ์–ด๋–ค ์นด๋ฉ”๋ผ๋ฅผ ์‚ฌ์šฉํ• ๊ฑด์ง€ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์‚ฌ์šฉํ•  ์นด๋ฉ”๋ผ๋ฅผ ๋ฐฐ์—ด์— ๋„ฃ์œผ๋ฉด ๋˜๋Š”๊ฑฐ๋ผ ํฌ๊ฒŒ ์–ด๋ ต์ง€ ์•Š๋‹ค.

2๏ธโƒฃ mediaType : ๊ณต์‹ ๋ฌธ์„œ์˜ ์„ค๋ช…์„ ๋ณด๋ฉด "์บก์ณํ•  ๋ฏธ๋””์–ด์˜ ์œ ํ˜•" ์ด๋ž˜ audio๋„ ์žˆ๊ณ  ๊ทธ๋Ÿฐ๋ฐ, ์šฐ๋ฆฌ๋Š” ์‚ฌ์ง„์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž„์œผ๋กœ media๋ฅผ ๋„ฃ์–ด์„œ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด์•ผ

3๏ธโƒฃ position : "์‹œ์Šคํ…œ ํ•˜๋“œ์›จ์–ด (์ „๋ฉด ๋˜๋Š” ํ›„๋ฉด)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฒ€์ƒ‰ ํ•  ์บก์ฒ˜ ์žฅ์น˜์˜ ์œ„์น˜" ๋ผ๊ณ  ์„ค๋ช…ํ•ด์ฃผ๊ณ  ์ „๋ฉด, ํ›„๋ฉด ์ƒ๊ด€์—†์ด ์‚ฌ์šฉํ•˜๋ ค๋ฉด

ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’์œผ๋กœ .unspecified์„ ๋„ฃ์–ด์ฃผ๋ฉด ๋ผ. ์ „๋ฉด์€(.front) ํ›„๋ฉด์€(.back)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค!

 

โœ… 2. setupSession์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž

์ดˆ๊ธฐ์„ค์ •์ด ๋๋‚ฌ์œผ๋ฉด ๊ทธ ๋‹ค์Œ์œผ๋กœ ์•Œ์•„๋ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์œ„์—์„œ ๋ณด์•˜๋‹ค ์‹ถ์ด ์„ธ์…˜์— ๋Œ€ํ•ด์„œ ๋จผ์ € ์„ธํŒ…์„ ํ•ด์ค˜์•ผ๊ฒ ์ง€?

์ด ๋ถ€๋ถ„์€ ์ฝ”๋“œ๋กœ ํ•จ๊ป˜ ๋ณผ๊ฑด๋ฐ, ๊ธธ๊ณ  ๋ณต์žกํ•˜๋‹ˆ ๊ผผ๊ผผํ•˜๊ฒŒ ์ฝ์–ด๋ณด์ž

 

func setupSession() {
        // TODO: captureSession ๊ตฌ์„ฑํ•˜๊ธฐ
        // - presetSetting ํ•˜๊ธฐ : ๋ฏธ๋””์–ด ์บก์ณ๋ฅผ ํ• ๋•Œ, ์‚ฌ์ง„์„ ์ฐ์„์ˆ˜๋„ ์žˆ๊ณ , ์˜์ƒ์„ ์ฐ์„์ˆ˜๋„ ์žˆ๊ณ  ํ•ด์ƒ๋„๋ฅผ ์ •ํ• ์ˆ˜๋„ ์žˆ๊ณ  ๋“ฑ๋“ฑ ํ•˜๋‹ˆ๊นŒ ๊ทธ์— ๋Œ€ํ•œ ๊ฒƒ์„ ๋จผ์ € ์„ค์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.
        // - beginConfiguration
        // - Add Video Input
        // - Add Photo Output
        // - commitConfiguration
        
        captureSession.sessionPreset = .photo // ์‚ฌ์ง„์„ ์ฐ์„๊ฑฐ๋‹ˆ๊นŒ ํฌํ† ๋กœ!
        captureSession.beginConfiguration() // ๋‚˜ ์ด์ œ ๊ตฌ์„ฑ ์‹œ์ž‘ํ• ๊ฑฐ๋ผ๊ณ  ํ”ผ์‹œ์—๊ฒŒ ์•Œ๋ฆผ
        
        
        // - Add Video Input
        
        do {
            var defaultVideoDevice : AVCaptureDevice?
            if let dualCameraDevice = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back) {
                defaultVideoDevice = dualCameraDevice
            } else if let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
                defaultVideoDevice = backCameraDevice
            } else if let frontCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) {
                defaultVideoDevice = frontCameraDevice
            }
            
            guard let camera = videoDeviceDiscoverySession.devices.first else {// ์—ฌ๊ธฐ์—์„œ ์ฐพ์€๊ฒŒ ์žˆ์œผ๋ฉด ์ฒซ๋ฒˆ์งธ๊บผ ๊ฐ€์ ธ์˜ด - ํ•ธ๋“œํฐ์—์„œ ์นด๋ฉ”๋ผ๋ฅผ ์ฐพ์„๋–„, videoDeviceDiscoverySession ์ค‘ ์ œ์ผ ๋งˆ์ง€๋ง‰ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋ช…์‹œ๋œ ๋ถ€๋ถ„์„ ์ฐพ์œผ๋ผ๋Š” ์˜๋ฏธ
                captureSession.commitConfiguration() // ๋ชป์ฐพ์•˜์œผ๋ฉด ๊ทธ๋ƒฅ ์ข…๋ฃŒํ•˜๊ฒŒ๋”
                return
            }
            let videoDeviceInput = try AVCaptureDeviceInput(device: camera)
            
            if captureSession.canAddInput(videoDeviceInput) {
                captureSession.addInput(videoDeviceInput)
                self.videoDeviceInput = videoDeviceInput
            } else {
                captureSession.commitConfiguration()
                return
            }
        } catch let error{
            captureSession.commitConfiguration()
            return
        }
        
        // -Add photo Output
        photoOutput.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil) // ์‚ฌ์ง„์„ ์–ด๋–ค ํ˜•์‹์œผ๋กœ ์ €์žฅํ• ์ง€ ๋ฏธ๋ฆฌ ์„ธํŒ…ํ•ด๋‘๊ธฐ
        if captureSession.canAddOutput(photoOutput) {
            captureSession.addOutput(photoOutput)
        } else {
            captureSession.commitConfiguration()
            return
        }
        
        captureSession.commitConfiguration() // ๊ตฌ์„ฑ ์™„๋ฃŒํ–ˆ๋‹ค๊ณ  ์•Œ๋ฆผ
    }

 

์œ„์— ๋ณด๋‹ค์‹œํ”ผ ์ฝ”๋“œ๊ฐ€ ๊ธธ๊ณ  ์–ด๋ ค์›Œ ๋ณด์ด์ง€? 

โœ… ์บก์ณ ์„ธ์…˜ ๊ตฌ์„ฑ์—์„œ 5๊ฐ€์ง€ ํ๋ฆ„์œผ๋กœ ๊ตฌ์„ฑ์ด ๋˜๋Š”๋ฐ ํ•œ๋ฒˆ ๋ณผ๊นŒ? (captureSession ๊ตฌ์„ฑํ•˜๊ธฐ)

- presetSetting : ๋ฏธ๋””์–ด ์บก์ณ๋ฅผ ํ• ๋•Œ, ์‚ฌ์ง„์„ ์ฐ์„์ˆ˜๋„ ์žˆ๊ณ , ์˜์ƒ์„ ์ฐ์„์ˆ˜๋„ ์žˆ๊ณ  ํ•ด์ƒ๋„๋ฅผ ์ •ํ• ์ˆ˜๋„ ์žˆ๊ณ  ๋“ฑ๋“ฑ ํ•˜๋‹ˆ๊นŒ ๊ทธ์— ๋Œ€ํ•œ ๊ฒƒ์„ ๋จผ์ € ์„ค์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

- beginConfiguration : ์‹คํ–‰์ค‘์ธ ์„ธ์…˜์—์„œ ์—ฌ๋Ÿฌ ๊ตฌ์„ฑ ์ž‘์—…์„ ๋ฐฐ์น˜, commitConfiguration ์‹คํ–‰๋˜๊ธฐ ์ „๊นŒ์ง€ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์ ์šฉ๋˜์ง€ ์•Š์Œ

- Add Video Input :

- Add Photo Output :

- commitConfiguration : beginConfiguration๊ณผ ์ด ์ฝ”๋“œ ์‚ฌ์ด์—์„œ ์„ค์ •๋œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์‹ค์ œ๋กœ ์ ์šฉ ์‹œ์ผœ์คŒ

 

์ฝ”๋“œ์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด๋ฉด, ์ฝ”๋“œ์—์„œ๋„ ์‚ฌ์ง„์„ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด๋ผ sessionPreset ๋ถ€๋ถ„์— photo๋ฅผ ๋„ฃ์–ด์ฃผ๊ณ , ๊ทธ ๋‹ค์Œ์— beginConfiguration์„ ํ•˜๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด.

 

๊ทธ ๋‹ค์Œ์€ ์„ธ์…˜์„ ์ •ํ•ด์ฃผ๊ณ  ์‹ค์ œ๋กœ ์–ด๋–ค ์นด๋ฉ”๋ผ๋ฅผ ์“ธ๊ฑด์ง€ ์•Œ ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜์ž–์•„? 

AVCaptureDevice.default์— ๋Œ€ํ•œ ๊ณต์‹๋ฌธ์„œ์˜ ์„ค๋ช…์„ ๋ณด๋ฉด

 

"์ง€์ •๋œ ์žฅ์น˜ ์œ ํ˜•, ๋ฏธ๋””์–ด ์œ ํ˜• ๋ฐ ์œ„์น˜์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์žฅ์น˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."

 

๋ผ๊ณ  ๋งํ•ด์ฃผ๊ณ  ์žˆ์–ด -> ์ด ๋ถ€๋ถ„์€ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์“ฑ ์ฝ์–ด๋ณด๋ฉด ์•„์ฃผ ์ดํ•ด๊ฐ€ ์ž˜๋˜๋‹ˆ๊นŒ ์ง์ ‘ ์ฐพ์•„๋ด๋„ ์ข‹์„๊ฑฐ ๊ฐ™์•„.

 

๊ทธ ๋‹ค์Œ์ด guard๋ฌธ์ธ๋ฐ, ๋””๋ฐ”์ด์Šค์—์„œ ์นด๋ฉ”๋ผ๋ฅผ ์ฐพ์„๊ฑด๋ฐ, ๋””๋ฐ”์ด์Šค์—์„œ ์นด๋ฉ”๋ผ๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ๊ฒ€์ƒ‰๋  ์ˆ˜๋„ ์žˆ์–ด. (์ „๋ฉด, ํ›„๋ฉด-๋“€์–ผ ๋“ฑ) ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” first๋ฅผ ํ†ตํ•ด ์นด๋ฉ”๋ผ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ด์šฉํ•ด

 

๊ทธ ๋‹ค์Œ์€ captureDeviceInput์— ๋Œ€ํ•œ๊ฑด๋ฐ, ์ด๊ฒƒ๋„ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ๋กœ ๋„ฃ์–ด ๋‘์—ˆ์–ด

๊ณต์‹๋ฌธ์„œ์˜ ์„ค๋ช…์— ๋”ฐ๋ฅด๋ฉด

 

"์บก์ฒ˜ ์„ธ์…˜์— ๋Œ€ํ•œ ์ž…๋ ฅ (์˜ˆ : ์˜ค๋””์˜ค ๋˜๋Š” ๋น„๋””์˜ค)์„ ์ œ๊ณตํ•˜๊ณ  ํ•˜๋“œ์›จ์–ด ๋ณ„ ์บก์ฒ˜ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ œ์–ด๋ฅผ ์ œ๊ณตํ•˜๋Š” ์žฅ์น˜์ž…๋‹ˆ๋‹ค."

 

๋ผ๊ณ  ์–ธ๊ธ‰๋˜์–ด ์žˆ์–ด.

๊ทธ๋Ÿฌ๋‹ˆ๊นŒ ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ฆฌํ•ด๋ณด์ž๋ฉด

1. AVCaptureDevice ํ†ตํ•ด ๋””๋ฐ”์ด์Šค ์นด๋ฉ”๋ผ์˜ ์–ด๋–ค ๋ถ€๋ถ„ ์“ธ๊ฑด์ง€ ์ง€์ •ํ•ด์ฃผ๊ณ 

2. videoDeviceDiscoverySession ํ†ตํ•ด์„œ 1์˜ ๊ณผ์ •์—์„œ ์„ค์ •ํ•œ ์นด๋ฉ”๋ผ๋ฅผ ์šฐ๋ฆฌ ๋””๋ฐ”์ด์Šค์—์„œ ์‹ค์ œ๋กœ ์ฐพ์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ •๋ณด๋ฅผ ํ™•์ธํ•ด์ค€ ๋’ค

3. AVCaptureDeviceInput ํ†ตํ•ด์„œ ์‹ค์ œ ๊ธฐ๊ธฐ์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์ธํ’‹์œผ๋กœ ๋ฐ›์•„๋“ค์ผ ์นด๋ฉ”๋ผ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋Š” ๊ฒƒ๊นŒ์ง€์•ผ

 

๊ทธ ์ดํ›„๋กœ๋Š” CanAddInput์„ ํ†ตํ•ด ์„ธ์…˜์— ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š”์ง€ ๋ฌผ์–ด๋ณธ ๋‹ค์Œ์— (์‹คํŒจํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์ด์—ฌ์„œ ๋จผ์ € ๋ฌผ์–ด๋ณด๋Š”๊ฒŒ ์šฐ์„ )

์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์–ด ๋‘” ์บก์ณ์„ธ์…˜์— ์นด๋ฉ”๋ผ๋ฅผ ๋ถ™์ธ๋‹ค! 

 

๋‹ค์Œ์€ photoOut์— ๋Œ€ํ•œ ๋ถ€๋ถ„์ธ๋ฐ ์ด ๋ถ€๋ถ„์€ ์‚ฌ์ง„์„ ์ฐ์—ˆ์œผ๋ฉด ์ด์ œ ์–ด๋””๋กœ ๋‚ด๋ณด๋‚ด์•ผํ• ๊ฑฐ ์•„๋ƒ ๊ทธ์น˜?

์šฐ๋ฆฌ๊ฐ€ ์ฐ์€์‚ฌ์ง„์˜ ํฌ๋งท์„ ์ง€์ •ํ•œ ํ›„ ๋‚ด๋ณด๋‚ด์ฃผ๋ฉด ๋!

 

๐Ÿ”ธ ์„ธ์…˜์„ ์ด์šฉํ•  ๋•Œ, ๋น„๊ธด์„ ์„ธ์…˜์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์„ ์ €์žฅํ•˜๋Š” ๊ฑฐ๊ณ  ์„ธ์…˜์„ ๊ผญ isRunning์ด๋‚˜ isStop์„ ํ†ตํ•ด ํ™•์ธํ•˜๊ณ  ์‹คํ–‰ํ•ด์ฃผ์–ด์•ผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

์„ธ์…˜ ์ฝ”๋“œ
๋ทฐ๋””๋“œ๋กœ๋“œ์—์„œ ์„ธ์…˜ํ๋ฅผ ์ด์šฉํ•œ๋‹ค๊ณ 

 

โœ… 3. capturePhoto ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์‚ฌ์ง„์„ ์ฐ์–ด์•ผ๊ฒ ์ง€? ์ด๊ฑด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”๊ฑธ๊นŒ

๋ฏธ๋ฆฌ ์•Œ๊ณ ๊ฐ€์ž! preview๋Š” ์ด๋ ‡๊ฒŒ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„๋‹ค.

ํ”„๋ฆฌ๋ทฐ๊ฐ€ ์—ฐ๊ฒฐ๋œ ์Šคํ† ๋ฆฌ๋ณด๋“œ์— ๋Œ€ํ•œ ๋‚ด์šฉ

   @IBAction func capturePhoto(_ sender: UIButton) {
        // TODO: photoOutput์˜ capturePhoto ๋ฉ”์†Œ๋“œ
        // orientation
        // photooutput
        
        let videoPreviewLayerOrientation = self.previewView.videoPreviewLayer.connection?.videoOrientation // ์‚ฌ์ง„์„ ๋’ค์ง‘์–ด์„œ ์ฐ์„ ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ํ˜„์žฌ ํ”„๋ฆฌ๋ทฐ๋ ˆ์ด์–ด๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š”๊ฑธ ๊ทธ๋Œ€๋กœ ์ ์šฉํ•œ๋‹ค.
        sessionQueue.async {
            // ๋ฏธ๋””์–ด์—์„œ ๋“ค์–ด์˜จ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ์ง„์ด ๋˜์–ด์„œ ๋ฐ”๊นฅ์œผ๋กœ ๋‚˜๊ฐ€์•ผํ•˜๋Š”๋ฐ
            let connection = self.photoOutput.connection(with: .video) // ์•„์›ƒํ’‹ ๊ฒฐ๊ณผ๋ฅผ ์ปค๋„ฅ์…˜์„ ํ†ตํ•ด ์—ฐ๊ฒฐ
            connection?.videoOrientation = videoPreviewLayerOrientation! // ์‚ฌ์ง„์— ๋Œ€ํ•œ ์˜ค๋ฆฌ์—”ํ…Œ์ด์…˜์„ ์„ค์ •
            
            let setting = AVCapturePhotoSettings() // ์บก์ณํฌํ†  ์„ธํŒ….
            self.photoOutput.capturePhoto(with: setting, delegate: self) // ์‚ฌ์ง„์ฐ์ž๊ณ  ํฌํ†  ์•„์›ƒํ’‹์— ์•Œ๋ ค์ฃผ๋Š”๊ฑฐ
            // ์‚ฌ์ง„ ์ฐ์„๋•Œ, ํ›„์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค„ ์ˆ˜ ์žˆ๋Š”๋ฐ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์…€ํ”„๋กœ ํ•ด์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋Š”๋ฐ ๊ทธ๊ฑธ๋กœ ํ•ด๊ฒฐํ•ด์ฃผ๊ธฐ
        }
        

    }

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด์ž.

์‚ฌ์ง„์„ ์ฐ์—ˆ์„ ๋•Œ, ์šฐ๋ฆฌ๋Š” ์šฐ์„  Orientation ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•„์•ผ ํ•ด.

Orientation ๋ถ€๋ถ„์€ ์‰ฝ๊ฒŒ ๋งํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ํ™”๋ฉด์„ ๋’ค์ง‘์–ด์„œ ์ฐ์„ ์ˆ˜๋„ ์žˆ์„๊ฑฐ ์•„๋‹ˆ์•ผ? ๊ทธ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅด ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ๋ช…์‹œํ•˜๋Š” ๋ถ€๋ถ„์ด์•ผ.

์• ํ”Œ ๊ณต์‹ ๋ฌธ์„œ

 

์ €๊ธฐ ์„ค๋ช…์„ ๋ณด๋ฉด 

 

"์บก์ฒ˜ ๋œ ๋น„๋””์˜ค๋ฅผ ํ‘œ์‹œํ•˜๋Š” ํ•ต์‹ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ ˆ์ด์–ด์ž…๋‹ˆ๋‹ค."

 

๋ผ๊ณ  ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋‹ค. ๊ทธ๋งŒํผ ์ค‘์š”ํ•˜๋‹ค๋Š” ๋ง!! ์ด๊ฑด ์ฐธ๊ณ  ๋ถ€๋ถ„์— ๋„ฃ์–ด ๋‘์—ˆ๋Š”๋ฐ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฝ์–ด๊ฐ€๋ฉด์„œ ์ฝ”๋“œ๋ฅผ ์ฝ์–ด๋ณด๋ฉด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด.

Preview์ชฝ๋„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด ๋‘์—ˆ๋Š”๋ฐ, ์ž ์‹œ ํ›„์— ๋ณด๊ธฐ๋กœ ํ•˜๊ณ  ์šฐ์„  ์ด ์ฝ”๋“œ์— ๋งž๊ฒŒ๋” ์•Œ์•„๋ณด์ž.

์ €๊ธฐ ๋ณด๋ฉด connection์ด๋ผ๋Š” ์ฝ”๋“œ๊ฐ€ ๋ณด์ด์ง€? ์ €๊ฑด ์šฐ๋ฆฌ๊ฐ€ ํฌํ†  ์•„์›ƒํ’‹์œผ๋กœ ์ฐ์€ ๊ฒฐ๊ณผ๋ฅผ ์ปค๋„ฅ์…˜์„ ํ†ตํ•ด ์—ฐ๊ฒฐํ•˜๋Š” ์ฝ”๋“œ์•ผ.

์ด ๋ถ€๋ถ„๋„ ์ฐธ๊ณ ์ชฝ์— ๋„ฃ์–ด๋‘์—ˆ์œผ๋‹ˆ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•ด ๋ณด๊ธธ!!

 

๊ทธ ๋‹ค์Œ์€ ์˜ค๋ฆฌ์—”ํŠธ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ธ๋ฐ ์‚ฌ์ง„์ด ๋’ค๋Œ์•„ ์žˆ์„ ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•ด์ค„์ง€์— ๋Œ€ํ•œ ์ฝ”๋“œ์•ผ.

๊ทธ ๋‹ค์Œ์€ AVCapturePhotoSettings() ์ธ๋ฐ ์ด๊ฑด ์‚ฌ์ง„์„ ์ฐ์€ ํ›„์— ์–ด๋–ค ์ž‘์—…(ํ›„๋ณด์ •)์„ ํ•ด์ค„๊ฑด์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ค„ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ์•ผ.

์ฆ‰, ์šฐ๋ฆฌ๊ฐ€ ์ฐ์€ ์‚ฌ์ง„์— ๋Œ€ํ•ด์„œ ํ•„ํ„ฐ๋ฅผ ์”Œ์šฐ๊ฑฐ๋‚˜ ์›Œํ„ฐ๋งˆํฌ ๋“ฑ์„ ์ค„๋•Œ, ์ €๋ ‡๊ฒŒ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด.

delegate ๋ถ€๋ถ„์€ extension์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์กฐ๊ธˆ ์ด๋”ฐ๊ฐ€ ๋ณด์ž.

 

๊ทธ๋Ÿผ ๋‹ค์‹œ ์ •๋ฆฌํ•˜์ž๋ฉด,

์˜ค๋ฆฌ์—”ํŠธ๋ฅผ ํ†ตํ•ด preview์— ์šฐ๋ฆฌ๊ฐ€ ๋ณด์—ฌ์ค„ ํ™”๋ฉด์„ ์•Œ๋ ค์ฃผ๊ณ 

์‚ฌ์ง„์„ ์ฐ์€ ๊ฒฐ๊ณผ๋Š” ํ›„๋ณด์ •์„ ํ†ตํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

โœ… 4. ์‚ฌ์ง„์„ ์ฐ์—ˆ์œผ๋ฉด ํ›„๋ณด์ •? ๊ทธ๊ฑด ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฑด๋ฐ?

extension CameraViewController: AVCapturePhotoCaptureDelegate {
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { // ์‚ฌ์ง„์ด ๋‹ค ์ฐํ˜”์„๋•Œ, ํ›„์ฒ˜๋ฆฌ
        // TODO: capturePhoto delegate method ๊ตฌํ˜„
        
        guard error == nil else { return }
        guard let imageData = photo.fileDataRepresentation() else { return }
        guard let image = UIImage(data: imageData) else { return }
        self.savePhotoLibrary(image: image)
        
        
    }
}

์œ„์—์„œ delegate๋ฅผ ์—ฐ๊ฒฐํ•œ ๋ถ€๋ถ„์€ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.

์นด๋ฉ”๋ผ ํ›„๋ณด์ •๊ณผ ๊ด€๋ จํ•œ ๋ฉ”์†Œ๋“œ

์นด๋ฉ”๋ผ ํ›„๋ณด์ •๊ณผ ๊ด€๋ จํ•œ ๋ฉ”์†Œ๋“œ์ธ๋ฐ, ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์ง„ ์ฐ๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ , ์ด๋Ÿฐ ๋กœ์ง์— ์˜ํ•ด์„œ ์‹คํ–‰ ๋ผ.

์ž์„ธํ•œ ์ •๋ณด๋Š” ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฝ์–ด๋ณด๋ฉด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด.

์ €๊ธฐ ๊ฐ€๋ฉด ๋ผ์ด๋ธŒ ํฌํ† , ์˜์ƒ ๋“ฑ ๋กœ์ง์ด ์กฐ๊ธˆ์”ฉ ๋‹ค๋ฅธ๋ฐ ์ฝ์–ด๋ณด๊ธฐ.

 

์šฐ๋ฆฌ๋Š” didFinishProcessingPhoto ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•  ์˜ˆ์ •์ธ๋ฐ guard๋ฌธ์„ ๋ช‡๋ช‡ ์กฐ๊ฑด์„ ์ฒ˜๋ฆฌํ•œ ํ›„ ๋„˜์–ด๊ฐ€

fileDataRepresentation ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ์—๋Š” ์• ํ”Œ ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด

 

"์‚ฌ์ง„ ๋ฐ ์ฒจ๋ถ€ ํŒŒ์ผ์˜ ํ”Œ๋žซ ๋ฐ์ดํ„ฐ ํ‘œํ˜„์„ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."

 

๋ผ๊ณ  ๋งํ•˜๊ณ  ์žˆ์–ด.  

 

์• ํ”Œ ๊ณต์‹ ๋ฌธ์„œ

์ž์„ธํ•œ๊ฑด ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋” ์ฐพ์•„๋ณด๋ฉด ๋„์›€์ด ๋˜๊ฒ ์ง€!!

์ •๋ฆฌํ•˜์ž๋ฉด, 

1. photo ํƒ€์ž…์ด AVCapturePhoto ํƒ€์ž…์ด๋ผ์„œ 

2. fileDataRepresentation ํ†ตํ•ด Data ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝ

3. UIImage๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์ด๋ฏธ์ง€๋กœ ๋ณ€๊ฒฝ 

4. ๊ทธ ์ดํ›„์— ์ €์žฅ!! ์ด๋Ÿฐ ๋กœ์ง์ด์•ผ

 

โœ… 5. ์‚ฌ์ง„์ฐ๊ณ  ํ›„๋ณด์ •๋„ ํ–ˆ์–ด, ๊ทธ๋Ÿผ ์ด๋ฒˆ์—๋Š” ์‚ฌ์ง„์„ ์ €์žฅํ•ด๋ณผ๊นŒ?

   func savePhotoLibrary(image: UIImage) {
        // TODO: captureํ•œ ์ด๋ฏธ์ง€ ํฌํ† ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ €์žฅ
        PHPhotoLibrary.requestAuthorization { status in
            if status == .authorized {
                // ์ €์žฅ
                PHPhotoLibrary.shared().performChanges {
                    PHAssetChangeRequest.creationRequestForAsset(from: image) // ์ด๋ฏธ์ง€๋ฅผ ํฌํ†  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ƒ์„ฑํ•˜๊ฒ ๋‹ค
                } completionHandler: { (success, error) in
                    print("์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ --> \(success)")
                }

            } else {
                // ๋‹ค์‹œ ์š”์ฒญ
                print("--> ๊ถŒํ•œ์„ ์–ป์ง€ ๋ชปํ•จ")
            }
        }
    }

์‚ฌ์ง„์„ ์šฐ๋ฆฌ์˜ ๊ฐค๋Ÿฌ๋ฆฌ์— ์ €์žฅํ•ด์•ผ๊ฒ ์ง€?

์ €์žฅํ•˜๊ธฐ ์ „์—๋Š” ํ•ญ์ƒ ๊ถŒํ•œ์„ ์–ป์–ด์•ผ ํ•˜๋Š”๋ฐ, ๊ถŒํ•œ์„ ํ™•์ธํ•œ ํ›„์— ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„.

์—ฌ๊ธฐ์„œ ๊ถŒํ•œ์ด ์กฐ๊ธˆ ์‹ ๊ฒฝ์“ฐ์ด๋Š” ๊ฒƒ์ด, ์šฐ๋ฆฌ๊ฐ€ ์‹œ์Šคํ…œ ์นด๋ฉ”๋ผ๋ฅผ ์‚ฌ์šฉํ• ๋•Œ๋Š” ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์ด์™€ ๋‹ฌ๋ž๋Š”๋ฐ, ์‹ค์ œ๋กœ ๊ถŒํ•œ์„ ์–ป๋Š” ๋ถ€๋ถ„์— ์žˆ์–ด์„œ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„๋‘์ž!

 

์ด ์ฝ”๋“œ๋Š” ์• ํ”Œ ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด shared๋Š” ๊ณต์œ  ์‚ฌ์ง„ ์ €์žฅ์†Œ์— ๋Œ€ํ•œ ์‹ฑ๊ธ€ํ†ค ํ‘œํ˜„์ด๋ฉฐ, performChages ์‚ฌ์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์„ ๋น„๋™๊ธฐ๋กœ ์š”์ฒญํ•˜๋Š” ๋ธ”๋ก์ด๋ฉฐ, PHAssetChangeRequest๋Š” ์‚ฌ์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ƒ์„ฑ, ์‚ญ์ œ, ๋ณ€๊ฒฝ, ํŽธ์ง‘์„ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ๊ทธ์ค‘ creationRequestForAsset์„ ํ†ตํ•ด ์ƒ์„ฑ์„ ๋‹ด๋‹นํ•œ๋‹ค.

 

์• ํ”Œ ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ถŒํ•œ ๋ฐ ์•ก์„ธ์Šค ๋ณ€๊ฒฝ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค.

์• ํ”Œ ๊ณต์‹ ๋ฌธ์„œ

 

โœ… 6. ์ „๋ฉด, ํ›„๋ฉด ์นด๋ฉ”๋ผ ๋“ฑ ์ „ํ™˜ํ•  ๋•Œ์˜ ์ฝ”๋“œ

์—ฌ๊ธฐ์—๋Š” ์ „๋ฉด๋ถ€ ํ›„๋ฉด๋ถ€ ์‚ฌ์šฉ์— ๋”ฐ๋ผ ์นด๋ฉ”๋ผ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ด์•ผ.

   @IBAction func switchCamera(sender: Any) {
        // TODO: ์นด๋ฉ”๋ผ๋Š” 1๊ฐœ ์ด์ƒ์ด์–ด์•ผํ•จ
        guard videoDeviceDiscoverySession.devices.count > 1 else {
            return
        }
        
        // TODO: ๋ฐ˜๋Œ€ ์นด๋ฉ”๋ผ ์ฐพ์•„์„œ ์žฌ์„ค์ •
        // - ๋ฐ˜๋Œ€ ์นด๋ฉ”๋ผ ์ฐพ๊ณ 
        // - ์ƒˆ๋กœ์šด ๋””๋ฐ”์ด์Šค ๊ฐ€์ง€๊ณ  ์„ธ์…˜ ์—…๋ฐ์ดํŠธ
        // - ์นด๋ฉ”๋ผ ํ† ๊ธ€ ๋ฒ„ํŠผ ์—…๋ฐ์ดํŠธ
        
        sessionQueue.async {
            let currentvideoDevice = self.videoDeviceInput.device // ํ˜„์žฌ ์นด๋ฉ”๋ผ๊ฐ€ ๋ญ”์ง€ ์•Œ์•„์•ผ์ง€
            let currentPosition = currentvideoDevice.position // ์นด๋ฉ”๋ผ๊ฐ€ ์•ž์ธ์ง€ ๋’ค์ธ์ง€๋„ ์•Œ์•„์•ผ์ง€
            let isFornt = currentPosition == .front // ์ „๋ฉด ์นด๋ฉ”๋ผ ์ธ๊ฐ€์š”?
            let preferredPosition : AVCaptureDevice.Position = isFornt ? .back : .front
            
            let devices = self.videoDeviceDiscoverySession.devices
            var newVideoDevice : AVCaptureDevice?
            
            newVideoDevice = devices.first(where: { device in
                return preferredPosition == device.position
            })
            
            // update capture session
            
            if let newDevice = newVideoDevice {
                do {
                    
                    let videoDeviceInput = try AVCaptureDeviceInput(device: newDevice)
                    self.captureSession.beginConfiguration()
                    self.captureSession.removeInput(self.videoDeviceInput)
                    
                    // add new device input
                    if self.captureSession.canAddInput(videoDeviceInput) {
                        self.captureSession.addInput(videoDeviceInput)
                        self.videoDeviceInput = videoDeviceInput
                    } else {
                        self.captureSession.addInput(videoDeviceInput)
                    }
                    self.captureSession.commitConfiguration()
                    
                    DispatchQueue.main.async {
                        self.updateSwitchCameraIcon(position: preferredPosition) // ์ด ํฌ์ง€์…˜์— ๋”ฐ๋ผ์„œ ์•„์ด์ฝ˜์„ ์—…๋ฐ์ดํŠธ ์‹œํ‚จ๋‹ค๋Š” ์ด์•ผ๊ธฐ
                    }
                    
                } catch let error{
                    print(" error occured while creating device input : \(error.localizedDescription)")
                }
                
            }
        }
    }

์šฐ์„  ์„ธ์…˜์— ์นด๋ฉ”๋ผ๊ฐ€ 1๊ฐœ ์ดˆ๊ณผ(์ „๋ฉด, ํ›„๋ฉด) ์ฆ‰, ์ตœ์†Œ 2๊ฐœ๋Š” ์žˆ์–ด์•ผ๊ฒ ์ง€? - ์ฃผ์„์ด ์กฐ๊ธˆ ์ž˜๋ชป๋‹ฌ๋ฆผ.

ํ˜„์žฌ ์‚ฌ์šฉํ•˜๋Š” ์นด๋ฉ”๋ผ๊ฐ€ ์–ด๋–ค๊ฑด์ง€ ์•Œ์•„๋‚ด์•ผ์ง€ ์ฝ”๋“œ๋ฅผ ์ญ‰ ์ฝ์–ด๋ณด๋ฉด ์‰ฝ์ง„ ์•Š์ง€๋งŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด.

์ „,ํ›„๋ฉด ์ „ํ™˜์‹œ์—๋Š” ํ˜„์žฌ ๋ถ™์–ด์žˆ๋Š” ์นด๋ฉ”๋ผ๋ฅผ ์ง€์šด๋’ค์— ๋‹ค์‹œ addํ•ด์•ผ ํ•ด

 

โœ… 7. ์ฝ”๋“œ์— ๋ฆฌํ„ฐ๋Ÿด(literal)์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ• + setupUI()

๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ

์ด๋ฏธ์ง€๋ž‘ ์ปฌ๋Ÿฌ ๋“ฑ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, #image literal ๋˜๋Š” #color literal ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ €๋ ‡๊ฒŒ ๋„ค๋ชจ ๋ชจ์–‘์œผ๋กœ ๋ฐ”๋€Œ๊ณ  ์ € ๋„ค๋ชจ ๋ชจ์–‘์„ ๋”๋ธ” ํด๋ฆญํ•ด์„œ ๋ณ€๊ฒฝํ•  ์ˆ˜๊ฐ€ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์–ด..!

setupUI() ์ชฝ์€ ๋‘ฅ๊ธ€๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ์ธ๋ฐ, ์ด๊ฑด ์ง์ ‘ ์ฐพ์•„๋ณด์ž

 

โœ… 8. preview.swift ํŒŒ์ผ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณผ๊นŒ?

// Preview.swift

class PreviewView: UIView {
    var videoPreviewLayer: AVCaptureVideoPreviewLayer {
        guard let layer = layer as? AVCaptureVideoPreviewLayer else {
            fatalError("Expected `AVCaptureVideoPreviewLayer` type for layer. Check PreviewView.layerClass implementation.")
        }
        
        layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        layer.connection?.videoOrientation = .portrait
        return layer
    }
    
    var session: AVCaptureSession? {
        get {
            return videoPreviewLayer.session
        }
        set {
            videoPreviewLayer.session = newValue
        }
    }
    
    // MARK: UIView
    
    override class var layerClass: AnyClass {
        return AVCaptureVideoPreviewLayer.self
    }
}

์•„๊นŒ๋„ ๊ณ„์† ์–ธ๊ธ‰ํ–ˆ์—ˆ๋Š”๋ฐ, ์ด์ œ์•ผ ์—ฌ๊ธฐ๋ฅผ ๋ณด๊ธฐ๋กœ ํ•ด์š”.

์ด๋ถ€๋ถ„์€ ์˜ค๋ฆฌ์—”ํŠธ ๊ด€๋ จํ•œ ๋ถ€๋ถ„์ธ๋ฐ, ๊ฐ•์˜์—์„œ๋Š” ๋”ฐ๋กœ ์„ค๋ช…์ด ์—†์—ˆ์–ด. ๊ทธ๋Ÿฐ๋ฐ ์ •๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š๊ณ  ๋„˜์–ด๊ฐˆ ์ˆ˜๋Š” ์—†๊ฒ ์ง€?

AVCaptureSession ๋ถ€๋ถ„์€ ๋ฏธ๋ฆฌ ๋ณธ ์บก์ณ ์„ธ์…˜ ๋ถ€๋ถ„์ด๋‹ค.

 

"AVCaptureVideoPreviewLayer is a subclass of CALayer that you use to display video as it’s captured by an input device."

 

์‰ฝ๊ฒŒ ๋งํ•ด์„œ ์ž…๋ ฅ ์žฅ์น˜์—์„œ ์บก์ฒ˜ ํ•œ ๋น„๋””์˜ค๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” CALayer์˜ ํ•˜์œ„ ํด๋ž˜์Šค๋ž€ ์˜๋ฏธ์ด๋‹ค.

 

๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด videoGravity ๋ถ€๋ถ„์„ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋ ˆ์ด์–ด๊ฐ€ ํ™”๋ฉด ๋‚ด์—์„œ ์ปจํ…์ธ ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋ถ€๋ถ„์ด๋‹ค.

๊ทผ๋ฐ ์ด๊ฒŒ default ๊ฐ’์ด resizeAspect๋ผ์„œ ๋ฐ”๊ฟ”์ค€ ๋ชจ์Šต์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  .portrait์ด ๋ณด์ด์ง€? ์ €๊ฑด ์–ด๋–ป๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋ƒ? ์ด๋Ÿฐ ๋ง์ธ๋ฐ, ์†”์งํžˆ ์™€๋‹ฟ์ง€๋Š” ์•Š๋Š”๋ฐ, ์ €๊ฑธ ์ž‘์„ฑํ•˜๋ฉด ํœด๋Œ€ํฐ์„ ๊ฐ€๋กœ๋กœ ์ฐ์–ด๋„ ๊ฐค๋Ÿฌ๋ฆฌ์— ๊ฐ€๋กœ๋กœ ์ €์žฅ๋˜์ง€, ๊ตณ์ด ๊ฐ€๋กœ๋กœ ์ฐ์€ ์‚ฌ์ง„์„ ์„ธ๋กœ๋กœ ๋Œ๋ ค์„œ ๊ฐค๋Ÿฌ๋ฆฌ์— ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ง ๊ฐ™๋‹ค.(์ถ”์ธก)

 

๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์—

   override class var layerClass: AnyClass {
        return AVCaptureVideoPreviewLayer.self
    }

์ด ๋ถ€๋ถ„์€ ๊ตฌ๊ธ€๋ง ํ•ด๋ณธ ๊ฒฐ๊ณผ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ์„ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋งค์šฐ ์ค‘์š”ํ•œ ์ฝ”๋“œ๋ผ๊ณ  ํ•œ๋‹ค.

 

 

ํ›„,, ์šฐ์„  ์ด์ •๋„๋กœ ์ฝ”๋“œ๋ฆฌ๋ทฐ๋ฅผ ๋งˆ์น˜๊ธฐ๋กœ ํ•˜๊ณ , ๊ฐ•์˜์—์„œ๋Š” Learning AV Foundation ์ฑ…์„ ์ถ”์ฒœํ•ด ์ฃผ์…จ๋Š”๋ฐ, ๊นƒํ—ˆ๋ธŒ์— ๋‹ค๋ฅธ ๋ถ„์ด ๋จผ์ € ๊ณต๋ถ€ํ•˜๊ณ  ๋‚จ๊ธด ๊ธ€์ด ์žˆ์–ด ์ด๊ฑธ ์ฐธ๊ณ ํ•œ๋‹ค๋ฉด ๋” ํ™•์žฅํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ ์‹ถ๋‹ค.

https://github.com/Kiboom/AVFoundation

 

Kiboom/AVFoundation

Learning AV Foundation ํ•œ๊ธ€ ์š”์•ฝ ์ •๋ฆฌ. Contribute to Kiboom/AVFoundation development by creating an account on GitHub.

github.com

 

 

 

 

 

(์ฐธ๊ณ )

https://developer.apple.com/documentation/avfoundation/avcapturedevice/discoverysession

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/avcapturedevice/discoverysession/2361539-init

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/avcapturedevice

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/avcapturedeviceinput

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer/1390893-connection

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/avcam_building_a_camera_app

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/avfoundation/avcapturephoto/2873919-filedatarepresentation

 

Apple Developer Documentation

 

developer.apple.com

https://hyunsikwon.github.io/ios/iOS-AVCaptureSession/

 

์นด๋ฉ”๋ผ ์•ฑ์„ ์œ„ํ•œ AVCaptureSession!

Overview

hyunsikwon.github.io

 

Comments