Files
rog_app/plugin/qr_code_scanner/ios/Classes/QRView.swift

305 lines
13 KiB
Swift

//
// QRView.swift
// flutter_qr
//
// Created by Julius Canute on 21/12/18.
//
import Foundation
import MTBBarcodeScanner
public class QRView:NSObject,FlutterPlatformView {
@IBOutlet var previewView: UIView!
var scanner: MTBBarcodeScanner?
var registrar: FlutterPluginRegistrar
var channel: FlutterMethodChannel
var cameraFacing: MTBCamera
// Codabar, maxicode, rss14 & rssexpanded not supported. Replaced with qr.
// UPCa uses ean13 object.
var QRCodeTypes = [
0: AVMetadataObject.ObjectType.aztec,
1: AVMetadataObject.ObjectType.qr,
2: AVMetadataObject.ObjectType.code39,
3: AVMetadataObject.ObjectType.code93,
4: AVMetadataObject.ObjectType.code128,
5: AVMetadataObject.ObjectType.dataMatrix,
6: AVMetadataObject.ObjectType.ean8,
7: AVMetadataObject.ObjectType.ean13,
8: AVMetadataObject.ObjectType.interleaved2of5,
9: AVMetadataObject.ObjectType.qr,
10: AVMetadataObject.ObjectType.pdf417,
11: AVMetadataObject.ObjectType.qr,
12: AVMetadataObject.ObjectType.qr,
13: AVMetadataObject.ObjectType.qr,
14: AVMetadataObject.ObjectType.ean13,
15: AVMetadataObject.ObjectType.upce
]
public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary<String, Any>){
self.registrar = registrar
previewView = UIView(frame: frame)
cameraFacing = MTBCamera.init(rawValue: UInt(Int(params["cameraFacing"] as! Double))) ?? MTBCamera.back
channel = FlutterMethodChannel(name: "net.touchcapture.qr.flutterqr/qrview_\(id)", binaryMessenger: registrar.messenger())
}
deinit {
scanner?.stopScanning()
}
public func view() -> UIView {
channel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
switch(call.method){
case "setDimensions":
let arguments = call.arguments as! Dictionary<String, Double>
self?.setDimensions(result,
width: arguments["width"] ?? 0,
height: arguments["height"] ?? 0,
scanAreaWidth: arguments["scanAreaWidth"] ?? 0,
scanAreaHeight: arguments["scanAreaHeight"] ?? 0,
scanAreaOffset: arguments["scanAreaOffset"] ?? 0)
case "startScan":
self?.startScan(call.arguments as! Array<Int>, result)
case "flipCamera":
self?.flipCamera(result)
case "toggleFlash":
self?.toggleFlash(result)
case "pauseCamera":
self?.pauseCamera(result)
case "stopCamera":
self?.stopCamera(result)
case "resumeCamera":
self?.resumeCamera(result)
case "getCameraInfo":
self?.getCameraInfo(result)
case "getFlashInfo":
self?.getFlashInfo(result)
case "getSystemFeatures":
self?.getSystemFeatures(result)
default:
result(FlutterMethodNotImplemented)
return
}
})
return previewView
}
func setDimensions(_ result: @escaping FlutterResult, width: Double, height: Double, scanAreaWidth: Double, scanAreaHeight: Double, scanAreaOffset: Double) {
// Then set the size of the preview area.
previewView.frame = CGRect(x: 0, y: 0, width: width, height: height)
// Then set the size of the scan area.
let midX = self.view().bounds.midX
let midY = self.view().bounds.midY
if let sc: MTBBarcodeScanner = scanner {
// Set the size of the preview if preview is already created.
if let previewLayer = sc.previewLayer {
previewLayer.frame = self.previewView.bounds
}
} else {
// Create new preview.
scanner = MTBBarcodeScanner(previewView: previewView)
}
// Set scanArea if provided.
if (scanAreaWidth != 0 && scanAreaHeight != 0) {
scanner?.didStartScanningBlock = {
self.scanner?.scanRect = CGRect(x: Double(midX) - (scanAreaWidth / 2), y: Double(midY) - (scanAreaHeight / 2), width: scanAreaWidth, height: scanAreaHeight)
// Set offset if provided.
if (scanAreaOffset != 0) {
let reversedOffset = -scanAreaOffset
self.scanner?.scanRect = (self.scanner?.scanRect.offsetBy(dx: 0, dy: CGFloat(reversedOffset)))!
}
}
}
return result(width)
}
func startScan(_ arguments: Array<Int>, _ result: @escaping FlutterResult) {
// Check for allowed barcodes
var allowedBarcodeTypes: Array<AVMetadataObject.ObjectType> = []
arguments.forEach { arg in
allowedBarcodeTypes.append( QRCodeTypes[arg]!)
}
MTBBarcodeScanner.requestCameraPermission(success: { [weak self] permissionGranted in
guard let self = self else { return }
self.channel.invokeMethod("onPermissionSet", arguments: permissionGranted)
if permissionGranted {
do {
try self.scanner?.startScanning(with: self.cameraFacing, resultBlock: { [weak self] codes in
if let codes = codes {
for code in codes {
var typeString: String;
switch(code.type) {
case AVMetadataObject.ObjectType.aztec:
typeString = "AZTEC"
case AVMetadataObject.ObjectType.code39:
typeString = "CODE_39"
case AVMetadataObject.ObjectType.code93:
typeString = "CODE_93"
case AVMetadataObject.ObjectType.code128:
typeString = "CODE_128"
case AVMetadataObject.ObjectType.dataMatrix:
typeString = "DATA_MATRIX"
case AVMetadataObject.ObjectType.ean8:
typeString = "EAN_8"
case AVMetadataObject.ObjectType.ean13:
typeString = "EAN_13"
case AVMetadataObject.ObjectType.itf14,
AVMetadataObject.ObjectType.interleaved2of5:
typeString = "ITF"
case AVMetadataObject.ObjectType.pdf417:
typeString = "PDF_417"
case AVMetadataObject.ObjectType.qr:
typeString = "QR_CODE"
case AVMetadataObject.ObjectType.upce:
typeString = "UPC_E"
default:
return
}
let bytes = { () -> Data? in
if #available(iOS 11.0, *) {
switch (code.descriptor) {
case let qrDescriptor as CIQRCodeDescriptor:
return qrDescriptor.errorCorrectedPayload
case let aztecDescriptor as CIAztecCodeDescriptor:
return aztecDescriptor.errorCorrectedPayload
case let pdf417Descriptor as CIPDF417CodeDescriptor:
return pdf417Descriptor.errorCorrectedPayload
case let dataMatrixDescriptor as CIDataMatrixCodeDescriptor:
return dataMatrixDescriptor.errorCorrectedPayload
default:
return nil
}
} else {
return nil
}
}()
let result = { () -> [String : Any]? in
guard let stringValue = code.stringValue else {
guard let safeBytes = bytes else {
return nil
}
return ["type": typeString, "rawBytes": safeBytes]
}
guard let safeBytes = bytes else {
return ["code": stringValue, "type": typeString]
}
return ["code": stringValue, "type": typeString, "rawBytes": safeBytes]
}()
guard result != nil else { continue }
if allowedBarcodeTypes.count == 0 || allowedBarcodeTypes.contains(code.type) {
self?.channel.invokeMethod("onRecognizeQR", arguments: result)
}
}
}
})
} catch {
let scanError = FlutterError(code: "unknown-error", message: "Unable to start scanning", details: error)
result(scanError)
}
}
})
}
func stopCamera(_ result: @escaping FlutterResult) {
if let sc: MTBBarcodeScanner = self.scanner {
if sc.isScanning() {
sc.stopScanning()
}
}
}
func getCameraInfo(_ result: @escaping FlutterResult) {
result(self.cameraFacing.rawValue)
}
func flipCamera(_ result: @escaping FlutterResult) {
if let sc: MTBBarcodeScanner = self.scanner {
if sc.hasOppositeCamera() {
sc.flipCamera()
self.cameraFacing = sc.camera
}
return result(sc.camera.rawValue)
}
return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil))
}
func getFlashInfo(_ result: @escaping FlutterResult) {
if let sc: MTBBarcodeScanner = self.scanner {
result(sc.torchMode.rawValue != 0)
} else {
let error = FlutterError(code: "cameraInformationError", message: "Could not get flash information", details: nil)
result(error)
}
}
func toggleFlash(_ result: @escaping FlutterResult){
if let sc: MTBBarcodeScanner = self.scanner {
if sc.hasTorch() {
sc.toggleTorch()
return result(sc.torchMode == MTBTorchMode(rawValue: 1))
}
return result(FlutterError(code: "404", message: "This device doesn\'t support flash", details: nil))
}
return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil))
}
func pauseCamera(_ result: @escaping FlutterResult) {
if let sc: MTBBarcodeScanner = self.scanner {
if sc.isScanning() {
sc.freezeCapture()
}
return result(true)
}
return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil))
}
func resumeCamera(_ result: @escaping FlutterResult) {
if let sc: MTBBarcodeScanner = self.scanner {
if !sc.isScanning() {
sc.unfreezeCapture()
}
return result(true)
}
return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil))
}
func getSystemFeatures(_ result: @escaping FlutterResult) {
if let sc: MTBBarcodeScanner = scanner {
var hasBackCameraVar = false
var hasFrontCameraVar = false
let camera = sc.camera
if(camera == MTBCamera(rawValue: 0)){
hasBackCameraVar = true
if sc.hasOppositeCamera() {
hasFrontCameraVar = true
}
}else{
hasFrontCameraVar = true
if sc.hasOppositeCamera() {
hasBackCameraVar = true
}
}
return result([
"hasFrontCamera": hasFrontCameraVar,
"hasBackCamera": hasBackCameraVar,
"hasFlash": sc.hasTorch(),
"activeCamera": camera.rawValue
])
}
return result(FlutterError(code: "404", message: nil, details: nil))
}
}