- OC
#import "ScanViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface ScanViewController ()<AVCaptureMetadataOutputObjectsDelegate>
{
AVCaptureSession * session;
AVCaptureMetadataOutput * output;
}
@end
@implementation ScanViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.hbd_barShadowHidden = YES;
self.hbd_barTintColor = UIColor.whiteColor;
self.hbd_tintColor = UIColor.blackColor;
self.view.backgroundColor = UIColor.whiteColor;
self.navigationItem.title = @"扫描二维码";
[self createQrView];
}
-(void)createQrView{
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status) {
case AVAuthorizationStatusNotDetermined:{
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
[self continueConfigUI:AVMediaTypeVideo];
} else {
NSLog(@"denied");
}
});
}];
}
break;
case AVAuthorizationStatusAuthorized:{
[self continueConfigUI:AVMediaTypeVideo];
}
break;
default:
break;
}
}
- (void)continueConfigUI:(AVMediaType)mediaType{
AVCaptureDevice * device = [AVCaptureDevice defaultDeviceWithMediaType:mediaType];
if (!device) {
return;
}
CGFloat W = KSWidth - 100;
CGRect scanRect = CGRectMake(50,80, W , W );
AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
output = [[AVCaptureMetadataOutput alloc] init];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
session = [[AVCaptureSession alloc] init];
if([session canAddInput:deviceInput]){
[session addInput:deviceInput];
}
if([session canAddOutput:output]){
[session addOutput:output];
}
// 必须放在上面设置完session之后
output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
AVCaptureVideoPreviewLayer *layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
layer.frame = self.view.bounds;
[self.view.layer addSublayer:layer];
[session startRunning];
/ 采用此方法必须放到startRunning之后
output.rectOfInterest = [layer metadataOutputRectOfInterestForRect:scanRect];
// 取景框图片
UIImageView *quJingImage =[[UIImageView alloc] initWithFrame:[layer rectForMetadataOutputRectOfInterest:output.rectOfInterest]];
quJingImage.image = [UIImage imageNamed:@"biankuang"];
[self.view addSubview:quJingImage];
UILabel *label = [[UILabel alloc] init];
label.text = @"扫描旧手机上二维码";
label.textAlignment = NSTextAlignmentCenter;
label.textColor = UIColor.blackColor;
label.font = [UIFont systemFontOfSize:18];
[self.view addSubview:label];
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.mas_equalTo(0);
make.top.equalTo(quJingImage.mas_bottom).offset(24);
}];
}
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
if(metadataObjects.count > 0) {
[session stopRunning];
AVMetadataMachineReadableCodeObject * readCode = metadataObjects.firstObject;
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"结果" message:readCode.stringValue preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * alertAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alertVC addAction:alertAction];
[self presentViewController:alertVC animated:YES completion:nil];
NSLog(@"%@", readCode.stringValue);
}
}
- Swift
import UIKit
import AVFoundation
import Photos
import CoreImage
protocol ScanViewDelegate: NSObjectProtocol{
/// 扫码数据
///
/// - Parameter pileCode: 编码
func captureOutputSuccess(pileCode:String);
}
class YMScanView: UIView,UIImagePickerControllerDelegate,UINavigationControllerDelegate,AVCaptureMetadataOutputObjectsDelegate {
/// 创建设备
var captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
/// 捕获视频
var captureSession : AVCaptureSession?
/// 捕获视频的预览层
var videoPreviewLayer : AVCaptureVideoPreviewLayer?
/// 提示文字
var titleLabel = UILabel()
/// 图片
var quJingImage:UIImageView!
var saoMaTiaoImage : UIImageView?
/// 开灯
var openLampButton:UIButton!
/// 扫码结果
var sweepResult : ((_ resultStr : String)->())!
// 代理
weak var delegate : ScanViewDelegate!
/// 元数据输出
let captureMetadataOutput = AVCaptureMetadataOutput()
override init(frame: CGRect) {
super.init(frame: frame)
createQrView()
createScanPanelView()
}
/// 创建扫描设备
func createQrView(){
let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
if authStatus == AVAuthorizationStatus.restricted || authStatus == AVAuthorizationStatus.denied {
DZAlertView.sharedInstance.show(DZAlertViewType.blackViewAndClickDisappear, contentType: DZAlertContentViewType.warning, message: ["请在iPhone的“设置”-“隐私”-“相机”功能中,找到“\(Bundle.main.appDisplayName)”打开相机访问权限"])
return
}
//给设备添加input输入
var input : AVCaptureDeviceInput!
do{
input = try AVCaptureDeviceInput(device: captureDevice!)
}catch{
}
if input == nil {
return
}
captureSession = AVCaptureSession()
captureSession?.addInput(input)
captureSession?.addOutput(captureMetadataOutput)
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr,
AVMetadataObject.ObjectType.ean8,
AVMetadataObject.ObjectType.ean13,
AVMetadataObject.ObjectType.code128,
AVMetadataObject.ObjectType.code39,
AVMetadataObject.ObjectType.code93,
AVMetadataObject.ObjectType.code39Mod43,
AVMetadataObject.ObjectType.pdf417,
AVMetadataObject.ObjectType.aztec,
AVMetadataObject.ObjectType.upce
]
//
let cropRect = CGRect(x: UIConfigure.Width / 2 - (258 * UIConfigure.SizeScale) / 2, y: 85 * UIConfigure.SizeScale, width: 258 * UIConfigure.SizeScale, height: 258 * UIConfigure.SizeScale)
captureMetadataOutput.rectOfInterest = CGRect(x: cropRect.origin.y / UIConfigure.Height, y: cropRect.origin.x / UIConfigure.Width, width: cropRect.size.height / UIConfigure.Height , height: cropRect.size.width / UIConfigure.Width)
// captureMetadataOutput.rectOfInterest = CGRect(x: (UIConfigure.Width - 258 * UIConfigure.SizeScale) / 2 / UIConfigure.Width , y: 85 * UIConfigure.SizeScale / (UIConfigure.Height - UIConfigure.navigationBarHeight), width: 258 * UIConfigure.SizeScale / UIConfigure.Width, height: 258 * UIConfigure.SizeScale / (UIConfigure.Height - UIConfigure.navigationBarHeight))
/// 显示的layer
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
let isphone = UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.phone ? true : false
if isphone {
videoPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
}else{
videoPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeRight
}
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoPreviewLayer?.frame = self.layer.bounds
self.layer.addSublayer(videoPreviewLayer!)
captureSession?.startRunning()
// if captureDevice!.isFocusPointOfInterestSupported && captureDevice!.isFocusModeSupported(AVCaptureDevice.FocusMode.autoFocus) {
// input.device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus
// }
}
/// 创建扫描面板
func createScanPanelView(){
// 取景框图片
quJingImage = UIImageView(image: BaseImage(named: "QSD_Scan_ScanQr")?.image)
self.addSubview(quJingImage)
quJingImage.snp.makeConstraints { (make) -> Void in
make.top.equalTo(85 * UIConfigure.SizeScale)
make.centerX.equalTo(self.snp.centerX)
make.width.equalTo(258 * UIConfigure.SizeScale)
make.height.equalTo(258 * UIConfigure.SizeScale)
}
self.layoutIfNeeded()
// 上下左右四个遮盖黑边
let topView = UIView()
self.addSubview(topView)
topView.snp.makeConstraints { (make) -> Void in
make.top.equalTo(0)
make.left.equalTo(0)
make.right.equalTo(0)
make.bottom.equalTo(quJingImage.snp.top)
}
topView.backgroundColor = UIColor.black
topView.alpha = 0.5
let leftView = UIView()
self.addSubview(leftView)
leftView.snp.makeConstraints { (make) -> Void in
make.top.equalTo(topView.snp.bottom)
make.left.equalTo(0)
make.right.equalTo(quJingImage.snp.left)
make.bottom.equalTo(quJingImage.snp.bottom)
}
leftView.backgroundColor = UIColor.black
leftView.alpha = 0.5
let rightView = UIView()
self.addSubview(rightView)
rightView.snp.makeConstraints { (make) -> Void in
make.top.equalTo(topView.snp.bottom)
make.left.equalTo(quJingImage.snp.right)
make.right.equalTo(0)
make.bottom.equalTo(quJingImage.snp.bottom)
}
rightView.backgroundColor = UIColor.black
rightView.alpha = 0.5
let bottomView = UIView()
self.addSubview(bottomView)
bottomView.snp.makeConstraints { (make) -> Void in
make.top.equalTo(quJingImage.snp.bottom)
make.left.equalTo(0)
make.right.equalTo(0)
make.bottom.equalTo(0)
}
bottomView.backgroundColor = UIColor.black
bottomView.alpha = 0.5
//提示文字
self.addSubview(titleLabel)
titleLabel.snp.makeConstraints { (make) -> Void in
make.top.equalTo(quJingImage.snp.bottom).offset(10)
make.left.equalTo(40)
make.right.equalTo(-40)
}
titleLabel.numberOfLines = 0
titleLabel.textAlignment = NSTextAlignment.center
titleLabel.text = "将二维码/条形码放入框内,即可自动扫描"
titleLabel.textColor = UIColor.white
titleLabel.font = UIConfigure.Font14
self.bringSubviewToFront(titleLabel)
//扫描条不断动的那个
saoMaTiaoImage = UIImageView(image: BaseImage(named: "QSD_Scan_ScanQrLIne")?.image)
self.addSubview(saoMaTiaoImage!)
saoMaTiaoImage!.snp.makeConstraints { (make) -> Void in
make.top.equalTo(quJingImage.snp.top)
make.centerX.equalTo(quJingImage.snp.centerX)
make.width.equalTo(200 * UIConfigure.SizeScale)
make.height.equalTo(1)
}
saoMaTiaoImage?.isHidden = false
changeStyle()
//闪光灯按钮
openLampButton = UIButton()
self.addSubview(openLampButton)
openLampButton.snp.makeConstraints { (make) -> Void in
make.top.equalTo(titleLabel.snp.bottom).offset(30)
make.width.equalTo(60 * UIConfigure.SizeScale)
make.height.equalTo(60 * UIConfigure.SizeScale)
make.centerX.equalTo(self.snp.centerX)
}
openLampButton.setImage(BaseImage(named: "open_flash")?.image, for: UIControl.State.normal)
openLampButton.setImage(BaseImage(named: "close_flash")?.image, for: UIControl.State.selected)
openLampButton.addTarget(self, action: #selector(openShanGuang(sender:)), for: UIControl.Event.touchUpInside)
openLampButton.setTitleColor(UIConfigure.ThemeColor, for: UIControl.State.normal)
// openLampButton.titleLabel?.textColor = UIConfigure.ThemeColor
// openLampButton.backgroundColor = UIColor.white
// openLampButton.layer.cornerRadius = UIConfigure.CornerRadius
// openLampButton.layer.masksToBounds = true
// openLampButton.layer.borderColor = UIConfigure.ThemeColor.cgColor
// openLampButton.layer.borderWidth = 0.5
}
/**
扫描条不断的移动
*/
func changeStyle(){
guard self.saoMaTiaoImage != nil else {
return
}
self.saoMaTiaoImage?.isHidden = false
UIView.animate(withDuration: 3.5, animations: { [weak self] () -> Void in
if(self != nil){
self!.saoMaTiaoImage?.transform = CGAffineTransform(translationX: 0, y: 258 * UIConfigure.SizeScale)
}
}) { [weak self](end) -> Void in
if(self != nil){
UIView.animate(withDuration: 3.5, animations: { [weak self] in
if(self != nil){
self!.saoMaTiaoImage?.transform = CGAffineTransform.identity
}
}, completion: { [weak self](end) in
if self != nil {
self!.changeStyle()
}
})
}
}
}
/// 开闪光
@objc func openShanGuang(sender:UIButton){
sender.isSelected = !sender.isSelected
if captureDevice != nil && captureDevice!.hasFlash && captureDevice!.hasTorch {
do {
try captureDevice?.lockForConfiguration()
if sender.isSelected {
captureDevice!.flashMode = AVCaptureDevice.FlashMode.on
captureDevice!.torchMode = AVCaptureDevice.TorchMode.on
}else{
captureDevice!.flashMode = AVCaptureDevice.FlashMode.off
captureDevice!.torchMode = AVCaptureDevice.TorchMode.off
}
captureDevice?.unlockForConfiguration()
}catch{
}
}else{
DZAlertView.sharedInstance.show(DZAlertViewType.blackViewAndClickDisappear, contentType: DZAlertContentViewType.warning, message: ["不能打开设备的闪光灯"])
}
}
// func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
// if metadataObjects == nil || metadataObjects.count == 0 {
// return
// }
//
playSoundEffect(name: "Qcodesound.caf")
// let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
// captureSession?.stopRunning()
// print("刚刚扫描出来!!!\(metadataObj.stringValue)")
// delegate.captureOutputSuccess(pileCode: metadataObj.stringValue!)
if metadataObj.type == AVMetadataObjectTypeQRCode {
if metadataObj.stringValue != nil {
captureSession?.stopRunning()
print("刚刚扫描出来!!!\(metadataObj.stzzzzringValue)")
delegate.captureOutputSuccess(pileCode: metadataObj.stringValue!)
}
}
//
// }
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if metadataObjects.count == 0 {
return
}
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
sweepResult(String(describing: metadataObj.stringValue!))
DebugLog(object: "刚刚扫描出来!!!\(String(describing: metadataObj.stringValue!))")
// let obj = videoPreviewLayer?.transformedMetadataObject(for: metadataObjects.last!) as! AVMetadataMachineReadableCodeObject
// delegate.captureOutputSuccess(pileCode: metadataObj.stringValue!)
captureSession?.stopRunning()
}
// func changeVideoScale(objc : AVMetadataMachineReadableCodeObject){
// let array = objc.corners
// let point = CGPoint.zero
// var index = 0
// let dic = array[index += 1] as! CFDictionary
// }
/// 扫描成功后播放声音
///
/// - Parameter name: 地址
func playSoundEffect(name : String){
let audioFile = Bundle.main.path(forResource: name, ofType: nil)
let fileUrl = URL(fileURLWithPath: audioFile!)
var soundID : SystemSoundID = 0
AudioServicesCreateSystemSoundID(fileUrl as CFURL, &soundID)
AudioServicesAddSystemSoundCompletion(soundID, nil, nil, { (SounID, clientData) in
print("播放完成")
}, nil)
AudioServicesPlaySystemSound(soundID)
}
// 打开相册
func openPhoto(){
let status = PHPhotoLibrary.authorizationStatus()
if status == PHAuthorizationStatus.denied || status == .restricted {
APPDelStatic.showNoPhotoPhotoAlertView()
return
}else{
//没有被禁用
//先设定sourceType为相机,然后判断相机是否可用(ipod)没相机,不可用将sourceType设定为相片库
// var sourceType = UIImagePickerControllerSourceType.photoLibrary
// if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary){
// sourceType = UIImagePickerControllerSourceType.photoLibrary
// }
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.delegate = self
picker.sourceType = UIImagePickerController.SourceType.savedPhotosAlbum
self.ViewController()?.present(picker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
var image = info[UIImagePickerController.InfoKey.editedImage];
if image == nil {
image = info[UIImagePickerController.InfoKey.originalImage]
}
var detector : CIDetector?
if #available(iOS 9.0, *) {
detector = CIDetector(ofType: CIDetectorTypeText, context: nil, options: nil)
} else {
// Fallback on earlier versions
}
picker.dismiss(animated: true) {
if let imageData = (image as! UIImage).pngData() {
if let ciimage = CIImage(data: imageData) {
if let features = detector?.features(in: ciimage) {
if features.count >= 1 {
let feature = features[0] as! CIQRCodeFeature
let scannedResult = feature.messageString
// delegate.captureOutputSuccess(pileCode: scannedResult)
DebugLog(object: "刚刚扫描照片出来!!!\(scannedResult!)")
}else{
DebugLog(object: "不是二维码!!")
}
}
}
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
请大家关注我的个人公众号: