import UIKit import Flutter @objc class ScannerViewController: UIViewController, ZXCaptureDelegate, UITextFieldDelegate { enum AppLocale { case ru case en } enum ButtonState { case card case phone var icon: UIImage { switch self { case .card: return UIImage(named: "card")! case .phone: return UIImage(named: "phone")! } } var searchType: String { switch self { case .card: return "card" case .phone: return "phone" } } } // @IBOutlet weak var decodedLabel: UILabel! private var captureSizeTransform: CGAffineTransform? var buttonState: ButtonState = .card var platformChannel: FlutterMethodChannel? // Квадрат для наведения на цель (надеюсь) let scanRectView = UIView() //Вьюшка для верхнего меню let topView = UIView() //Окно ввода кода let textField = UITextField() //Кнопка настроек var settingButton: UIButton! var strings = [String:String]() required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } init(strings: [String: String]) { super.init(nibName: nil, bundle: nil) strings.forEach { (k,v) in self.strings[k] = v } } func getInputHint() -> String { switch self.buttonState { case .card: return strings["enter_manual"]! case .phone: return strings["enter_phone"]! } } func getErrorText() -> String { switch self.buttonState { case .card: return strings["user_card_not_found"]! case .phone: return strings["user_phone_not_found"]! } } func setButtonState() { switch self.buttonState { case .card: self.buttonState = .phone case .phone: self.buttonState = .card } } let capture: ZXCapture = ZXCapture() override func viewDidLoad() { super.viewDidLoad() capture.camera = capture.back() capture.focusMode = .continuousAutoFocus view.layer.addSublayer((capture.layer)!) view.addSubview(scanRectView) settingButton = UIButton(type: .system) settingButton.addTarget(self, action: #selector(ScannerViewController.buttonTouch), for: .touchUpInside) topView.addSubview(textField) topView.addSubview(settingButton) view.addSubview(topView) view.bringSubview(toFront: topView) textField.delegate = self let tap = UITapGestureRecognizer(target: self, action: #selector(ScannerViewController.hideKeyboard)) view.addGestureRecognizer(tap) settingButton.setImage(self.buttonState.icon, for: .normal) textField.placeholder = self.getInputHint() textField.text = "79087654321" } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) topView.backgroundColor = UIColor.white textField.borderStyle = .roundedRect capture.delegate = self applyOrientation() } override func viewWillLayoutSubviews() { scanRectView.frame = view.bounds topView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: 56) settingButton.frame = CGRect(x: 8, y: 26, width: 20, height: 20) textField.frame = CGRect(x: settingButton.frame.maxX + 8, y: 21, width: view.frame.size.width - settingButton.frame.maxX - 16, height: 30) } func hideKeyboard() { view.endEditing(false) } func buttonTouch(){ setButtonState() settingButton.setImage(self.buttonState.icon, for: .normal) textField.placeholder = self.getInputHint() } func textFieldShouldReturn(_ textField: UITextField) -> Bool { print("User from manual input: \(textField.text)") sendResult(textField.text!) return true } func sendResult(_ str: String) { platformChannel?.invokeMethod("findUser", arguments: [str, buttonState.searchType], result: { (result: Any?) in if result is FlutterError { print("Result is nil (ios code)"); self.showErrorAlert(str) } else { print("Result is not nil (ios code)"); self.dismiss(animated: true) { self.platformChannel?.invokeMethod("purchase", arguments: [result, str]) } } }) } func showErrorAlert(_ str: String) { let alertController = UIAlertController( title: strings["error"]!, message: String(format: self.getErrorText(), str), preferredStyle: UIAlertControllerStyle.alert ) alertController.addAction(UIAlertAction(title: strings["dismiss"]!, style: UIAlertActionStyle.default,handler: nil)) self.present(alertController, animated: true, completion: nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } deinit { capture.layer.removeFromSuperlayer() } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate(alongsideTransition: {(_ context: UIViewControllerTransitionCoordinatorContext) -> Void in }, completion: {(_ context: UIViewControllerTransitionCoordinatorContext) -> Void in self.applyOrientation() }) } func applyOrientation() { print("APPLY ORIENTATION") let orientation: UIInterfaceOrientation = UIApplication.shared.statusBarOrientation var scanRectRotation: Float var captureRotation: Float switch orientation { case .portrait: captureRotation = 0 scanRectRotation = 90 case .landscapeLeft: captureRotation = 90 scanRectRotation = 180 case .landscapeRight: captureRotation = 270 scanRectRotation = 0 case .portraitUpsideDown: captureRotation = 180 scanRectRotation = 270 default: captureRotation = 0 scanRectRotation = 90 } applyRectOfInterest(orientation) let transform = CGAffineTransform(rotationAngle: CGFloat(captureRotation / 180 * .pi)) capture.transform = transform capture.rotation = CGFloat(scanRectRotation) capture.layer.frame = view.frame } func applyRectOfInterest(_ orientation: UIInterfaceOrientation) { var scaleVideo: CGFloat var scaleVideoX: CGFloat var scaleVideoY: CGFloat var videoSizeX: CGFloat var videoSizeY: CGFloat var transformedVideoRect: CGRect = scanRectView.frame videoSizeX = 720 videoSizeY = 1280 if UIInterfaceOrientationIsPortrait(orientation) { scaleVideoX = view.frame.size.width / videoSizeX scaleVideoY = view.frame.size.height / videoSizeY scaleVideo = max(scaleVideoX, scaleVideoY) if scaleVideoX > scaleVideoY { transformedVideoRect.origin.y += (scaleVideo * videoSizeY - view.frame.size.height) / 2 } else { transformedVideoRect.origin.x += (scaleVideo * videoSizeX - view.frame.size.width) / 2 } } else { scaleVideoX = view.frame.size.width / videoSizeY scaleVideoY = view.frame.size.height / videoSizeX scaleVideo = max(scaleVideoX, scaleVideoY) if scaleVideoX > scaleVideoY { transformedVideoRect.origin.y += (scaleVideo * videoSizeX - view.frame.size.height) / 2 } else { transformedVideoRect.origin.x += (scaleVideo * videoSizeY - view.frame.size.width) / 2 } } captureSizeTransform = CGAffineTransform(scaleX: 1 / scaleVideo, y: 1 / scaleVideo) capture.scanRect = transformedVideoRect.applying(captureSizeTransform!) } // MARK: - Private Methods func barcodeFormat(toString format: ZXBarcodeFormat) -> String { switch format { case kBarcodeFormatAztec: return "Aztec" case kBarcodeFormatCodabar: return "CODABAR" case kBarcodeFormatCode39: return "Code 39" case kBarcodeFormatCode93: return "Code 93" case kBarcodeFormatCode128: return "Code 128" case kBarcodeFormatDataMatrix: return "Data Matrix" case kBarcodeFormatEan8: return "EAN-8" case kBarcodeFormatEan13: return "EAN-13" case kBarcodeFormatITF: return "ITF" case kBarcodeFormatPDF417: return "PDF417" case kBarcodeFormatQRCode: return "QR Code" case kBarcodeFormatRSS14: return "RSS 14" case kBarcodeFormatRSSExpanded: return "RSS Expanded" case kBarcodeFormatUPCA: return "UPCA" case kBarcodeFormatUPCE: return "UPCE" case kBarcodeFormatUPCEANExtension: return "UPC/EAN extension" default: return "Unknown" } } // MARK: - ZXCaptureDelegate Methods func captureResult(_ capture: ZXCapture, result: ZXResult) { let inverse: CGAffineTransform = captureSizeTransform!.inverted() var points = [AnyHashable]() var location = "" for resultPoint in result.resultPoints { let p = resultPoint as! ZXResultPoint let cgPoint = CGPoint(x: CGFloat(p.x), y: CGFloat(p.y)) var transformedPoint: CGPoint = cgPoint.applying(inverse) transformedPoint = scanRectView.convert(transformedPoint, to: scanRectView.window) let windowPointValue = NSValue(cgPoint: transformedPoint) location = "\(location) (\(transformedPoint.x), \(transformedPoint.y))" points.append(windowPointValue) } print("User from scanner: \(result.text)") sendResult(result.text) print(result.text) self.capture.stop() DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(2 * Double(NSEC_PER_SEC)) / Double(NSEC_PER_SEC), execute: {() -> Void in self.capture.start() }) } }