Files
mier_ios/SweetParty/第三方库/SGQRCode/SGQRCodeObtain.m
2025-08-11 10:43:19 +08:00

387 lines
18 KiB
Objective-C
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SGQRCodeObtain.m
// SGQRCodeExample
//
// Created by kingsic on 2016/8/16.
// Copyright © 2016年 kingsic. All rights reserved.
//
#import "SGQRCodeObtain.h"
#import "SGQRCodeObtainConfigure.h"
#import <Photos/Photos.h>
@interface SGQRCodeObtain () <AVCaptureMetadataOutputObjectsDelegate, AVCaptureVideoDataOutputSampleBufferDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate>
@property (nonatomic, weak) UIViewController *controller;
@property (nonatomic, strong) SGQRCodeObtainConfigure *configure;
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, copy) SGQRCodeObtainScanResultBlock scanResultBlock;
@property (nonatomic, copy) SGQRCodeObtainScanBrightnessBlock scanBrightnessBlock;
@property (nonatomic, copy) SGQRCodeObtainAlbumDidCancelImagePickerControllerBlock albumDidCancelImagePickerControllerBlock;
@property (nonatomic, copy) SGQRCodeObtainAlbumResultBlock albumResultBlock;
@property (nonatomic, copy) NSString *detectorString;
@end
@implementation SGQRCodeObtain
+ (instancetype)QRCodeObtain {
return [[self alloc] init];
}
- (void)dealloc {
if (_configure.openLog == YES) {
NSLog(@"SGQRCodeObtain - - dealloc");
}
}
#pragma mark - - 生成二维码相关方法
/**
* 生成二维码
*
* @param data 二维码数据
* @param size 二维码大小
*/
+ (UIImage *)generateQRCodeWithData:(NSString *)data size:(CGFloat)size {
return [self generateQRCodeWithData:data size:size color:[UIColor blackColor] backgroundColor:[UIColor whiteColor]];
}
/**
* 生成二维码
*
* @param data 二维码数据
* @param size 二维码大小
* @param color 二维码颜色
* @param backgroundColor 二维码背景颜色
*/
+ (UIImage *)generateQRCodeWithData:(NSString *)data size:(CGFloat)size color:(UIColor *)color backgroundColor:(UIColor *)backgroundColor {
NSData *string_data = [data dataUsingEncoding:NSUTF8StringEncoding];
// 1、二维码滤镜
CIFilter *fileter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[fileter setValue:string_data forKey:@"inputMessage"];
[fileter setValue:@"H" forKey:@"inputCorrectionLevel"];
CIImage *ciImage = fileter.outputImage;
// 2、颜色滤镜
CIFilter *color_filter = [CIFilter filterWithName:@"CIFalseColor"];
[color_filter setValue:ciImage forKey:@"inputImage"];
[color_filter setValue:[CIColor colorWithCGColor:color.CGColor] forKey:@"inputColor0"];
[color_filter setValue:[CIColor colorWithCGColor:backgroundColor.CGColor] forKey:@"inputColor1"];
// 3、生成处理
CIImage *outImage = color_filter.outputImage;
CGFloat scale = size / outImage.extent.size.width;
outImage = [outImage imageByApplyingTransform:CGAffineTransformMakeScale(scale, scale)];
return [UIImage imageWithCIImage:outImage];
}
/**
* 生成带 logo 的二维码(推荐使用)
*
* @param data 二维码数据
* @param size 二维码大小
* @param logoImage logo
* @param ratio logo 相对二维码的比例
*/
+ (UIImage *)generateQRCodeWithData:(NSString *)data size:(CGFloat)size logoImage:(UIImage *)logoImage ratio:(CGFloat)ratio {
return [self generateQRCodeWithData:data size:size logoImage:logoImage ratio:ratio logoImageCornerRadius:5 logoImageBorderWidth:5 logoImageBorderColor:[UIColor whiteColor]];
}
/**
* 生成带 logo 的二维码(拓展)
*
* @param data 二维码数据
* @param size 二维码大小
* @param logoImage logo
* @param ratio logo 相对二维码的比例
* @param logoImageCornerRadius logo 外边框圆角
* @param logoImageBorderWidth logo 外边框宽度
* @param logoImageBorderColor logo 外边框颜色
*/
+ (UIImage *)generateQRCodeWithData:(NSString *)data size:(CGFloat)size logoImage:(UIImage *)logoImage ratio:(CGFloat)ratio logoImageCornerRadius:(CGFloat)logoImageCornerRadius logoImageBorderWidth:(CGFloat)logoImageBorderWidth logoImageBorderColor:(UIColor *)logoImageBorderColor {
UIImage *image = [self generateQRCodeWithData:data size:size color:[UIColor blackColor] backgroundColor:[UIColor whiteColor]];
if (logoImage == nil) return image;
if (ratio < 0.0 || ratio > 0.5) {
ratio = 0.25;
}
CGFloat logoImageW = ratio * size;
CGFloat logoImageH = logoImageW;
CGFloat logoImageX = 0.5 * (image.size.width - logoImageW);
CGFloat logoImageY = 0.5 * (image.size.height - logoImageH);
CGRect logoImageRect = CGRectMake(logoImageX, logoImageY, logoImageW, logoImageH);
// 绘制logo
UIGraphicsBeginImageContextWithOptions(image.size, false, [UIScreen mainScreen].scale);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
if (logoImageCornerRadius < 0.0 || logoImageCornerRadius > 10) {
logoImageCornerRadius = 5;
}
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:logoImageRect cornerRadius:logoImageCornerRadius];
if (logoImageBorderWidth < 0.0 || logoImageBorderWidth > 10) {
logoImageBorderWidth = 5;
}
path.lineWidth = logoImageBorderWidth;
[logoImageBorderColor setStroke];
[path stroke];
[path addClip];
[logoImage drawInRect:logoImageRect];
UIImage *QRCodeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return QRCodeImage;
}
#pragma mark - - 扫描二维码相关方法
- (void)establishQRCodeObtainScanWithController:(UIViewController *)controller configure:(SGQRCodeObtainConfigure *)configure {
if (controller == nil) {
@throw [NSException exceptionWithName:@"SGQRCode" reason:@"SGQRCodeObtain 中 establishQRCodeObtainScanWithController:configuration:方法的 controller 参数不能为空" userInfo:nil];
}
if (configure == nil) {
@throw [NSException exceptionWithName:@"SGQRCode" reason:@"SGQRCodeObtain 中 establishQRCodeObtainScanWithController:configure:方法的 configure 参数不能为空" userInfo:nil];
}
_controller = controller;
_configure = configure;
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 1、捕获设备输入流
AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
// 2、捕获元数据输出流
AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
[metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 设置扫描范围(每一个取值 0 1以屏幕右上角为坐标原点
// 注:微信二维码的扫描范围是整个屏幕,这里并没有做处理(可不用设置)
if (configure.rectOfInterest.origin.x == 0 && configure.rectOfInterest.origin.y == 0 && configure.rectOfInterest.size.width == 0 && configure.rectOfInterest.size.height == 0) {
} else {
metadataOutput.rectOfInterest = configure.rectOfInterest;
}
// 3、设置会话采集率
self.captureSession.sessionPreset = configure.sessionPreset;
// 4(1)、添加捕获元数据输出流到会话对象
[_captureSession addOutput:metadataOutput];
// 4(2)、添加捕获输出流到会话对象;构成识了别光线强弱
if (configure.sampleBufferDelegate == YES) {
AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
[videoDataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
[_captureSession addOutput:videoDataOutput];
}
// 4(3)、添加捕获设备输入流到会话对象
[_captureSession addInput:deviceInput];
// 5、设置数据输出类型需要将数据输出添加到会话后才能指定元数据类型否则会报错
metadataOutput.metadataObjectTypes = configure.metadataObjectTypes;
// 6、预览图层
AVCaptureVideoPreviewLayer *videoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_captureSession];
// 保持纵横比,填充层边界
videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
videoPreviewLayer.frame = controller.view.frame;
[controller.view.layer insertSublayer:videoPreviewLayer atIndex:0];
}
- (AVCaptureSession *)captureSession {
if (!_captureSession) {
_captureSession = [[AVCaptureSession alloc] init];
}
return _captureSession;
}
- (void)startRunningWithBefore:(void (^)(void))before completion:(void (^)(void))completion {
if (before) {
before();
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.captureSession startRunning];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion();
}
});
});
}
- (void)stopRunning {
if (self.captureSession.isRunning) {
[self.captureSession stopRunning];
}
}
#pragma mark - - AVCaptureMetadataOutputObjectsDelegate 的方法
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
NSString *resultString = nil;
if (metadataObjects != nil && metadataObjects.count > 0) {
AVMetadataMachineReadableCodeObject *obj = metadataObjects[0];
resultString = [obj stringValue];
if (_scanResultBlock) {
_scanResultBlock(self, resultString);
}
}
}
#pragma mark - - AVCaptureVideoDataOutputSampleBufferDelegate 的方法
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
CFRelease(metadataDict);
NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
CGFloat brightnessValue = [[exifMetadata objectForKey:(NSString *)kCGImagePropertyExifBrightnessValue] floatValue];
if (_scanBrightnessBlock) {
_scanBrightnessBlock(self, brightnessValue);
}
}
- (void)setBlockWithQRCodeObtainScanResult:(SGQRCodeObtainScanResultBlock)block {
_scanResultBlock = block;
}
- (void)setBlockWithQRCodeObtainScanBrightness:(SGQRCodeObtainScanBrightnessBlock)block {
_scanBrightnessBlock = block;
}
- (void)playSoundName:(NSString *)name {
/// 静态库 path 的获取
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:nil];
if (!path) {
/// 动态库 path 的获取
path = [[NSBundle bundleForClass:[self class]] pathForResource:name ofType:nil];
}
NSURL *fileUrl = [NSURL fileURLWithPath:path];
SystemSoundID soundID = 0;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
AudioServicesPlaySystemSound(soundID);
}
void soundCompleteCallback(SystemSoundID soundID, void *clientData){
}
#pragma mark - - 相册中读取二维码相关方法
- (void)establishAuthorizationQRCodeObtainAlbumWithController:(UIViewController *)controller {
if (controller == nil && _controller == nil) {
@throw [NSException exceptionWithName:@"SGQRCode" reason:@"SGQRCodeObtain 中 establishAuthorizationQRCodeObtainAlbumWithController: 方法的 controller 参数不能为空" userInfo:nil];
}
if (_controller == nil) {
_controller = controller;
}
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (device) {
// 判断授权状态
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusNotDetermined) { // 用户还没有做出选择
// 弹框请求用户授权
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) { // 用户第一次同意了访问相册权限
self.isPHAuthorization = YES;
dispatch_sync(dispatch_get_main_queue(), ^{
[self P_enterImagePickerController];
});
if (self.configure.openLog == YES) {
NSLog(@"用户第一次同意了访问相册权限");
}
} else { // 用户第一次拒绝了访问相机权限
if (self.configure.openLog == YES) {
NSLog(@"用户第一次拒绝了访问相册权限");
}
}
}];
} else if (status == PHAuthorizationStatusAuthorized) { // 用户允许当前应用访问相册
self.isPHAuthorization = YES;
if (self.configure.openLog == YES) {
NSLog(@"用户允许访问相册权限");
}
[self P_enterImagePickerController];
} else if (status == PHAuthorizationStatusDenied) { // 用户拒绝当前应用访问相册
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
NSString *app_Name = [infoDict objectForKey:@"CFBundleDisplayName"];
if (app_Name == nil) {
app_Name = [infoDict objectForKey:@"CFBundleName"];
}
NSString *messageString = [NSString stringWithFormat:@"[前往:设置 - 隐私 - 照片 - %@] 允许应用访问", app_Name];
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"温馨提示" message:messageString preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *alertA = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
}];
[alertC addAction:alertA];
[_controller presentViewController:alertC animated:YES completion:nil];
} else if (status == PHAuthorizationStatusRestricted) {
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"由于系统原因, 无法访问相册" preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *alertA = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
}];
[alertC addAction:alertA];
[_controller presentViewController:alertC animated:YES completion:nil];
}
}
}
- (void)P_enterImagePickerController {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.delegate = self;
[_controller presentViewController:imagePicker animated:YES completion:nil];
}
#pragma mark - - UIImagePickerControllerDelegate 的方法
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[_controller dismissViewControllerAnimated:YES completion:nil];
if (_albumDidCancelImagePickerControllerBlock) {
_albumDidCancelImagePickerControllerBlock(self);
}
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// 创建 CIDetector并设定识别类型CIDetectorTypeQRCode
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
// 获取识别结果
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
if (features.count == 0) {
[_controller dismissViewControllerAnimated:YES completion:^{
if (self.albumResultBlock) {
self.albumResultBlock(self, nil);
}
}];
return;
} else {
for (int index = 0; index < [features count]; index ++) {
CIQRCodeFeature *feature = [features objectAtIndex:index];
NSString *resultStr = feature.messageString;
if (_configure.openLog == YES) {
NSLog(@"相册中读取二维码数据信息 - - %@", resultStr);
}
self.detectorString = resultStr;
}
[_controller dismissViewControllerAnimated:YES completion:^{
if (self.albumResultBlock) {
self.albumResultBlock(self, self.detectorString);
}
}];
}
}
- (void)setBlockWithQRCodeObtainAlbumDidCancelImagePickerController:(SGQRCodeObtainAlbumDidCancelImagePickerControllerBlock)block {
_albumDidCancelImagePickerControllerBlock = block;
}
- (void)setBlockWithQRCodeObtainAlbumResult:(SGQRCodeObtainAlbumResultBlock)block {
_albumResultBlock = block;
}
#pragma mark - - 手电筒相关方法
- (void)openFlashlight {
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
if ([captureDevice hasTorch]) {
BOOL locked = [captureDevice lockForConfiguration:&error];
if (locked) {
[captureDevice setTorchMode:AVCaptureTorchModeOn];
[captureDevice unlockForConfiguration];
}
}
}
- (void)closeFlashlight {
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([captureDevice hasTorch]) {
[captureDevice lockForConfiguration:nil];
[captureDevice setTorchMode:AVCaptureTorchModeOff];
[captureDevice unlockForConfiguration];
}
}
@end