|
|
|
|
Quelle DKCamera.swift
Sprache: JAVA
|
|
//
// DKCamera.swift
// DKCameraDemo
//
// Created by ZhangAo on 15/8/30.
// Copyright (c) 2015年 ZhangAo. All rights reserved.
//
import UIKit
import AVFoundation
import CoreMotion
open class DKCameraPassthroughView: UIView {
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitTestingView = super.hitTest(point, with: event)
return hitTestingView == self ? nil : hitTestingView
}
}
extension AVMetadataFaceObject {
open func realBounds(inCamera camera: DKCamera) -> CGRect {
var bounds = CGRect()
let previewSize = camera.previewLayer.bounds.size
let isFront = camera.currentDevice == camera.captureDeviceFront
if isFront {
bounds.origin = CGPoint(x: previewSize.width - previewSize.width * (1 - self.bounds.origin.y - self.bounds.size.height / 2),
y: previewSize.height * (self.bounds.origin.x + self.bounds.size.width / 2))
} else {
bounds.origin = CGPoint(x: previewSize.width * (1 - self.bounds.origin.y - self.bounds.size.height / 2),
y: previewSize.height * (self.bounds.origin.x + self.bounds.size.width / 2))
}
bounds.size = CGSize(width: self.bounds.width * previewSize.height,
height: self.bounds.height * previewSize.width)
return bounds
}
}
@objc
public enum DKCameraDeviceSourceType : Int {
case front, rear
}
open class DKCamera: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
open class func checkCameraPermission(_ handler: @escaping (_ granted: Bool) -> Void) {
func hasCameraPermission() -> Bool {
return AVCaptureDevice.authorizationStatus(for: AVMediaType.video) == .authorized
}
func needsToRequestCameraPermission() -> Bool {
return AVCaptureDevice.authorizationStatus(for: AVMediaType.video) == .notDetermined
}
hasCameraPermission() ? handler(true) : (needsToRequestCameraPermission() ?
AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { granted in
DispatchQueue.main.async(execute: { () -> Void in
hasCameraPermission() ? handler(true) : handler(false)
})
}) : handler(false))
}
open var didCancel: (() -> Void)?
open var didFinishCapturingImage: ((_ image: UIImage) -> Void)?
/// Notify the listener of the detected faces in the preview frame.
open var onFaceDetection: ((_ faces: [AVMetadataFaceObject]) -> Void)?
/// Be careful this may cause the view to load prematurely.
open var cameraOverlayView: UIView? {
didSet {
if let cameraOverlayView = cameraOverlayView {
self.view.addSubview(cameraOverlayView)
}
}
}
/// The flashModel will to be remembered to next use.
open var flashMode:AVCaptureDevice.FlashMode! {
didSet {
self.updateFlashButton()
self.updateFlashMode()
self.updateFlashModeToUserDefautls(self.flashMode)
}
}
open class func isAvailable() -> Bool {
return UIImagePickerController.isSourceTypeAvailable(.camera)
}
/// Determines whether or not the rotation is enabled.
open var allowsRotate = false
/// set to NO to hide all standard camera UI. default is YES.
open var showsCameraControls = true {
didSet {
self.contentView.isHidden = !self.showsCameraControls
}
}
public let captureSession = AVCaptureSession()
open var previewLayer: AVCaptureVideoPreviewLayer!
fileprivate var beginZoomScale: CGFloat = 1.0
fileprivate var zoomScale: CGFloat = 1.0
open var defaultCaptureDevice = DKCameraDeviceSourceType.rear
open var currentDevice: AVCaptureDevice?
open var captureDeviceFront: AVCaptureDevice?
open var captureDeviceRear: AVCaptureDevice?
fileprivate weak var stillImageOutput: AVCaptureStillImageOutput?
open var contentView = UIView()
open var originalOrientation: UIDeviceOrientation!
open var currentOrientation: UIDeviceOrientation!
public let motionManager = CMMotionManager()
open lazy var flashButton: UIButton = {
let flashButton = UIButton()
flashButton.addTarget(self, action: #selector(DKCamera.switchFlashMode), for: .touchUpInside)
return flashButton
}()
open var cameraSwitchButton: UIButton!
open var captureButton: UIButton!
override open func viewDidLoad() {
super.viewDidLoad()
self.setupDevices()
self.setupUI()
self.setupSession()
self.setupMotionManager()
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !self.captureSession.isRunning {
self.captureSession.startRunning()
}
if !self.motionManager.isAccelerometerActive {
self.motionManager.startAccelerometerUpdates(to: OperationQueue.current!, withHandler: { accelerometerData, error in
if error == nil {
let currentOrientation = accelerometerData!.acceleration.toDeviceOrientation() ?? self.currentOrientation
if self.originalOrientation == nil {
self.initialOriginalOrientationForOrientation()
self.currentOrientation = self.originalOrientation
}
if let currentOrientation = currentOrientation , self.currentOrientation != currentOrientation {
self.currentOrientation = currentOrientation
self.updateContentLayoutForCurrentOrientation()
}
} else {
print("error while update accelerometer: \(error!.localizedDescription)", terminator: "")
}
})
}
self.updateSession(isEnable: true)
}
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if self.originalOrientation == nil {
self.contentView.frame = self.view.bounds
self.previewLayer.frame = self.view.bounds
}
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.updateSession(isEnable: false)
self.motionManager.stopAccelerometerUpdates()
}
override open func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
open override var prefersStatusBarHidden : Bool {
return true
}
// MARK: - Setup
let bottomView = UIView()
open func setupUI() {
self.view.backgroundColor = UIColor.black
self.view.addSubview(self.contentView)
self.contentView.backgroundColor = UIColor.clear
self.contentView.frame = self.view.bounds
let bottomViewHeight: CGFloat = 70
bottomView.bounds.size = CGSize(width: contentView.bounds.width, height: bottomViewHeight)
bottomView.frame.origin = CGPoint(x: 0, y: contentView.bounds.height - bottomViewHeight)
bottomView.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
bottomView.backgroundColor = UIColor(white: 0, alpha: 0.4)
contentView.addSubview(bottomView)
// switch button
let cameraSwitchButton: UIButton = {
let cameraSwitchButton = UIButton()
cameraSwitchButton.addTarget(self, action: #selector(DKCamera.switchCamera), for: .touchUpInside)
cameraSwitchButton.setImage(DKCameraResource.cameraSwitchImage(), for: .normal)
cameraSwitchButton.sizeToFit()
return cameraSwitchButton
}()
cameraSwitchButton.frame.origin = CGPoint(x: bottomView.bounds.width - cameraSwitchButton.bounds.width - 15,
y: (bottomView.bounds.height - cameraSwitchButton.bounds.height) / 2)
cameraSwitchButton.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleBottomMargin]
bottomView.addSubview(cameraSwitchButton)
self.cameraSwitchButton = cameraSwitchButton
// capture button
let captureButton: UIButton = {
class DKCaptureButton: UIButton {
fileprivate override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
self.backgroundColor = UIColor.white
return true
}
fileprivate override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
self.backgroundColor = UIColor.white
return true
}
fileprivate override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
self.backgroundColor = nil
}
fileprivate override func cancelTracking(with event: UIEvent?) {
self.backgroundColor = nil
}
}
let captureButton = DKCaptureButton()
captureButton.addTarget(self, action: #selector(DKCamera.takePicture), for: .touchUpInside)
captureButton.bounds.size = CGSize(width: bottomViewHeight,
height: bottomViewHeight).applying(CGAffineTransform(scaleX: 0.9, y: 0.9))
captureButton.layer.cornerRadius = captureButton.bounds.height / 2
captureButton.layer.borderColor = UIColor.white.cgColor
captureButton.layer.borderWidth = 2
captureButton.layer.masksToBounds = true
return captureButton
}()
captureButton.center = CGPoint(x: bottomView.bounds.width / 2, y: bottomView.bounds.height / 2)
captureButton.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin]
bottomView.addSubview(captureButton)
self.captureButton = captureButton
// cancel button
let cancelButton: UIButton = {
let cancelButton = UIButton()
cancelButton.addTarget(self, action: #selector(dismiss as () -> Void), for: .touchUpInside)
cancelButton.setImage(DKCameraResource.cameraCancelImage(), for: .normal)
cancelButton.sizeToFit()
return cancelButton
}()
cancelButton.frame.origin = CGPoint(x: contentView.bounds.width - cancelButton.bounds.width - 15, y: 25)
cancelButton.autoresizingMask = [.flexibleBottomMargin, .flexibleLeftMargin]
contentView.addSubview(cancelButton)
self.flashButton.frame.origin = CGPoint(x: 5, y: 15)
contentView.addSubview(self.flashButton)
contentView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(DKCamera.handleZoom(_:))))
contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(DKCamera.handleFocus(_:))))
}
open func setupSession() {
self.captureSession.sessionPreset = AVCaptureSession.Preset.photo
self.setupCurrentDevice()
let stillImageOutput = AVCaptureStillImageOutput()
if self.captureSession.canAddOutput(stillImageOutput) {
self.captureSession.addOutput(stillImageOutput)
self.stillImageOutput = stillImageOutput
}
if self.onFaceDetection != nil {
let metadataOutput = AVCaptureMetadataOutput()
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue(label: "MetadataOutputQueue"))
if self.captureSession.canAddOutput(metadataOutput) {
self.captureSession.addOutput(metadataOutput)
metadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.face]
}
}
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
self.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.previewLayer.frame = self.view.bounds
let rootLayer = self.view.layer
rootLayer.masksToBounds = true
rootLayer.insertSublayer(self.previewLayer, at: 0)
}
open func setupCurrentDevice() {
if let currentDevice = self.currentDevice {
if currentDevice.isFlashAvailable {
self.flashButton.isHidden = false
self.flashMode = self.flashModeFromUserDefaults()
} else {
self.flashButton.isHidden = true
}
for oldInput in self.captureSession.inputs {
self.captureSession.removeInput(oldInput)
}
let frontInput = try? AVCaptureDeviceInput(device: self.currentDevice!)
if self.captureSession.canAddInput(frontInput!) {
self.captureSession.addInput(frontInput!)
}
try! currentDevice.lockForConfiguration()
if currentDevice.isFocusModeSupported(.continuousAutoFocus) {
currentDevice.focusMode = .continuousAutoFocus
}
if currentDevice.isExposureModeSupported(.continuousAutoExposure) {
currentDevice.exposureMode = .continuousAutoExposure
}
currentDevice.unlockForConfiguration()
}
}
open func setupDevices() {
let devices = AVCaptureDevice.devices(for: AVMediaType.video)
for device in devices {
if device.position == .back {
self.captureDeviceRear = device
}
if device.position == .front {
self.captureDeviceFront = device
}
}
switch self.defaultCaptureDevice {
case .front:
self.currentDevice = self.captureDeviceFront ?? self.captureDeviceRear
case .rear:
self.currentDevice = self.captureDeviceRear ?? self.captureDeviceFront
}
}
// MARK: - Session
fileprivate var isStopped = false
open func startSession() {
self.isStopped = false
if !self.captureSession.isRunning {
self.captureSession.startRunning()
}
}
open func stopSession() {
self.pauseSession()
self.captureSession.stopRunning()
}
open func pauseSession() {
self.isStopped = true
self.updateSession(isEnable: false)
}
open func updateSession(isEnable: Bool) {
if ((!self.isStopped) || (self.isStopped && !isEnable)),
let connection = self.previewLayer.connection {
connection.isEnabled = isEnable
}
}
// MARK: - Callbacks
@objc internal func dismiss() {
self.didCancel?()
}
@objc open func takePicture() {
let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
if authStatus == .denied {
return
}
if let stillImageOutput = self.stillImageOutput, !stillImageOutput.isCapturingStillImage {
self.captureButton.isEnabled = false
DispatchQueue.global().async(execute: {
if let connection = stillImageOutput.connection(with: AVMediaType.video) {
connection.videoOrientation = self.currentOrientation.toAVCaptureVideoOrientation()
connection.videoScaleAndCropFactor = self.zoomScale
stillImageOutput.captureStillImageAsynchronously(from: connection, completionHandler: { (imageDataSampleBuffer, error) in
if error == nil {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer!)
if let didFinishCapturingImage = self.didFinishCapturingImage, let imageData = imageData, let takenImage = UIImage(data: imageData) {
let outputRect = self.previewLayer.metadataOutputRectConverted(fromLayerRect: self.previewLayer.bounds)
let takenCGImage = takenImage.cgImage!
let width = CGFloat(takenCGImage.width)
let height = CGFloat(takenCGImage.height)
let cropRect = CGRect(x: outputRect.origin.x * width, y: outputRect.origin.y * height, width: outputRect.size.width * width, height: outputRect.size.height * height)
let cropCGImage = takenCGImage.cropping(to: cropRect)
let cropTakenImage = UIImage(cgImage: cropCGImage!, scale: 1, orientation: takenImage.imageOrientation)
didFinishCapturingImage(cropTakenImage)
self.captureButton.isEnabled = true
}
} else {
print("error while capturing still image: \(error!.localizedDescription)", terminator: "")
}
})
}
})
}
}
// MARK: - Handles Zoom
@objc open func handleZoom(_ gesture: UIPinchGestureRecognizer) {
if gesture.state == .began {
self.beginZoomScale = self.zoomScale
} else if gesture.state == .changed {
self.zoomScale = min(4.0, max(1.0, self.beginZoomScale * gesture.scale))
CATransaction.begin()
CATransaction.setAnimationDuration(0.025)
self.previewLayer.setAffineTransform(CGAffineTransform(scaleX: self.zoomScale, y: self.zoomScale))
CATransaction.commit()
}
}
// MARK: - Handles Focus
@objc open func handleFocus(_ gesture: UITapGestureRecognizer) {
if let currentDevice = self.currentDevice , currentDevice.isFocusPointOfInterestSupported {
let touchPoint = gesture.location(in: self.view)
self.focusAtTouchPoint(touchPoint)
}
}
open func focusAtTouchPoint(_ touchPoint: CGPoint) {
func showFocusViewAtPoint(_ touchPoint: CGPoint) {
struct FocusView {
static let focusView: UIView = {
let focusView = UIView()
let diameter: CGFloat = 100
focusView.bounds.size = CGSize(width: diameter, height: diameter)
focusView.layer.borderWidth = 2
focusView.layer.cornerRadius = diameter / 2
focusView.layer.borderColor = UIColor.white.cgColor
return focusView
}()
}
FocusView.focusView.transform = CGAffineTransform.identity
FocusView.focusView.center = touchPoint
self.view.addSubview(FocusView.focusView)
UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.1,
options: UIView.AnimationOptions(), animations: { () -> Void in
FocusView.focusView.transform = CGAffineTransform.identity.scaledBy(x: 0.6, y: 0.6)
}) { (Bool) -> Void in
FocusView.focusView.removeFromSuperview()
}
}
if self.currentDevice == nil || self.currentDevice?.isFlashAvailable == false {
return
}
let focusPoint = self.previewLayer.captureDevicePointConverted(fromLayerPoint: touchPoint)
showFocusViewAtPoint(touchPoint)
if let currentDevice = self.currentDevice {
try! currentDevice.lockForConfiguration()
currentDevice.focusPointOfInterest = focusPoint
currentDevice.exposurePointOfInterest = focusPoint
currentDevice.focusMode = .continuousAutoFocus
if currentDevice.isExposureModeSupported(.continuousAutoExposure) {
currentDevice.exposureMode = .continuousAutoExposure
}
currentDevice.unlockForConfiguration()
}
}
// MARK: - Handles Switch Camera
@objc internal func switchCamera() {
self.currentDevice = self.currentDevice == self.captureDeviceRear ?
self.captureDeviceFront : self.captureDeviceRear
self.setupCurrentDevice()
}
// MARK: - Handles Flash
@objc internal func switchFlashMode() {
switch self.flashMode! {
case .auto:
self.flashMode = .off
case .on:
self.flashMode = .auto
case .off:
self.flashMode = .on
}
}
open func flashModeFromUserDefaults() -> AVCaptureDevice.FlashMode {
let rawValue = UserDefaults.standard.integer(forKey: "DKCamera.flashMode")
return AVCaptureDevice.FlashMode(rawValue: rawValue)!
}
open func updateFlashModeToUserDefautls(_ flashMode: AVCaptureDevice.FlashMode) {
UserDefaults.standard.set(flashMode.rawValue, forKey: "DKCamera.flashMode")
}
open func updateFlashButton() {
struct FlashImage {
static let images = [
AVCaptureDevice.FlashMode.auto : DKCameraResource.cameraFlashAutoImage(),
AVCaptureDevice.FlashMode.on : DKCameraResource.cameraFlashOnImage(),
AVCaptureDevice.FlashMode.off : DKCameraResource.cameraFlashOffImage()
]
}
let flashImage: UIImage = FlashImage.images[self.flashMode]!
self.flashButton.setImage(flashImage, for: .normal)
self.flashButton.sizeToFit()
}
open func updateFlashMode() {
if let currentDevice = self.currentDevice
, currentDevice.isFlashAvailable && currentDevice.isFlashModeSupported(self.flashMode) {
try! currentDevice.lockForConfiguration()
currentDevice.flashMode = self.flashMode
currentDevice.unlockForConfiguration()
}
}
// MARK: - AVCaptureMetadataOutputObjectsDelegate
public func metadataOutput(_ captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
self.onFaceDetection?(metadataObjects as! [AVMetadataFaceObject])
}
// MARK: - Handles Orientation
open override var shouldAutorotate : Bool {
return false
}
open func setupMotionManager() {
self.motionManager.accelerometerUpdateInterval = 0.5
self.motionManager.gyroUpdateInterval = 0.5
}
open func initialOriginalOrientationForOrientation() {
self.originalOrientation = UIApplication.shared.statusBarOrientation.toDeviceOrientation()
if let connection = self.previewLayer.connection {
connection.videoOrientation = self.originalOrientation.toAVCaptureVideoOrientation()
}
}
open func updateContentLayoutForCurrentOrientation() {
let newAngle = self.currentOrientation.toAngleRelativeToPortrait() - self.originalOrientation.toAngleRelativeToPortrait()
if self.allowsRotate {
var contentViewNewSize: CGSize!
let width = self.view.bounds.width
let height = self.view.bounds.height
if self.currentOrientation.isLandscape {
contentViewNewSize = CGSize(width: max(width, height), height: min(width, height))
} else {
contentViewNewSize = CGSize(width: min(width, height), height: max(width, height))
}
UIView.animate(withDuration: 0.2, animations: {
self.contentView.bounds.size = contentViewNewSize
self.contentView.transform = CGAffineTransform(rotationAngle: newAngle)
})
} else {
let rotateAffineTransform = CGAffineTransform.identity.rotated(by: newAngle)
UIView.animate(withDuration: 0.2, animations: {
self.flashButton.transform = rotateAffineTransform
self.cameraSwitchButton.transform = rotateAffineTransform
})
}
}
}
// MARK: - Utilities
public extension UIInterfaceOrientation {
func toDeviceOrientation() -> UIDeviceOrientation {
switch self {
case .portrait:
return .portrait
case .portraitUpsideDown:
return .portraitUpsideDown
case .landscapeRight:
return .landscapeLeft
case .landscapeLeft:
return .landscapeRight
default:
return .portrait
}
}
}
public extension UIDeviceOrientation {
func toAVCaptureVideoOrientation() -> AVCaptureVideoOrientation {
switch self {
case .portrait:
return .portrait
case .portraitUpsideDown:
return .portraitUpsideDown
case .landscapeRight:
return .landscapeLeft
case .landscapeLeft:
return .landscapeRight
default:
return .portrait
}
}
func toInterfaceOrientationMask() -> UIInterfaceOrientationMask {
switch self {
case .portrait:
return .portrait
case .portraitUpsideDown:
return .portraitUpsideDown
case .landscapeRight:
return .landscapeLeft
case .landscapeLeft:
return .landscapeRight
default:
return .portrait
}
}
func toAngleRelativeToPortrait() -> CGFloat {
switch self {
case .portrait:
return 0
case .portraitUpsideDown:
return CGFloat.pi
case .landscapeRight:
return -CGFloat.pi / 2.0
case .landscapeLeft:
return CGFloat.pi / 2.0
default:
return 0.0
}
}
}
public extension CMAcceleration {
func toDeviceOrientation() -> UIDeviceOrientation? {
if self.x >= 0.75 {
return .landscapeRight
} else if self.x <= -0.75 {
return .landscapeLeft
} else if self.y <= -0.75 {
return .portrait
} else if self.y >= 0.75 {
return .portraitUpsideDown
} else {
return nil
}
}
}
// MARK: - Rersources
public extension Bundle {
class func cameraBundle() -> Bundle {
let assetPath = Bundle(for: DKCameraResource.self).resourcePath!
return Bundle(path: (assetPath as NSString).appendingPathComponent("DKCameraResource.bundle"))!
}
}
open class DKCameraResource {
open class func imageForResource(_ name: String) -> UIImage {
let bundle = Bundle.cameraBundle()
let imagePath = bundle.path(forResource: name, ofType: "png", inDirectory: "Images")
let image = UIImage(contentsOfFile: imagePath!)
return image!
}
class func cameraCancelImage() -> UIImage {
return imageForResource("camera_cancel")
}
class func cameraFlashOnImage() -> UIImage {
return imageForResource("camera_flash_on")
}
class func cameraFlashAutoImage() -> UIImage {
return imageForResource("camera_flash_auto")
}
class func cameraFlashOffImage() -> UIImage {
return imageForResource("camera_flash_off")
}
class func cameraSwitchImage() -> UIImage {
return imageForResource("camera_switch")
}
}
¤ Dauer der Verarbeitung: 0.9 Sekunden
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104856 |
| #Domains=417283 |
|
|