ios-unity/Classes_cocos/QRCodeReaderViewController/QRCodeReaderViewController.m
2022-12-06 19:41:36 +08:00

423 lines
15 KiB
Objective-C

/*
* QRCodeReaderViewController
*
* Copyright 2014-present Yannick Loriot.
* http://yannickloriot.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#import "QRCodeReaderViewController.h"
#import "QRCameraSwitchButton.h"
#import "QRCodeReaderView.h"
#import "QRToggleTorchButton.h"
#import "QRPhotoAlbumButton.h"
#import "LBXPermission.h"
#import "LBXPermissionSetting.h"
#import "LBXScanNative.h"
@interface QRCodeReaderViewController ()<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@property (strong, nonatomic) QRCameraSwitchButton *switchCameraButton;
@property (strong, nonatomic) QRPhotoAlbumButton *photoAlbumButton;
@property (strong, nonatomic) QRToggleTorchButton *toggleTorchButton;
@property (strong, nonatomic) QRCodeReaderView *cameraView;
@property (strong, nonatomic) UIButton *cancelButton;
@property (strong, nonatomic) QRCodeReader *codeReader;
@property (assign, nonatomic) BOOL startScanningAtLoad;
@property (assign, nonatomic) BOOL showSwitchCameraButton;
@property (assign, nonatomic) BOOL showTorchButton;
@end
@implementation QRCodeReaderViewController
- (void)dealloc
{
[self stopScanning];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (id)init
{
return [self initWithCancelButtonTitle:nil];
}
- (id)initWithCancelButtonTitle:(NSString *)cancelTitle
{
return [self initWithCancelButtonTitle:cancelTitle metadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
}
- (id)initWithMetadataObjectTypes:(NSArray *)metadataObjectTypes
{
return [self initWithCancelButtonTitle:nil metadataObjectTypes:metadataObjectTypes];
}
- (id)initWithCancelButtonTitle:(NSString *)cancelTitle metadataObjectTypes:(NSArray *)metadataObjectTypes
{
QRCodeReader *reader = [QRCodeReader readerWithMetadataObjectTypes:metadataObjectTypes];
return [self initWithCancelButtonTitle:cancelTitle codeReader:reader];
}
- (id)initWithCancelButtonTitle:(NSString *)cancelTitle codeReader:(QRCodeReader *)codeReader
{
return [self initWithCancelButtonTitle:cancelTitle codeReader:codeReader startScanningAtLoad:true];
}
- (id)initWithCancelButtonTitle:(NSString *)cancelTitle codeReader:(QRCodeReader *)codeReader startScanningAtLoad:(BOOL)startScanningAtLoad
{
return [self initWithCancelButtonTitle:cancelTitle codeReader:codeReader startScanningAtLoad:startScanningAtLoad showSwitchCameraButton:YES showTorchButton:NO];
}
- (id)initWithCancelButtonTitle:(nullable NSString *)cancelTitle codeReader:(nonnull QRCodeReader *)codeReader startScanningAtLoad:(BOOL)startScanningAtLoad showSwitchCameraButton:(BOOL)showSwitchCameraButton showTorchButton:(BOOL)showTorchButton
{
if ((self = [super init])) {
self.view.backgroundColor = [UIColor blackColor];
self.codeReader = codeReader;
self.startScanningAtLoad = startScanningAtLoad;
self.showSwitchCameraButton = showSwitchCameraButton;
self.showTorchButton = showTorchButton;
if (cancelTitle == nil) {
cancelTitle = NSLocalizedString(@"Cancel", @"Cancel");
}
[self setupUIComponentsWithCancelButtonTitle:cancelTitle];
[self setupAutoLayoutConstraints];
[_cameraView.layer insertSublayer:_codeReader.previewLayer atIndex:0];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
__weak __typeof__(self) weakSelf = self;
[codeReader setCompletionWithBlock:^(NSString *resultAsString) {
if (weakSelf.completion) {
weakSelf.completion(resultAsString, nil);
}
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(reader:didScanResult:)]) {
[weakSelf.delegate reader:weakSelf didScanResult:resultAsString];
}
[weakSelf dismissViewControllerAnimated:YES completion:nil];
}];
}
return self;
}
+ (instancetype)readerWithCancelButtonTitle:(NSString *)cancelTitle
{
return [[self alloc] initWithCancelButtonTitle:cancelTitle];
}
+ (instancetype)readerWithMetadataObjectTypes:(NSArray *)metadataObjectTypes
{
return [[self alloc] initWithMetadataObjectTypes:metadataObjectTypes];
}
+ (instancetype)readerWithCancelButtonTitle:(NSString *)cancelTitle metadataObjectTypes:(NSArray *)metadataObjectTypes
{
return [[self alloc] initWithCancelButtonTitle:cancelTitle metadataObjectTypes:metadataObjectTypes];
}
+ (instancetype)readerWithCancelButtonTitle:(NSString *)cancelTitle codeReader:(QRCodeReader *)codeReader
{
return [[self alloc] initWithCancelButtonTitle:cancelTitle codeReader:codeReader];
}
+ (instancetype)readerWithCancelButtonTitle:(NSString *)cancelTitle codeReader:(QRCodeReader *)codeReader startScanningAtLoad:(BOOL)startScanningAtLoad
{
return [[self alloc] initWithCancelButtonTitle:cancelTitle codeReader:codeReader startScanningAtLoad:startScanningAtLoad];
}
+ (instancetype)readerWithCancelButtonTitle:(NSString *)cancelTitle codeReader:(QRCodeReader *)codeReader startScanningAtLoad:(BOOL)startScanningAtLoad showSwitchCameraButton:(BOOL)showSwitchCameraButton showTorchButton:(BOOL)showTorchButton
{
return [[self alloc] initWithCancelButtonTitle:cancelTitle codeReader:codeReader startScanningAtLoad:startScanningAtLoad showSwitchCameraButton:showSwitchCameraButton showTorchButton:showTorchButton];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (_startScanningAtLoad) {
[self startScanning];
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[self stopScanning];
[super viewWillDisappear:animated];
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
_codeReader.previewLayer.frame = self.view.bounds;
}
- (BOOL)shouldAutorotate
{
return YES;
}
#pragma mark - Controlling the Reader
- (void)startScanning {
[_codeReader startScanning];
}
- (void)stopScanning {
[_codeReader stopScanning];
}
#pragma mark - Managing the Orientation
- (void)orientationChanged:(NSNotification *)notification
{
[_cameraView setNeedsDisplay];
if (_codeReader.previewLayer.connection.isVideoOrientationSupported) {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
_codeReader.previewLayer.connection.videoOrientation = [QRCodeReader videoOrientationFromInterfaceOrientation:
orientation];
}
}
#pragma mark - Managing the Block
- (void)setCompletionWithBlock:(nullable QRReadCompletion)completion
{
self.completion = completion;
}
#pragma mark - Initializing the AV Components
- (void)setupUIComponentsWithCancelButtonTitle:(NSString *)cancelButtonTitle
{
self.cameraView = [[QRCodeReaderView alloc] init];
_cameraView.translatesAutoresizingMaskIntoConstraints = NO;
_cameraView.clipsToBounds = YES;
[self.view addSubview:_cameraView];
[_codeReader.previewLayer setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
if ([_codeReader.previewLayer.connection isVideoOrientationSupported]) {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
_codeReader.previewLayer.connection.videoOrientation = [QRCodeReader videoOrientationFromInterfaceOrientation:orientation];
}
if (_showSwitchCameraButton && [_codeReader hasFrontDevice]) {
_switchCameraButton = [[QRCameraSwitchButton alloc] init];
[_switchCameraButton setTranslatesAutoresizingMaskIntoConstraints:false];
[_switchCameraButton addTarget:self action:@selector(switchCameraAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_switchCameraButton];
}
if (_showTorchButton && [_codeReader isTorchAvailable]) {
_toggleTorchButton = [[QRToggleTorchButton alloc] init];
[_toggleTorchButton setTranslatesAutoresizingMaskIntoConstraints:false];
[_toggleTorchButton addTarget:self action:@selector(toggleTorchAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_toggleTorchButton];
}
self.cancelButton = [[UIButton alloc] init];
_cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
[_cancelButton setTitle:cancelButtonTitle forState:UIControlStateNormal];
[_cancelButton setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted];
[_cancelButton addTarget:self action:@selector(cancelAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_cancelButton];
if (@available(iOS 8.0, *)) {
_photoAlbumButton = [[QRPhotoAlbumButton alloc] init];
[_photoAlbumButton setTranslatesAutoresizingMaskIntoConstraints:false];
[_photoAlbumButton addTarget:self action:@selector(showPhotoLibrary:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_photoAlbumButton];
}
}
- (void)setupAutoLayoutConstraints
{
NSLayoutYAxisAnchor * topLayoutAnchor;
NSLayoutYAxisAnchor * bottomLayoutAnchor;
NSLayoutXAxisAnchor * leftLayoutAnchor;
NSLayoutXAxisAnchor * rightLayoutAnchor;
if (@available(iOS 11.0, *)) {
topLayoutAnchor = self.view.safeAreaLayoutGuide.topAnchor;
bottomLayoutAnchor = self.view.safeAreaLayoutGuide.bottomAnchor;
leftLayoutAnchor = self.view.safeAreaLayoutGuide.leftAnchor;
rightLayoutAnchor = self.view.safeAreaLayoutGuide.rightAnchor;
} else {
topLayoutAnchor = self.topLayoutGuide.topAnchor;
bottomLayoutAnchor = self.bottomLayoutGuide.bottomAnchor;
leftLayoutAnchor = self.view.leftAnchor;
rightLayoutAnchor = self.view.rightAnchor;
}
NSDictionary *views = NSDictionaryOfVariableBindings(_cameraView, _cancelButton);
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_cameraView][_cancelButton(40)]" options:0 metrics:nil views:views]];
[[bottomLayoutAnchor constraintEqualToAnchor:_cancelButton.bottomAnchor] setActive:YES];
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_cameraView]|" options:0 metrics:nil views:views]];
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_cancelButton]-|" options:0 metrics:nil views:views]];
if (_switchCameraButton) {
[NSLayoutConstraint activateConstraints:@[
[topLayoutAnchor constraintEqualToAnchor:_switchCameraButton.topAnchor],
[rightLayoutAnchor constraintEqualToAnchor:_switchCameraButton.rightAnchor],
[_switchCameraButton.heightAnchor constraintEqualToConstant:50],
[_switchCameraButton.widthAnchor constraintEqualToConstant:70]
]];
}
if (_toggleTorchButton) {
[NSLayoutConstraint activateConstraints:@[
[topLayoutAnchor constraintEqualToAnchor:_toggleTorchButton.topAnchor],
[leftLayoutAnchor constraintEqualToAnchor:_toggleTorchButton.leftAnchor],
[_toggleTorchButton.heightAnchor constraintEqualToConstant:50],
[_toggleTorchButton.widthAnchor constraintEqualToConstant:70]
]];
}
if (_photoAlbumButton) {
[NSLayoutConstraint activateConstraints:@[
[topLayoutAnchor constraintEqualToAnchor:_photoAlbumButton.topAnchor constant:-70],
[rightLayoutAnchor constraintEqualToAnchor:_photoAlbumButton.rightAnchor],
[_photoAlbumButton.heightAnchor constraintEqualToConstant:50],
[_photoAlbumButton.widthAnchor constraintEqualToConstant:70]
]];
}
}
- (void)switchDeviceInput
{
[_codeReader switchDeviceInput];
}
#pragma mark - Catching Button Events
- (void)cancelAction:(UIButton *)button
{
[_codeReader stopScanning];
if (self.completion) {
NSError *err = [NSError errorWithDomain:@"qrreader"
code:100
userInfo:@{
NSLocalizedDescriptionKey:@"cancel"
}];
self.completion(nil, err);
}
if (_delegate && [_delegate respondsToSelector:@selector(readerDidCancel:)]) {
[_delegate readerDidCancel:self];
}
}
- (void)switchCameraAction:(UIButton *)button
{
[self switchDeviceInput];
}
- (void)toggleTorchAction:(UIButton *)button
{
[_codeReader toggleTorch];
}
- (void)showPhotoLibrary:(UIButton *)button {
__weak __typeof(self) weakSelf = self;
[LBXPermission authorizeWithType:LBXPermissionType_Photos completion:^(BOOL granted, BOOL firstTime) {
if (granted) {
[weakSelf openLocalPhoto];
} else if (!firstTime ) {
[LBXPermissionSetting showAlertToDislayPrivacySettingWithTitle:@"Error" msg:@"The Photo Library is need to restore recovery key." cancel:@"Cancel" setting:@"Setting" completion:^{
// TODO:: cancel action
}];
}
}];
}
- (void)openLocalPhoto {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
// crash on some mobile
picker.allowsEditing = NO;
[self presentViewController:picker animated:YES completion:nil];
}
#pragma mark- - UIImagePickerControllerDelegate
-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
[picker dismissViewControllerAnimated:YES completion:^{
[self handPhotoDidFinishPickingMediaWithInfo:info];
}];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController*)picker {
[picker dismissViewControllerAnimated:YES completion:^{
NSLog(@"imagePickerControllerDidCancel");
}];
}
- (void)handPhotoDidFinishPickingMediaWithInfo:(NSDictionary *)info {
__block UIImage* image = [info objectForKey:UIImagePickerControllerEditedImage];
if (!image){
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
__weak __typeof(self) weakSelf = self;
[LBXScanNative recognizeImage:image success:^(NSArray<LBXScanResult *> *array) {
[weakSelf scanResultWithArray:array];
}];
}
- (void)scanResultWithArray:(NSArray<LBXScanResult*>*)array {
if (!array || array.count < 1) {
NSLog(@"error scan photo");
return;
}
if (!array[0].strScanned || [array[0].strScanned isEqualToString:@""] ) {
NSLog(@"failed decoding QR code");
return;
}
LBXScanResult *scanResult = array[0];
if (self.completion) {
self.completion(scanResult.strScanned, nil);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
@end