์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
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 |
- visionOS
- tableView
- Xcode
- MVVM
- combine
- rxcocoa
- SnapKit
- ios
- RxSwift
- realm
- Swfit
- TCA
- ํจ์คํธ์บ ํผ์ค
- BOJ
- raywenderlich
- arkit
- Kuring
- ๋ฐฑ์ค
- XCTest
- Lv2
- node.js
- SwiftUI
- CollectionView
- BFS
- swift
- ํ๋ก๊ทธ๋๋จธ์ค
- UIKit
- Flutter
- designpattern
- reactorkit
- Today
- Total
lgvv98
๐ธ ch18 FullScreen ์นด๋ฉ๋ผ ์ฑ ์ฝ๋๋ฆฌ๋ทฐ ๋ณธ๋ฌธ
๐ธ 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
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
'โ ๏ธ deprecated โ ๏ธ > ํจ์บ (์ฌ์ธ์)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ch19 ๐ค CreateML ์ฌ์ฉ ๋ฐ ์ฝ๋๋ฆฌ๋ทฐ (0) | 2021.07.19 |
---|---|
ch19 ๐ค CoreML (0) | 2021.07.19 |
๐ธ ch 18 AVFoundation ์นดํ ๊ณ ๋ฆฌ ๋ณ๋ก ํ๊ตฌ (0) | 2021.07.05 |
๐ฌ ch17 Netflix ํ์ฅ์ฑ ์ฝ๋๋ฆฌ๋ทฐ(firebase, kingfisher) + ch15 (0) | 2021.07.05 |
ch17 ๋์ ScrollView Guide! ์ํ์ข์ฐ!! (0) | 2021.06.29 |