CameraView.swift 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. //
  2. // CameraView.swift
  3. // ALCameraViewController
  4. //
  5. // Created by Alex Littlejohn on 2015/06/17.
  6. // Copyright (c) 2015 zero. All rights reserved.
  7. //
  8. import UIKit
  9. import AVFoundation
  10. public class CameraView: UIView {
  11. var session: AVCaptureSession!
  12. var input: AVCaptureDeviceInput!
  13. var device: AVCaptureDevice!
  14. var imageOutput: AVCaptureStillImageOutput!
  15. var preview: AVCaptureVideoPreviewLayer!
  16. let cameraQueue = DispatchQueue(label: "com.zero.ALCameraViewController.Queue")
  17. let focusView = CropOverlay(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
  18. public var currentPosition = CameraGlobals.shared.defaultCameraPosition
  19. public func startSession() {
  20. session = AVCaptureSession()
  21. session.sessionPreset = AVCaptureSession.Preset.photo
  22. device = cameraWithPosition(position: currentPosition)
  23. if device == nil {
  24. print("Error: No Camera device found")
  25. return
  26. }
  27. if device.hasFlash {
  28. do {
  29. try device.lockForConfiguration()
  30. device.flashMode = .auto
  31. device.unlockForConfiguration()
  32. } catch _ {}
  33. }
  34. let outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
  35. do {
  36. input = try AVCaptureDeviceInput(device: device)
  37. } catch let error as NSError {
  38. input = nil
  39. print("Error: \(error.localizedDescription)")
  40. return
  41. }
  42. if session.canAddInput(input) {
  43. session.addInput(input)
  44. }
  45. imageOutput = AVCaptureStillImageOutput()
  46. imageOutput.outputSettings = outputSettings
  47. session.addOutput(imageOutput)
  48. cameraQueue.sync {
  49. session.startRunning()
  50. DispatchQueue.main.async() { [weak self] in
  51. self?.createPreview()
  52. self?.rotatePreview()
  53. }
  54. }
  55. }
  56. public func stopSession() {
  57. cameraQueue.sync {
  58. session?.stopRunning()
  59. preview?.removeFromSuperlayer()
  60. session = nil
  61. input = nil
  62. imageOutput = nil
  63. preview = nil
  64. device = nil
  65. }
  66. }
  67. public override func layoutSubviews() {
  68. super.layoutSubviews()
  69. preview?.frame = bounds
  70. }
  71. public func configureFocus() {
  72. if let gestureRecognizers = gestureRecognizers {
  73. gestureRecognizers.forEach({ removeGestureRecognizer($0) })
  74. }
  75. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(focus(gesture:)))
  76. addGestureRecognizer(tapGesture)
  77. isUserInteractionEnabled = true
  78. addSubview(focusView)
  79. focusView.isHidden = true
  80. let lines = focusView.horizontalLines + focusView.verticalLines + focusView.outerLines
  81. lines.forEach { line in
  82. line.alpha = 0
  83. }
  84. }
  85. public func configureZoom() {
  86. let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinch(gesture:)))
  87. addGestureRecognizer(pinchGesture)
  88. }
  89. @objc internal func focus(gesture: UITapGestureRecognizer) {
  90. let point = gesture.location(in: self)
  91. guard focusCamera(toPoint: point) else {
  92. return
  93. }
  94. focusView.isHidden = false
  95. focusView.center = point
  96. focusView.alpha = 0
  97. focusView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
  98. bringSubviewToFront(focusView)
  99. UIView.animateKeyframes(withDuration: 1.5, delay: 0, options: UIView.KeyframeAnimationOptions(), animations: {
  100. UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.15, animations: { [weak self] in
  101. self?.focusView.alpha = 1
  102. self?.focusView.transform = CGAffineTransform.identity
  103. })
  104. UIView.addKeyframe(withRelativeStartTime: 0.80, relativeDuration: 0.20, animations: { [weak self] in
  105. self?.focusView.alpha = 0
  106. self?.focusView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
  107. })
  108. }, completion: { [weak self] finished in
  109. if finished {
  110. self?.focusView.isHidden = true
  111. }
  112. })
  113. }
  114. @objc internal func pinch(gesture: UIPinchGestureRecognizer) {
  115. guard let device = device else { return }
  116. // Return zoom value between the minimum and maximum zoom values
  117. func minMaxZoom(_ factor: CGFloat) -> CGFloat {
  118. return min(max(factor, 1.0), device.activeFormat.videoMaxZoomFactor)
  119. }
  120. func update(scale factor: CGFloat) {
  121. do {
  122. try device.lockForConfiguration()
  123. defer { device.unlockForConfiguration() }
  124. device.videoZoomFactor = factor
  125. } catch {
  126. print("\(error.localizedDescription)")
  127. }
  128. }
  129. let velocity = gesture.velocity
  130. let velocityFactor: CGFloat = 8.0
  131. let desiredZoomFactor = device.videoZoomFactor + atan2(velocity, velocityFactor)
  132. let newScaleFactor = minMaxZoom(desiredZoomFactor)
  133. switch gesture.state {
  134. case .began, .changed:
  135. update(scale: newScaleFactor)
  136. case _:
  137. break
  138. }
  139. }
  140. private func createPreview() {
  141. preview = AVCaptureVideoPreviewLayer(session: session)
  142. preview.videoGravity = AVLayerVideoGravity.resizeAspectFill
  143. preview.frame = bounds
  144. layer.addSublayer(preview)
  145. }
  146. private func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
  147. let devices = AVCaptureDevice.devices(for: AVMediaType.video)
  148. return devices.filter { $0.position == position }.first
  149. }
  150. public func capturePhoto(completion: @escaping CameraShotCompletion) {
  151. isUserInteractionEnabled = false
  152. guard let output = imageOutput, let orientation = AVCaptureVideoOrientation(rawValue: UIDevice.current.orientation.rawValue) else {
  153. completion(nil)
  154. return
  155. }
  156. let size = frame.size
  157. cameraQueue.sync {
  158. takePhoto(output, videoOrientation: orientation, cameraPosition: device.position, cropSize: size) { image in
  159. DispatchQueue.main.async() { [weak self] in
  160. self?.isUserInteractionEnabled = true
  161. completion(image)
  162. }
  163. }
  164. }
  165. }
  166. public func focusCamera(toPoint: CGPoint) -> Bool {
  167. guard let device = device, let preview = preview, device.isFocusModeSupported(.continuousAutoFocus) else {
  168. return false
  169. }
  170. do { try device.lockForConfiguration() } catch {
  171. return false
  172. }
  173. let focusPoint = preview.captureDevicePointConverted(fromLayerPoint: toPoint)
  174. device.focusPointOfInterest = focusPoint
  175. device.focusMode = .continuousAutoFocus
  176. device.exposurePointOfInterest = focusPoint
  177. device.exposureMode = .continuousAutoExposure
  178. device.unlockForConfiguration()
  179. return true
  180. }
  181. public func cycleFlash() {
  182. guard let device = device, device.hasFlash else {
  183. return
  184. }
  185. do {
  186. try device.lockForConfiguration()
  187. if device.flashMode == .on {
  188. device.flashMode = .off
  189. } else if device.flashMode == .off {
  190. device.flashMode = .auto
  191. } else {
  192. device.flashMode = .on
  193. }
  194. device.unlockForConfiguration()
  195. } catch _ { }
  196. }
  197. public func swapCameraInput() {
  198. guard let session = session, let currentInput = input else {
  199. return
  200. }
  201. session.beginConfiguration()
  202. session.removeInput(currentInput)
  203. if currentInput.device.position == AVCaptureDevice.Position.back {
  204. currentPosition = AVCaptureDevice.Position.front
  205. device = cameraWithPosition(position: currentPosition)
  206. } else {
  207. currentPosition = AVCaptureDevice.Position.back
  208. device = cameraWithPosition(position: currentPosition)
  209. }
  210. guard let newInput = try? AVCaptureDeviceInput(device: device) else {
  211. return
  212. }
  213. input = newInput
  214. session.addInput(newInput)
  215. session.commitConfiguration()
  216. }
  217. public func rotatePreview() {
  218. guard preview != nil else {
  219. return
  220. }
  221. switch UIApplication.shared.statusBarOrientation {
  222. case .portrait:
  223. preview?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
  224. break
  225. case .portraitUpsideDown:
  226. preview?.connection?.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown
  227. break
  228. case .landscapeRight:
  229. preview?.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeRight
  230. break
  231. case .landscapeLeft:
  232. preview?.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
  233. break
  234. default: break
  235. }
  236. }
  237. }