diff --git a/o2ios/O2Platform.xcodeproj/project.pbxproj b/o2ios/O2Platform.xcodeproj/project.pbxproj index 4b5da6559229e2704581f9eca96b11690aa61bcf..fb424ec7749d650a036186fec6a989c3ddcb346f 100644 --- a/o2ios/O2Platform.xcodeproj/project.pbxproj +++ b/o2ios/O2Platform.xcodeproj/project.pbxproj @@ -187,6 +187,11 @@ B167F7632362BFC200F182B8 /* CloudFileMoveFolderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B167F7622362BFC200F182B8 /* CloudFileMoveFolderController.swift */; }; B167F7662362C60200F182B8 /* CloudFileMoveFolderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B167F7642362C60200F182B8 /* CloudFileMoveFolderCell.swift */; }; B167F7672362C60200F182B8 /* CloudFileMoveFolderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B167F7652362C60200F182B8 /* CloudFileMoveFolderCell.xib */; }; + B168BDC724F5FFA10070329F /* ScanQRViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B168BDC624F5FFA10070329F /* ScanQRViewController.swift */; }; + B168BDC924F5FFA80070329F /* ScanRecoObj.swift in Sources */ = {isa = PBXBuildFile; fileRef = B168BDC824F5FFA80070329F /* ScanRecoObj.swift */; }; + B168BDCC24F607520070329F /* QRCodeResultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B168BDCA24F607520070329F /* QRCodeResultViewController.swift */; }; + B168BDCD24F607520070329F /* QRCodeResultViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B168BDCB24F607520070329F /* QRCodeResultViewController.xib */; }; + B168BDCF24F62BA60070329F /* O2CanCopyUILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B168BDCE24F62BA60070329F /* O2CanCopyUILabel.swift */; }; B169E3D421095EC9007156B3 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B169E3D321095EC9007156B3 /* Date+Extension.swift */; }; B17126C121084A2F00369F15 /* calendar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B17126C021084A2F00369F15 /* calendar.storyboard */; }; B1750078233C6908003DA7B9 /* BlockPan.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1750072233C6907003DA7B9 /* BlockPan.swift */; }; @@ -1532,6 +1537,11 @@ B167F7622362BFC200F182B8 /* CloudFileMoveFolderController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileMoveFolderController.swift; sourceTree = ""; }; B167F7642362C60200F182B8 /* CloudFileMoveFolderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileMoveFolderCell.swift; sourceTree = ""; }; B167F7652362C60200F182B8 /* CloudFileMoveFolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CloudFileMoveFolderCell.xib; sourceTree = ""; }; + B168BDC624F5FFA10070329F /* ScanQRViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanQRViewController.swift; sourceTree = ""; }; + B168BDC824F5FFA80070329F /* ScanRecoObj.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanRecoObj.swift; sourceTree = ""; }; + B168BDCA24F607520070329F /* QRCodeResultViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeResultViewController.swift; sourceTree = ""; }; + B168BDCB24F607520070329F /* QRCodeResultViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QRCodeResultViewController.xib; sourceTree = ""; }; + B168BDCE24F62BA60070329F /* O2CanCopyUILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2CanCopyUILabel.swift; sourceTree = ""; }; B169E3D321095EC9007156B3 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; B17126C021084A2F00369F15 /* calendar.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = calendar.storyboard; sourceTree = ""; }; B1750072233C6907003DA7B9 /* BlockPan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockPan.swift; sourceTree = ""; }; @@ -2869,6 +2879,14 @@ path = Presentr; sourceTree = ""; }; + B168BD9724F5FF410070329F /* m */ = { + isa = PBXGroup; + children = ( + B168BDC824F5FFA80070329F /* ScanRecoObj.swift */, + ); + path = m; + sourceTree = ""; + }; B18FFF3323756B24001B2887 /* JpushAPI */ = { isa = PBXGroup; children = ( @@ -3020,6 +3038,7 @@ 090253DD1F0E8BA6006B1609 /* O2LoadingView.swift */, B1298EB323682C21006E9236 /* O2SlideImageAFSource.swift */, B1E95F4D2373DDC4004876B7 /* ZoomImageView.swift */, + B168BDCE24F62BA60070329F /* O2CanCopyUILabel.swift */, ); path = UI; sourceTree = ""; @@ -3447,6 +3466,7 @@ E45CD4211DFE503C008F99AD /* Scan-二维码扫码 */ = { isa = PBXGroup; children = ( + B168BD9724F5FF410070329F /* m */, E45CD4221DFE503C008F99AD /* c */, ); path = "Scan-二维码扫码"; @@ -3455,8 +3475,11 @@ E45CD4221DFE503C008F99AD /* c */ = { isa = PBXGroup; children = ( + B168BDC624F5FFA10070329F /* ScanQRViewController.swift */, B1B21459216073B400D9CA7E /* ScanHelper.swift */, E457558C1E0BA26000EC44F4 /* NewScanViewController.swift */, + B168BDCA24F607520070329F /* QRCodeResultViewController.swift */, + B168BDCB24F607520070329F /* QRCodeResultViewController.xib */, ); path = c; sourceTree = ""; @@ -5628,6 +5651,7 @@ E46E6CAE1DD41F5D00AB7561 /* ZSSundo@2x.png in Resources */, E46E6C921DD41F5D00AB7561 /* ZSSlink@2x.png in Resources */, E4C24B4920844F3C00E426B0 /* JCMessageImageCollectionViewCell.xib in Resources */, + B168BDCD24F607520070329F /* QRCodeResultViewController.xib in Resources */, E4C24C1820844F5200E426B0 /* yh_image_picked@2x.png in Resources */, E45755A61E0BA72E00EC44F4 /* qrcode_scan_part_net.png in Resources */, B1489B52248E192D009EE9FD /* IMChatMessageViewCell.xib in Resources */, @@ -6236,6 +6260,7 @@ B12FD1D42283D5B700E636BA /* ThemeStatePicker.swift in Sources */, E4B888FF1D9D48F1002E1A46 /* MainTodoTaskCell.swift in Sources */, B1B6A8F9217710C800C10F3C /* FaceRecognizeAPI.swift in Sources */, + B168BDCF24F62BA60070329F /* O2CanCopyUILabel.swift in Sources */, B1EE2CD02281771600842F48 /* O2JsApiUtil.swift in Sources */, E4B6975020762EA00062F6E8 /* OOPasswordTextField.swift in Sources */, E491664C1E03C09C006133C5 /* O2Account.swift in Sources */, @@ -6385,6 +6410,7 @@ E4B6982E207B5BC30062F6E8 /* UIViewController+Extension.swift in Sources */, E4C24B8B20844F3C00E426B0 /* JCBusinessCardContent.swift in Sources */, E491664E1E03D2C1006133C5 /* O2CountdownButton.swift in Sources */, + B168BDC724F5FFA10070329F /* ScanQRViewController.swift in Sources */, E4E212491E74F96400B1544D /* MainTaskSecondViewController.swift in Sources */, 09E02E021F12736C00579887 /* PersonV2.swift in Sources */, B1B0102F23586B34002BF874 /* CFFileTableViewCell.swift in Sources */, @@ -6436,6 +6462,7 @@ 09E02E971F16319600579887 /* UIImageView+Haneke.swift in Sources */, E4B8887D1D9D48F1002E1A46 /* FileMyDownloadViewController.swift in Sources */, E4C24B6620844F3C00E426B0 /* DLFixedTabbarView.m in Sources */, + B168BDCC24F607520070329F /* QRCodeResultViewController.swift in Sources */, B18398B3249B4154001C6FAA /* IMShowLocationViewController.swift in Sources */, E4C24B3520844F3C00E426B0 /* NSLayoutConstraint+JChat.swift in Sources */, E45DA9121DAF7EBE00E0735D /* SZKCleanCache.m in Sources */, @@ -6494,6 +6521,7 @@ E4C24C0E20844F4400E426B0 /* JCAddFriendViewController.swift in Sources */, E428AF6720AEA5E300D964B9 /* OOAttandanceSettingDataView.swift in Sources */, E40502C520722208009A8D30 /* ImagePickerController.swift in Sources */, + B168BDC924F5FFA80070329F /* ScanRecoObj.swift in Sources */, E4C24B7320844F3C00E426B0 /* JCCEmoticonGroup.swift in Sources */, E4C24BB720844F3C00E426B0 /* JCEmoticonPreviewer.swift in Sources */, 8840A992248CDC9D005970A5 /* OOCalendarEditRemarkViewController.swift in Sources */, diff --git "a/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/QRCodeResultViewController.swift" "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/QRCodeResultViewController.swift" new file mode 100644 index 0000000000000000000000000000000000000000..6050c0a57316a3df8753e3791b5ee7ad21cb5d41 --- /dev/null +++ "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/QRCodeResultViewController.swift" @@ -0,0 +1,165 @@ +// +// QRCodeResultViewController.swift +// O2Platform +// +// Created by FancyLou on 2020/8/26. +// Copyright © 2020 zoneland. All rights reserved. +// + +import UIKit +import Alamofire +import AlamofireImage +import AlamofireObjectMapper +import ObjectMapper +import CocoaLumberjack +import O2OA_Auth_SDK + +class QRCodeResultViewController: UIViewController { + + ///打开扫码结果 + static func openQRResult(result: String, vc: UIViewController) { + let resultVC = QRCodeResultViewController() + resultVC.scanResult = result + vc.navigationController?.pushViewController(resultVC, animated: false) + } + + @IBOutlet weak var loginStackView: UIStackView! + @IBOutlet weak var loginImage: UIImageView! + @IBOutlet weak var loginBtn: UIButton! + @IBOutlet weak var resultLabel: O2CanCopyUILabel! + + //扫码结果 + var scanResult: String? + + //登录url + private var loginURL: String? + + override func viewDidLoad() { + super.viewDidLoad() + if let result = scanResult { + self.showLoading() + //开始解析结果 + //todo 判断url还是其他 + self.resolveResult(result: result) + }else { + self.title = "扫码结果" + self.resultLabel.isHidden = false + self.resultLabel.text = "扫码结果为空" + } + } + + @IBAction func tap2Login(_ sender: UIButton) { + //点击登陆 + if let login = self.loginURL { + self.showLoading() + let account = O2AuthSDK.shared.myInfo() + Alamofire.request(login, method: .post, parameters: nil, encoding: JSONEncoding.default, headers: ["x-token":(account?.token)!]).responseJSON(completionHandler: { (response) in + switch response.result { + case .success(let val): + DispatchQueue.main.async { + DDLogDebug(String(describing:val)) + self.hideLoading() + let alertController = UIAlertController(title: "扫描结果", message: "PC端登录成功", preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .default) { + action in + self.popVC() + } + alertController.addAction(okAction) + self.presentVC(alertController) + } + case .failure(let err): + DispatchQueue.main.async { + self.hideLoading() + DDLogError(err.localizedDescription) + let alertController = UIAlertController(title: "扫描结果", message: "PC端登录失败", preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .destructive) { + action in + self.popVC() + } + alertController.addAction(okAction) + self.presentVC(alertController) + } + } + }) + } + } + + ///解析扫码结果 + private func resolveResult(result: String) { + let url = NSURL(string: result) + //会议签到功能 + var isMeetingCheck = false + let allU = url?.absoluteString + if allU != nil && allU!.contains("/checkin") && allU!.contains("x_meeting_assemble_control") { + isMeetingCheck = true + } + if(isMeetingCheck) {//会议签到 + self.meetingCheck(url: allU!) + }else { + self.hideLoading() + self.title = "扫码登录" + let query = url?.query + let querys = query?.split("&") + var meta = "" + querys?.forEach { (e) in + let name = e.split("=")[0] + if name == "meta" { + meta = e.split("=")[1] + } + } + if meta != "" {//登录O2OA + self.loginURL = AppDelegate.o2Collect.generateURLWithAppContextKey(LoginContext.loginContextKey, query: LoginContext.scanCodeAuthActionQuery, parameter: ["##meta##":meta as AnyObject]) + self.loginStackView.isHidden = false + self.loginBtn.isHidden = false + + }else {//其他扫描结果 +// let alertController = UIAlertController(title: "扫描结果", message: result, preferredStyle: .alert) +// let okAction = UIAlertAction(title: "确定", style: .default) { +// action in +// self.popVC() +// } +// alertController.addAction(okAction) +// self.presentVC(alertController) + self.resultLabel.isHidden = false + self.resultLabel.text = result + } + } + } + + + //会议签到 + private func meetingCheck(url: String) { + self.title = "会议签到" + let account = O2AuthSDK.shared.myInfo() + Alamofire.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: ["x-token":(account?.token)!]).responseJSON(completionHandler: {(response) in + switch response.result { + case .success(let val): + DispatchQueue.main.async { + self.hideLoading() + DDLogDebug(String(describing:val)) + let alertController = UIAlertController(title: "提示", message: "签到成功", preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .default) { + action in + self.popVC() + } + alertController.addAction(okAction) + self.presentVC(alertController) + } + case .failure(let err): + DispatchQueue.main.async { + self.hideLoading() + DDLogError(err.localizedDescription) + let alertController = UIAlertController(title: "提示", message: "签到失败", preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .destructive) { + action in + self.popVC() + } + alertController.addAction(okAction) + self.presentVC(alertController) + } + } + }) + } + + +} diff --git "a/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/QRCodeResultViewController.xib" "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/QRCodeResultViewController.xib" new file mode 100644 index 0000000000000000000000000000000000000000..afab11f55b1ffb097bd848c334689f3b0bdd4e5a --- /dev/null +++ "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/QRCodeResultViewController.xib" @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/ScanHelper.swift" "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/ScanHelper.swift" index f4c0d3ec40862f016b0332957b0d0d14cd94e61c..ba93ec3fdc6f838309c0152fe83459c9a56b0270 100644 --- "a/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/ScanHelper.swift" +++ "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/ScanHelper.swift" @@ -12,30 +12,44 @@ class ScanHelper { static func openScan(vc: UIViewController, callbackResult: ((String)->Void)? = nil) { - LBXPermissions.authorizeCameraWith { (result) in - if result { - if let scanVC = self.initScanViewController(callbackResult: callbackResult) { - vc.pushVC(scanVC) - }else { - vc.gotoApplicationSettings(alertMessage: "是否跳转到手机设置页面开启相机权限?") - } + //新版本 仿新版微信 全屏扫码 + let scanVC = ScanQRViewController() + scanVC.resultBlock = { result in + if let callback = callbackResult { + callback(result) }else { - vc.showError(title: "没有摄像头权限,请先开启!") + //打开O2业务vc 目前两个功能 扫码登录O2和会议签到 + QRCodeResultViewController.openQRResult(result: result, vc: vc) } } + scanVC.modalPresentationStyle = .fullScreen + vc.present(scanVC, animated: true, completion: nil) + + //老版本 +// LBXPermissions.authorizeCameraWith { (result) in +// if result { +// if let scanVC = self.initScanViewController(callbackResult: callbackResult) { +// vc.navigationController?.pushViewController(scanVC, animated: false) +// }else { +// vc.gotoApplicationSettings(alertMessage: "是否跳转到手机设置页面开启相机权限?") +// } +// }else { +// vc.showError(title: "没有摄像头权限,请先开启!") +// } +// } } /// 生成扫码的ViewController 如果没有权限就返回nil static private func initScanViewController(callbackResult: ((String)->Void)? = nil) -> NewScanViewController? { let scanVC = NewScanViewController() var scanStyle = LBXScanViewStyle() - scanStyle.centerUpOffset = 44; - scanStyle.photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle.Inner; - scanStyle.photoframeLineW = 2; - scanStyle.photoframeAngleW = 18; - scanStyle.photoframeAngleH = 18; - scanStyle.isNeedShowRetangle = false; - scanStyle.anmiationStyle = LBXScanViewAnimationStyle.LineMove; + scanStyle.centerUpOffset = 44 + scanStyle.photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle.Inner + scanStyle.photoframeLineW = 2 + scanStyle.photoframeAngleW = 18 + scanStyle.photoframeAngleH = 18 + scanStyle.isNeedShowRetangle = false + scanStyle.anmiationStyle = LBXScanViewAnimationStyle.LineMove //scanStyle.colorAngle = UIColor(red: 0.0/255, green: 200.0/255.0, blue: 20.0/255.0, alpha: 1.0) scanStyle.colorAngle = O2ThemeManager.color(for: "Base.base_color") ?? UIColor.init(hex: "#fb47474") scanStyle.animationImage = UIImage(named: "qrcode_scan_part_net.png") diff --git "a/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/ScanQRViewController.swift" "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/ScanQRViewController.swift" new file mode 100644 index 0000000000000000000000000000000000000000..b27919876fe2d2b30d4da1a1150c252bd7fa814b --- /dev/null +++ "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/c/ScanQRViewController.swift" @@ -0,0 +1,451 @@ +// +// ScanQRViewController.swift +// ScanQRCodeLikeWeChat +// +// Created by FancyLou on 2020/8/25. +// Copyright © 2020 muliba. All rights reserved. +// + +import UIKit +import AVFoundation +import Photos + +typealias ScanResultBlock = (String) -> Void + +///仿微信 扫码功能 全屏版本 +class ScanQRViewController: UIViewController { + + // MARK: - 返回结果 + var resultBlock: ScanResultBlock? + + // MARK: - private 参数 + ///摄像头输出 + private var output: AVCaptureMetadataOutput? + private var session: AVCaptureSession? + private var videoDataOutput: AVCaptureVideoDataOutput? + private var videoPreviewLayer: AVCaptureVideoPreviewLayer? + ///识别结果绘制图集 + private var reconizationViews: [ScanRecoObj] = [] + ///定时器 + private var animationTimeInterval: TimeInterval = 0.02 + private var timer: Timer? + + private var hasEntered = false + + private var scanBorderW: CGFloat = 0 + private var scanBorderX: CGFloat = 0 + private var scanBorderY: CGFloat = 0 + + // MARK: - UI + private var backBtn: UIButton! + private var scanningline: UIImageView? + + + // MARK: - system lifecycle func + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + self.addTimer() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + self.removeTimer() + } + + override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = UIColor.black + self.scanBorderW = 0.9 * self.view.frame.size.width + self.scanBorderX = 0.5 * (1 - 0.9) * self.view.frame.size.width + self.scanBorderY = 0.25 * self.view.frame.size.height + self.initUI() + + self.prepareScanQRCodeWithResult { (_) in + self.scanSessionStart() + } + } + + + // MARK: - private func + private func initUI() { + //返回按钮 + self.backBtn = UIButton(type: .custom) + self.backBtn.frame = CGRect(x: 15, y: 44, width: 44, height: 44) + self.backBtn.setImage(UIImage(named: "scan_back"), for: .normal) + self.backBtn.addTarget(self, action: #selector(closeSelf), for: .touchUpInside) + self.view.addSubview(self.backBtn) + //选择相册按钮 + let photosBtn = UIButton(type: .custom) + photosBtn.frame = CGRect(x: (self.view.bounds.size.width - 44) / 2, y: self.view.bounds.size.height - 44 - 34 - 40, width: 44, height: 44) + photosBtn.setImage(UIImage(named: "photos_icon"), for: .normal) + photosBtn.addTarget(self, action: #selector(photosAction), for: .touchUpInside) + self.view.addSubview(photosBtn) + + } + + ///开始扫描 + private func prepareScanQRCodeWithResult(block: @escaping (String) -> Void) { + //判断设备权限 + guard let _ = AVCaptureDevice.default(for: .video) else { + print("没有检测到摄像头,请在真机上测试!") + return + } + let authStatus = AVCaptureDevice.authorizationStatus(for: .video) + if authStatus == .restricted { + let alertC = UIAlertController(title: "提示", message: "无法访问相机,请检查权限", preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .default, handler: { action in + + }) + alertC.addAction(okAction) + DispatchQueue.main.async { + self.present(alertC, animated: true, completion: nil) + } + } else if authStatus == .denied { //拒绝相机访问权限 + var name: String? = "" + if let dic = Bundle.main.infoDictionary { + name = dic["CFBundleDisplayName"] as? String + if name == nil { + name = dic["CFBundleName"] as? String + } + } + let message = "[前往:设置 - 隐私 - 相机 - \(name ?? "")] 允许应用访问" + let alertC = UIAlertController(title: "提示", message: message, preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .default, handler: { action in + + }) + alertC.addAction(okAction) + DispatchQueue.main.async { + self.present(alertC, animated: true, completion: nil) + } + } else if authStatus == .authorized { //已经允许 + DispatchQueue.main.async { block("") } + } else if authStatus == .notDetermined { //初始未选择 + AVCaptureDevice.requestAccess(for: .video) { (granted) in + if granted { + DispatchQueue.main.async { block("") } + } else { + //todo 拒绝访问。。。。 + } + } + } + } + + ///准备摄像头,正式开始扫描 + private func scanSessionStart() { + // 获取摄像头 + guard let device = AVCaptureDevice.default(for: .video) else { + print("没有检测到摄像头,请在真机上测试!") + return + } + var input: AVCaptureDeviceInput? + do { + input = try AVCaptureDeviceInput(device: device) + } catch { + print("err: \(error.localizedDescription)") + } + guard let deviceInput = input else { + print("输入流创建失败") + return + } + //输出流 + self.output = AVCaptureMetadataOutput() + self.output?.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + // 注:微信二维码的扫描范围是整个屏幕,这里并没有做处理(可不用设置); + // 如需限制扫描框范围,如下 + // if !cropRect.equalTo(CGRect.zero) +// { + //启动相机后,直接修改该参数无效 +// output.rectOfInterest = cropRect +// } + //创建session + self.session = AVCaptureSession() + self.session?.sessionPreset = .high + //添加元数据输出流到会话对象 + self.session?.addOutput(self.output!) + //创建摄像数据输出流并将其添加到会话对象上, --> 用于识别光线强弱 + self.videoDataOutput = AVCaptureVideoDataOutput() + self.videoDataOutput?.setSampleBufferDelegate(self, queue: DispatchQueue.main) + self.session?.addOutput(self.videoDataOutput!) + //添加摄像设备输入流到会话对象 + self.session?.addInput(deviceInput) + //识别类型 + self.output?.metadataObjectTypes = [.qr, .ean13, .ean8, .code128] + self.videoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session!) + self.videoPreviewLayer?.videoGravity = .resizeAspectFill + self.videoPreviewLayer?.frame = self.view.bounds + self.view.layer.insertSublayer(self.videoPreviewLayer!, at: 0) + + self.session?.startRunning() + + } + + ///添加定时器 + private func addTimer() { + if self.session != nil && self.session?.isRunning != true && hasEntered { + self.session?.startRunning() + } + hasEntered = true + //扫描的时候绿色线条 + self.scanningline = UIImageView(image: UIImage(named: "QRCodeScanLine")) + self.view.addSubview(self.scanningline!) + + self.scanningline?.frame = CGRect(x: self.scanBorderX, y: self.scanBorderY, width: self.scanBorderW, height: 12) + self.scanningline?.isHidden = true + self.timer = Timer.scheduledTimer(timeInterval: self.animationTimeInterval, target: self, selector: #selector(beginRefreshUI), userInfo: nil, repeats: true) + self.timer?.fire() + } + + ///移除定时器 + private func removeTimer() { + self.timer?.invalidate() + self.timer = nil + self.scanningline?.removeFromSuperview() + self.scanningline = nil + if self.session?.isRunning == true { + self.session?.stopRunning() + } + } + + //开始动画 + @objc private func beginRefreshUI() { + //防止还没开始执行定时器就扫描到码,导致扫描动画一直进行 + if self.session?.isRunning != true { + self.removeTimer() + return + } + self.scanningline?.isHidden = false + var frame = self.scanningline?.frame + if self.scanningline?.frame.origin.y ?? self.scanBorderY >= self.scanBorderY { + let maxY = self.view.frame.size.height - self.scanBorderY + if self.scanningline?.frame.origin.y ?? self.scanBorderY >= maxY - 10 { + frame?.origin.y = self.scanBorderY + self.scanningline?.frame = frame! + } else { + UIView.animate(withDuration: self.animationTimeInterval) { + frame?.origin.y += 2 + self.scanningline?.frame = frame! + } + } + } + + } + + + ///返回结果 关闭页面 + private func processWithResult(result: String) { + self.resultBlock?(result) + self.dismiss(animated: true, completion: nil) + } + + ///关闭当前扫描页面 + @objc private func closeSelf() { + if self.session?.isRunning == true { + self.session?.stopRunning() + } + self.dismiss(animated: true, completion: nil) + } + + //取消扫描出来的结果 继续扫描 + @objc private func cancelResult() { + if self.session?.isRunning == true { + self.session?.stopRunning() + } + self.reconizationViews.forEach { (obj) in + obj.codeView.removeFromSuperview() + } + self.reconizationViews.removeAll() + if self.session?.isRunning == false { + self.addTimer() + } + self.backBtn.isHidden = false + } + + ///从相册选择照片 + @objc private func photosAction() { + let authStatus = PHPhotoLibrary.authorizationStatus() + if authStatus == .restricted { + let alertC = UIAlertController(title: "提示", message: "无法访问相册,请检查权限", preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .default, handler: { action in + + }) + alertC.addAction(okAction) + DispatchQueue.main.async { + self.present(alertC, animated: true, completion: nil) + } + } else if authStatus == .denied { //拒绝相机访问权限 + var name: String? = "" + if let dic = Bundle.main.infoDictionary { + name = dic["CFBundleDisplayName"] as? String + if name == nil { + name = dic["CFBundleName"] as? String + } + } + let message = "[前往:设置 - 隐私 - 照片 - \(name ?? "")] 允许应用访问" + let alertC = UIAlertController(title: "提示", message: message, preferredStyle: .alert) + let okAction = UIAlertAction(title: "确定", style: .default, handler: { action in + + }) + alertC.addAction(okAction) + DispatchQueue.main.async { + self.present(alertC, animated: true, completion: nil) + } + } else if authStatus == .authorized { //已经允许 + DispatchQueue.main.async { self.enterPhotos() } + } else if authStatus == .notDetermined { //初始未选择 + PHPhotoLibrary.requestAuthorization { (status) in + if status == .authorized { + DispatchQueue.main.async { self.enterPhotos() } + } else { + //todo 拒绝访问。。。。 + } + } + } + } + + @objc private func clickCurrentCode(btn: UIButton) { + let obj = self.reconizationViews[btn.tag] + self.processWithResult(result: obj.codeString) + } + + + ///打开相册 + private func enterPhotos() { + let imagePicker = UIImagePickerController() + imagePicker.sourceType = .photoLibrary + imagePicker.delegate = self + imagePicker.modalPresentationStyle = .fullScreen + self.present(imagePicker, animated: true, completion: nil) + } + + ///生成遮障层 + private func getMaskView(showTips: Bool) -> UIView { + let maskView = UIView(frame: self.view.bounds) + maskView.backgroundColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.6) + if showTips { + let cancel = UIButton(type: .custom) + cancel.frame = CGRect(x: 15, y: 44, width: 50, height: 44) + cancel.setTitle("取消", for: .normal) + cancel.setTitleColor(.white, for: .normal) + cancel.addTarget(self, action: #selector(cancelResult), for: .touchUpInside) + maskView.addSubview(cancel) + let tips = UILabel(frame: CGRect(x: 20, y: self.view.bounds.size.height - 64 - 50, width: self.view.bounds.size.width - 40, height: 50)) + tips.text = "轻触小蓝点,选中识别二维码" + tips.font = UIFont.boldSystemFont(ofSize: 14) + tips.textAlignment = .center + tips.textColor = UIColor(displayP3Red: 1, green: 1, blue: 1, alpha: 0.6) + maskView.addSubview(tips) + } + return maskView + } + + //动画 + private func getAnimation() -> CAKeyframeAnimation { + let ani = CAKeyframeAnimation(keyPath: "transform.scale") + ani.duration = 2.8 + ani.isRemovedOnCompletion = false + ani.repeatCount = HUGE + ani.fillMode = .forwards + ani.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + let v1 = NSNumber(value: 1.0) + let v2 = NSNumber(value: 0.8) + ani.values = [v1, v2, v1, v2, v1, v1, v1, v1] + return ani + } + + ///生成扫码结果提示按钮 + private func getScanResultCode(bounds: CGRect, icon: Bool) -> UIButton { + let btn = UIButton(type: .custom) + btn.frame = bounds + btn.backgroundColor = UIColor(displayP3Red: 54 / 255.0, green: 85 / 255.0, blue: 230 / 255.0, alpha: 1) + btn.addTarget(self, action: #selector(clickCurrentCode(btn:)), for: .touchUpInside) + if icon { + btn.setImage(UIImage(named: "right_arrow_icon"), for: .normal) + btn.layer.add(self.getAnimation(), forKey: "scale-layer") + } + var rect = btn.frame + let center = btn.center + rect.size.width = 40 + rect.size.height = 40 + btn.frame = rect + btn.center = center + btn.layer.cornerRadius = 20 + btn.clipsToBounds = true + btn.layer.borderColor = UIColor.white.cgColor + btn.layer.borderWidth = 3 + return btn + } +} + +// MARK: - 摄像头识别代理 +extension ScanQRViewController: AVCaptureMetadataOutputObjectsDelegate, AVCaptureVideoDataOutputSampleBufferDelegate { + func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + if metadataObjects.count > 0 { + self.removeTimer() + if #available(iOS 10, *) { + let impact = UIImpactFeedbackGenerator(style: .light) + impact.impactOccurred() + } + let maskView = self.getMaskView(showTips: metadataObjects.count > 1) + maskView.alpha = 0 + self.view.addSubview(maskView) + UIView.animate(withDuration: 0.6) { + maskView.alpha = 1 + } + let obj = ScanRecoObj(codeView: maskView, codeString: "") + self.reconizationViews.append(obj) + + var indx = 0 + for meta in metadataObjects { + if meta.isKind(of: AVMetadataMachineReadableCodeObject.self) { + let code = self.videoPreviewLayer?.transformedMetadataObject(for: meta) as! AVMetadataMachineReadableCodeObject + let codeBtn = self.getScanResultCode(bounds: code.bounds, icon: metadataObjects.count > 1) + codeBtn.tag = indx + 1 + self.view.addSubview(codeBtn) + let codeObj = ScanRecoObj(codeView: codeBtn, codeString: code.stringValue ?? "") + self.reconizationViews.append(codeObj) + } + indx += 1 + } + self.backBtn.isHidden = true + if metadataObjects.count == 1 { //只有一个直接返回结果 不需要点击 + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { + self.processWithResult(result: self.reconizationViews[1].codeString) + } + } + } + } +} + +// MARK: - 相册选择器代理 +extension ScanQRViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate { + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + self.dismiss(animated: true, completion: nil) + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { + return + } + let context = CIContext(options: nil) + let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: context, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]) + guard let ciImage = CIImage(image: image) else { + return + } + let features = detector?.features(in: ciImage) + if features?.count == 0 { + DispatchQueue.main.async { + self.dismiss(animated: true) { + self.processWithResult(result: "") + } + } + } else { + if let feature = features?.first as? CIQRCodeFeature, let result = feature.messageString { + DispatchQueue.main.async { + self.dismiss(animated: true) { + self.processWithResult(result: result) + } + } + } + } + } +} diff --git "a/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/m/ScanRecoObj.swift" "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/m/ScanRecoObj.swift" new file mode 100644 index 0000000000000000000000000000000000000000..a774945ea4a4433ccd91cd1a0df615338d481369 --- /dev/null +++ "b/o2ios/O2Platform/App/Scan-\344\272\214\347\273\264\347\240\201\346\211\253\347\240\201/m/ScanRecoObj.swift" @@ -0,0 +1,15 @@ +// +// ScanRecoObj.swift +// ScanQRCodeLikeWeChat +// +// Created by FancyLou on 2020/8/25. +// Copyright © 2020 muliba. All rights reserved. +// + +import Foundation +import UIKit + +struct ScanRecoObj { + var codeView: UIView + var codeString: String +} diff --git a/o2ios/O2Platform/Assets.xcassets/login/scan_login.imageset/Contents.json b/o2ios/O2Platform/Assets.xcassets/login/scan_login.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..bc39cc939b14e3fef16bdf4c8180f59ffc79d232 --- /dev/null +++ b/o2ios/O2Platform/Assets.xcassets/login/scan_login.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "scan_login_content_2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/o2ios/O2Platform/Assets.xcassets/login/scan_login.imageset/scan_login_content_2.png b/o2ios/O2Platform/Assets.xcassets/login/scan_login.imageset/scan_login_content_2.png new file mode 100644 index 0000000000000000000000000000000000000000..e7579f8c07195bf41042ef8a2af238cd40db5a56 Binary files /dev/null and b/o2ios/O2Platform/Assets.xcassets/login/scan_login.imageset/scan_login_content_2.png differ diff --git a/o2ios/O2Platform/Assets.xcassets/whipster/Contents.json b/o2ios/O2Platform/Assets.xcassets/whipster/Contents.json deleted file mode 100644 index da4a164c918651cdd1e11dca5cc62c333f097601..0000000000000000000000000000000000000000 --- a/o2ios/O2Platform/Assets.xcassets/whipster/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/QRCodeScanLine.imageset/Contents.json" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/QRCodeScanLine.imageset/Contents.json" new file mode 100644 index 0000000000000000000000000000000000000000..62b49b7d3a77352a623ded98381a1dfba59455c6 --- /dev/null +++ "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/QRCodeScanLine.imageset/Contents.json" @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "QRCodeScanLine@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/QRCodeScanLine.imageset/QRCodeScanLine@2x.png" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/QRCodeScanLine.imageset/QRCodeScanLine@2x.png" new file mode 100644 index 0000000000000000000000000000000000000000..c301db7acbd146f26581ae795b0e5a9ca18361cf Binary files /dev/null and "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/QRCodeScanLine.imageset/QRCodeScanLine@2x.png" differ diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/photos_icon.imageset/Contents.json" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/photos_icon.imageset/Contents.json" new file mode 100644 index 0000000000000000000000000000000000000000..74cbfd948f0bd51d4b8525f446be952f43ff5d9a --- /dev/null +++ "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/photos_icon.imageset/Contents.json" @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "photos_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/photos_icon.imageset/photos_icon@2x.png" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/photos_icon.imageset/photos_icon@2x.png" new file mode 100644 index 0000000000000000000000000000000000000000..e6e71018822ac584c5b45187965bb3160370359e Binary files /dev/null and "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/photos_icon.imageset/photos_icon@2x.png" differ diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/right_arrow_icon.imageset/Contents.json" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/right_arrow_icon.imageset/Contents.json" new file mode 100644 index 0000000000000000000000000000000000000000..26cd266145bd46f791e9be649e8a579b22d397c2 --- /dev/null +++ "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/right_arrow_icon.imageset/Contents.json" @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "right@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/right_arrow_icon.imageset/right@2x.png" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/right_arrow_icon.imageset/right@2x.png" new file mode 100644 index 0000000000000000000000000000000000000000..4a6c1f6657a8d438fbaf142b66e185ced8c4eaaa Binary files /dev/null and "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/right_arrow_icon.imageset/right@2x.png" differ diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/scan_back.imageset/Contents.json" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/scan_back.imageset/Contents.json" new file mode 100644 index 0000000000000000000000000000000000000000..5e40bf283a01fac804fd2e2a6d18b5aafda60575 --- /dev/null +++ "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/scan_back.imageset/Contents.json" @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "scan_back@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/scan_back.imageset/scan_back@2x.png" "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/scan_back.imageset/scan_back@2x.png" new file mode 100644 index 0000000000000000000000000000000000000000..c8905fd53638a3f08bb4d1ce2ea6255c6d1e7159 Binary files /dev/null and "b/o2ios/O2Platform/Assets.xcassets/\351\246\226\351\241\265/scan_back.imageset/scan_back@2x.png" differ diff --git a/o2ios/O2Platform/Framework/scan/LBXScanViewController.swift b/o2ios/O2Platform/Framework/scan/LBXScanViewController.swift index 63e170b13b30ea6db9e4542d7e4388de953ac9ad..6f2c69fcd2a66aecac47b8b821443bdb7ffdcea8 100755 --- a/o2ios/O2Platform/Framework/scan/LBXScanViewController.swift +++ b/o2ios/O2Platform/Framework/scan/LBXScanViewController.swift @@ -50,10 +50,8 @@ open class LBXScanViewController: UIViewController, UIImagePickerControllerDeleg // Do any additional setup after loading the view. // [self.view addSubview:_qRScanView]; - print("viewDidLoad...........") self.view.backgroundColor = UIColor.black self.edgesForExtendedLayout = UIRectEdge(rawValue: 0) - print("viewDidLoad......end.....") drawScanView() @@ -71,22 +69,18 @@ open class LBXScanViewController: UIViewController, UIImagePickerControllerDeleg override open func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - print("viewWillAppear...........") } override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - print("viewDidAppear...........") } @objc open func startScan() { - print("startScan. .................") if (scanObj == nil) { - print("scanObj oooo..................") var cropRect = CGRect.zero if isOpenInterestRect { @@ -96,7 +90,7 @@ open class LBXScanViewController: UIViewController, UIImagePickerControllerDeleg //指定识别几种码 if arrayCodeType == nil { - arrayCodeType = [AVMetadataObject.ObjectType.qr as NSString ,AVMetadataObject.ObjectType.ean13 as NSString ,AVMetadataObject.ObjectType.code128 as NSString] as [AVMetadataObject.ObjectType] + arrayCodeType = [AVMetadataObject.ObjectType.qr as NSString] as [AVMetadataObject.ObjectType] } scanObj = LBXScanWrapper(videoPreView: self.view,objType:arrayCodeType!, isCaptureImg: isNeedCodeImage,cropRect:cropRect, success: { [weak self] (arrayResult) -> Void in @@ -113,20 +107,16 @@ open class LBXScanViewController: UIViewController, UIImagePickerControllerDeleg //结束相机等待提示 qRScanView?.deviceStopReadying() - print("qRScanView deviceStopReadying..................") //开始扫描动画 qRScanView?.startScanAnimation() - print("qRScanView startScanAnimation..................") //相机运行 scanObj?.start() - print("scanObj start..................") } open func drawScanView() { if qRScanView == nil { - print("drawScanView..................") qRScanView = LBXScanView(frame: self.view.frame,vstyle:scanStyle! ) self.view.addSubview(qRScanView!) delegate?.drawwed() diff --git a/o2ios/O2Platform/Framework/scan/LBXScanWrapper.swift b/o2ios/O2Platform/Framework/scan/LBXScanWrapper.swift index c0ae46f84640003d718f40e6822162e5ffefd84c..af926649860a659a2e325028aabb17a63d105133 100755 --- a/o2ios/O2Platform/Framework/scan/LBXScanWrapper.swift +++ b/o2ios/O2Platform/Framework/scan/LBXScanWrapper.swift @@ -9,19 +9,19 @@ import UIKit import AVFoundation -public struct LBXScanResult { - +public struct LBXScanResult { + //码内容 - public var strScanned:String? = "" + public var strScanned: String? = "" //扫描图像 - public var imgScanned:UIImage? + public var imgScanned: UIImage? //码的类型 - public var strBarCodeType:String? = "" - + public var strBarCodeType: String? = "" + //码在图像中的位置 - public var arrayCorner:[AnyObject]? - - public init(str:String?,img:UIImage?,barCodeType:String?,corner:[AnyObject]?) + public var arrayCorner: [AnyObject]? + + public init(str: String?, img: UIImage?, barCodeType: String?, corner: [AnyObject]?) { self.strScanned = str self.imgScanned = img @@ -32,28 +32,34 @@ public struct LBXScanResult { -open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { - +open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate { + let device = AVCaptureDevice.default(for: AVMediaType.video) - var input:AVCaptureDeviceInput? - var output:AVCaptureMetadataOutput - + var input: AVCaptureDeviceInput? + var output: AVCaptureMetadataOutput + let session = AVCaptureSession() - var previewLayer:AVCaptureVideoPreviewLayer? - var stillImageOutput:AVCaptureStillImageOutput? + var previewLayer: AVCaptureVideoPreviewLayer? + var stillImageOutput: AVCaptureStillImageOutput? + + var videoPreView: UIView? - //存储返回结果 - var arrayResult:[LBXScanResult] = []; + var scaleSize:CGFloat = 1 + //存储返回结果 + var arrayResult: [LBXScanResult] = []; + //扫码结果返回block - var successBlock:([LBXScanResult]) -> Void - + var successBlock: ([LBXScanResult]) -> Void + //是否需要拍照 - var isNeedCaptureImage:Bool - + var isNeedCaptureImage: Bool + //当前扫码结果是否处理 - var isNeedScanResult:Bool = true + var isNeedScanResult: Bool = true + + /** 初始化设备 - parameter videoPreView: 视频显示UIView @@ -63,90 +69,92 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { - parameter success: 返回识别信息 - returns: */ - init( videoPreView:UIView,objType:[AVMetadataObject.ObjectType] = [(AVMetadataObject.ObjectType.qr as NSString) as AVMetadataObject.ObjectType],isCaptureImg:Bool,cropRect:CGRect=CGRect.zero,success:@escaping ( ([LBXScanResult]) -> Void) ) + init(videoPreView: UIView, objType: [AVMetadataObject.ObjectType] = [(AVMetadataObject.ObjectType.qr as NSString) as AVMetadataObject.ObjectType], isCaptureImg: Bool, cropRect: CGRect = CGRect.zero, success: @escaping (([LBXScanResult]) -> Void)) { - do{ + self.videoPreView = videoPreView + + do { input = try AVCaptureDeviceInput(device: device!) } catch let error as NSError { print("AVCaptureDeviceInput(): \(error)") } - + successBlock = success - + // Output output = AVCaptureMetadataOutput() - + isNeedCaptureImage = isCaptureImg - + stillImageOutput = AVCaptureStillImageOutput(); - + super.init() - + if device == nil || input == nil { return } - + if session.canAddInput(input!) { session.addInput(input!) } - + if session.canAddOutput(output) { session.addOutput(output) } - + if session.canAddOutput(stillImageOutput!) { session.addOutput(stillImageOutput!) } - - let outputSettings:Dictionary = [AVVideoCodecJPEG:AVVideoCodecKey] + + let outputSettings: Dictionary = [AVVideoCodecJPEG: AVVideoCodecKey] stillImageOutput?.outputSettings = outputSettings - + session.sessionPreset = AVCaptureSession.Preset.high - + //参数设置 output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) - + output.metadataObjectTypes = objType - - // output.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code] - + if !cropRect.equalTo(CGRect.zero) { //启动相机后,直接修改该参数无效 output.rectOfInterest = cropRect } - + previewLayer = AVCaptureVideoPreviewLayer(session: session) previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill - - var frame:CGRect = videoPreView.frame + + var frame: CGRect = videoPreView.frame frame.origin = CGPoint.zero previewLayer?.frame = frame + + videoPreView.layer.insertSublayer(previewLayer!, at: 0) - videoPreView.layer .insertSublayer(previewLayer!, at: 0) - - if ( device!.isFocusPointOfInterestSupported && device!.isFocusModeSupported(AVCaptureDevice.FocusMode.continuousAutoFocus) ) - { - do + do { + try input?.device.lockForConfiguration() + + if device?.isWhiteBalanceModeSupported(.autoWhiteBalance) == true { + input?.device.whiteBalanceMode = .autoWhiteBalance + } + + if (device!.isFocusPointOfInterestSupported && device!.isFocusModeSupported(AVCaptureDevice.FocusMode.continuousAutoFocus)) { - try input?.device.lockForConfiguration() - input?.device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus - - input?.device.unlockForConfiguration() - } - catch let error as NSError { - print("device.lockForConfiguration(): \(error)") - } + + input?.device.unlockForConfiguration() + } catch let error as NSError { + print("device.lockForConfiguration(): \(error)") } + } - + public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { captureOutput(output, didOutputMetadataObjects: metadataObjects, from: connection) } - + func start() { if !session.isRunning @@ -164,39 +172,92 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { } } - open func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { - +// func setVideoScale(scale: CGFloat) { +// print("调整镜头................") +// do { +// try input?.device.lockForConfiguration() +// let videoConnection = self.connectionWithMediaType(mediaType: .video, connections: self.stillImageOutput?.connections ?? []) +// let maxScaleAndCropFactor = self.stillImageOutput?.connection(with: .video)?.videoMaxScaleAndCropFactor ?? 0 / 16 +// var newScale = scale +// if newScale > maxScaleAndCropFactor { +// newScale = maxScaleAndCropFactor +// } +// print("scale.....\(newScale)") +// let videoFactor = videoConnection?.videoScaleAndCropFactor ?? 1 +// let zoom = newScale / videoFactor +// videoConnection?.videoScaleAndCropFactor = newScale +// input?.device.unlockForConfiguration() +// print("zoom.......\(zoom)") +// if let transform = self.videoPreView?.transform { +// self.videoPreView?.transform = transform.scaledBy(x: zoom, y: zoom) +// } +// +// } catch let error as NSError { +// print("device.lockForConfiguration(): \(error)") +// } +// } +// +// func changeVideoScale(obj: AVMetadataMachineReadableCodeObject?) { +// if let o = obj { +// let arr = o.corners +// if arr.count >= 3 { +// let point = arr[1] +// let point2 = arr[2] +// let scale = 150 / (point2.x - point.x) //当二维码图片宽小于150,进行放大 +// print("scale.......\(scale)") +// if scale > 1 { +// for item in stride(from: 1, to: scale, by: 0.01) { +// self.setVideoScale(scale: item) +// } +// } +// } +// } +// } + + open func captureOutput(_ captureOutput: AVCaptureOutput, didOutputMetadataObjects metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + if !isNeedScanResult { //上一帧处理中 return } - + isNeedScanResult = false - + arrayResult.removeAll() + +// print(metadataObjects) + +// if metadataObjects.count < 1 { +// if self.scaleSize == 1 { +// self.scaleSize = 2 +// }else { +// self.scaleSize = 1 +// } +// self.setVideoScale(scale: self.scaleSize) +// } //识别扫码类型 - for current:Any in metadataObjects + for current in metadataObjects { - if (current as AnyObject).isKind(of: AVMetadataMachineReadableCodeObject.self) + if (current).isKind(of: AVMetadataMachineReadableCodeObject.self) { let code = current as! AVMetadataMachineReadableCodeObject - + //码类型 let codeType = code.type // print("code type:%@",codeType) //码内容 let codeContent = code.stringValue // print("code string:%@",codeContent) - + //4个字典,分别 左上角-右上角-右下角-左下角的 坐标百分百,可以使用这个比例抠出码的图像 // let arrayRatio = code.corners - + arrayResult.append(LBXScanResult(str: codeContent, img: UIImage(), barCodeType: codeType.rawValue, corner: code.corners as [AnyObject]?)) } } - + if arrayResult.count > 0 { if isNeedCaptureImage @@ -208,52 +269,52 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { stop() successBlock(arrayResult) } - + } else { isNeedScanResult = true } - + } - + //MARK: ----拍照 open func captureImage() { - let stillImageConnection:AVCaptureConnection? = connectionWithMediaType(mediaType: AVMediaType.video as AVMediaType, connections: (stillImageOutput?.connections)! as [AnyObject]) - - + let stillImageConnection: AVCaptureConnection? = connectionWithMediaType(mediaType: AVMediaType.video as AVMediaType, connections: (stillImageOutput?.connections)! as [AnyObject]) + + stillImageOutput?.captureStillImageAsynchronously(from: stillImageConnection!, completionHandler: { (imageDataSampleBuffer, error) -> Void in - + self.stop() if imageDataSampleBuffer != nil { let imageData: Data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer!)! - let scanImg:UIImage? = UIImage(data: imageData) - - - for idx in 0...self.arrayResult.count-1 + let scanImg: UIImage? = UIImage(data: imageData) + + + for idx in 0...self.arrayResult.count - 1 { self.arrayResult[idx].imgScanned = scanImg } } - + self.successBlock(self.arrayResult) - + }) } - - open func connectionWithMediaType(mediaType:AVMediaType, connections:[AnyObject]) -> AVCaptureConnection? + + open func connectionWithMediaType(mediaType: AVMediaType, connections: [AnyObject]) -> AVCaptureConnection? { - for connection:AnyObject in connections + for connection: AnyObject in connections { - let connectionTmp:AVCaptureConnection = connection as! AVCaptureConnection - - for port:Any in connectionTmp.inputPorts + let connectionTmp: AVCaptureConnection = connection as! AVCaptureConnection + + for port: Any in connectionTmp.inputPorts { if (port as AnyObject).isKind(of: AVCaptureInput.Port.self) { - let portTmp:AVCaptureInput.Port = port as! AVCaptureInput.Port + let portTmp: AVCaptureInput.Port = port as! AVCaptureInput.Port if portTmp.mediaType == mediaType { return connectionTmp @@ -263,58 +324,58 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { } return nil } - - + + //MARK:切换识别区域 - open func changeScanRect(cropRect:CGRect) + open func changeScanRect(cropRect: CGRect) { //待测试,不知道是否有效 stop() output.rectOfInterest = cropRect start() } - + //MARK: 切换识别码的类型 - open func changeScanType(objType:[AVMetadataObject.ObjectType]) + open func changeScanType(objType: [AVMetadataObject.ObjectType]) { //待测试中途修改是否有效 output.metadataObjectTypes = objType } - - open func isGetFlash()->Bool + + open func isGetFlash() -> Bool { - if (device != nil && device!.hasFlash && device!.hasTorch) + if (device != nil && device!.hasFlash && device!.hasTorch) { return true } return false } - + /** 打开或关闭闪关灯 - parameter torch: true:打开闪关灯 false:关闭闪光灯 */ - open func setTorch(torch:Bool) + open func setTorch(torch: Bool) { if isGetFlash() { do { try input?.device.lockForConfiguration() - + input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off - + input?.device.unlockForConfiguration() } catch let error as NSError { print("device.lockForConfiguration(): \(error)") - + } } - + } - - + + /** ------闪光灯打开或关闭 */ @@ -325,9 +386,9 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { do { try input?.device.lockForConfiguration() - + var torch = false - + if input?.device.torchMode == AVCaptureDevice.TorchMode.on { torch = false @@ -336,52 +397,52 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { { torch = true } - + input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off - + input?.device.unlockForConfiguration() } catch let error as NSError { print("device.lockForConfiguration(): \(error)") - + } } } - + //MARK: ------获取系统默认支持的码的类型 - static func defaultMetaDataObjectTypes() ->[AVMetadataObject.ObjectType] + static func defaultMetaDataObjectTypes() -> [AVMetadataObject.ObjectType] { var types = [AVMetadataObject.ObjectType.qr, - AVMetadataObject.ObjectType.upce, - AVMetadataObject.ObjectType.code39, - AVMetadataObject.ObjectType.code39Mod43, - AVMetadataObject.ObjectType.ean13, - AVMetadataObject.ObjectType.ean8, - AVMetadataObject.ObjectType.code93, - AVMetadataObject.ObjectType.code128, - AVMetadataObject.ObjectType.pdf417, - AVMetadataObject.ObjectType.aztec - ] + AVMetadataObject.ObjectType.upce, + AVMetadataObject.ObjectType.code39, + AVMetadataObject.ObjectType.code39Mod43, + AVMetadataObject.ObjectType.ean13, + AVMetadataObject.ObjectType.ean8, + AVMetadataObject.ObjectType.code93, + AVMetadataObject.ObjectType.code128, + AVMetadataObject.ObjectType.pdf417, + AVMetadataObject.ObjectType.aztec + ] //if #available(iOS 8.0, *) - + types.append(AVMetadataObject.ObjectType.interleaved2of5) types.append(AVMetadataObject.ObjectType.itf14) types.append(AVMetadataObject.ObjectType.dataMatrix) return types as [AVMetadataObject.ObjectType] } - - - static func isSysIos8Later()->Bool + + + static func isSysIos8Later() -> Bool { // return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_0 - + if #available(iOS 8, *) { return true; } return false } - + /** 识别二维码码图像 @@ -389,74 +450,74 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { - returns: 返回识别结果 */ - static public func recognizeQRImage(image:UIImage) ->[LBXScanResult] + static public func recognizeQRImage(image: UIImage) -> [LBXScanResult] { - var returnResult:[LBXScanResult]=[] - + var returnResult: [LBXScanResult] = [] + if LBXScanWrapper.isSysIos8Later() { //if #available(iOS 8.0, *) - - let detector:CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])! - + + let detector: CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])! + let img = CIImage(cgImage: (image.cgImage)!) - - let features:[CIFeature]? = detector.features(in: img, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh]) - - if( features != nil && (features?.count)! > 0) + + let features: [CIFeature]? = detector.features(in: img, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]) + + if(features != nil && (features?.count)! > 0) { let feature = features![0] - + if feature.isKind(of: CIQRCodeFeature.self) { - let featureTmp:CIQRCodeFeature = feature as! CIQRCodeFeature - + let featureTmp: CIQRCodeFeature = feature as! CIQRCodeFeature + let scanResult = featureTmp.messageString - - - let result = LBXScanResult(str: scanResult, img: image, barCodeType: AVMetadataObject.ObjectType.qr.rawValue,corner: nil) - + + + let result = LBXScanResult(str: scanResult, img: image, barCodeType: AVMetadataObject.ObjectType.qr.rawValue, corner: nil) + returnResult.append(result) } } - + } - + return returnResult } - - + + //MARK: -- - 生成二维码,背景色及二维码颜色设置 - static public func createCode( codeType:String, codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage? + static public func createCode(codeType: String, codeString: String, size: CGSize, qrColor: UIColor, bkColor: UIColor) -> UIImage? { //if #available(iOS 8.0, *) - + let stringData = codeString.data(using: String.Encoding.utf8) - - + + //系统自带能生成的码 // CIAztecCodeGenerator // CICode128BarcodeGenerator // CIPDF417BarcodeGenerator // CIQRCodeGenerator let qrFilter = CIFilter(name: codeType) - - + + qrFilter?.setValue(stringData, forKey: "inputMessage") - + qrFilter?.setValue("H", forKey: "inputCorrectionLevel") - - + + //上色 - let colorFilter = CIFilter(name: "CIFalseColor", parameters: ["inputImage":qrFilter!.outputImage!,"inputColor0":CIColor(cgColor: qrColor.cgColor),"inputColor1":CIColor(cgColor: bkColor.cgColor)]) - - + let colorFilter = CIFilter(name: "CIFalseColor", parameters: ["inputImage": qrFilter!.outputImage!, "inputColor0": CIColor(cgColor: qrColor.cgColor), "inputColor1": CIColor(cgColor: bkColor.cgColor)]) + + let qrImage = colorFilter!.outputImage!; - + //绘制 let cgImage = CIContext().createCGImage(qrImage, from: qrImage.extent)! - - + + UIGraphicsBeginImageContext(size); let context = UIGraphicsGetCurrentContext()!; context.interpolationQuality = CGInterpolationQuality.none; @@ -464,16 +525,16 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { context.draw(cgImage, in: context.boundingBoxOfClipPath) let codeImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - + return codeImage - + } - - static public func createCode128( codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage? + + static public func createCode128(codeString: String, size: CGSize, qrColor: UIColor, bkColor: UIColor) -> UIImage? { let stringData = codeString.data(using: String.Encoding.utf8) - - + + //系统自带能生成的码 // CIAztecCodeGenerator 二维码 // CICode128BarcodeGenerator 条形码 @@ -482,36 +543,36 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { let qrFilter = CIFilter(name: "CICode128BarcodeGenerator") qrFilter?.setDefaults() qrFilter?.setValue(stringData, forKey: "inputMessage") - - - - let outputImage:CIImage? = qrFilter?.outputImage + + + + let outputImage: CIImage? = qrFilter?.outputImage let context = CIContext() let cgImage = context.createCGImage(outputImage!, from: outputImage!.extent) - + let image = UIImage(cgImage: cgImage!, scale: 1.0, orientation: UIImage.Orientation.up) - - + + // Resize without interpolating - let scaleRate:CGFloat = 20.0 + let scaleRate: CGFloat = 20.0 let resized = resizeImage(image: image, quality: CGInterpolationQuality.none, rate: scaleRate) - + return resized; } - - + + //MARK:根据扫描结果,获取图像中得二维码区域图像(如果相机拍摄角度故意很倾斜,获取的图像效果很差) - static func getConcreteCodeImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->UIImage? + static func getConcreteCodeImage(srcCodeImage: UIImage, codeResult: LBXScanResult) -> UIImage? { - let rect:CGRect = getConcreteCodeRectFromImage(srcCodeImage: srcCodeImage, codeResult: codeResult) - + let rect: CGRect = getConcreteCodeRectFromImage(srcCodeImage: srcCodeImage, codeResult: codeResult) + if rect.isEmpty { return nil } - + let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect) - + if img != nil { let imgRotation = imageRotation(image: img!, orientation: UIImage.Orientation.right) @@ -520,15 +581,15 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { return nil } //根据二维码的区域截取二维码区域图像 - static public func getConcreteCodeImage(srcCodeImage:UIImage,rect:CGRect)->UIImage? + static public func getConcreteCodeImage(srcCodeImage: UIImage, rect: CGRect) -> UIImage? { if rect.isEmpty { return nil } - + let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect) - + if img != nil { let imgRotation = imageRotation(image: img!, orientation: UIImage.Orientation.right) @@ -536,51 +597,51 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { } return nil } - + //获取二维码的图像区域 - static public func getConcreteCodeRectFromImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->CGRect + static public func getConcreteCodeRectFromImage(srcCodeImage: UIImage, codeResult: LBXScanResult) -> CGRect { - if (codeResult.arrayCorner == nil || (codeResult.arrayCorner?.count)! < 4 ) + if (codeResult.arrayCorner == nil || (codeResult.arrayCorner?.count)! < 4) { return CGRect.zero } - - let corner:[[String:Float]] = codeResult.arrayCorner as! [[String:Float]] - - let dicTopLeft = corner[0] - let dicTopRight = corner[1] + + let corner: [[String: Float]] = codeResult.arrayCorner as! [[String: Float]] + + let dicTopLeft = corner[0] + let dicTopRight = corner[1] let dicBottomRight = corner[2] - let dicBottomLeft = corner[3] - - let xLeftTopRatio:Float = dicTopLeft["X"]! - let yLeftTopRatio:Float = dicTopLeft["Y"]! - - let xRightTopRatio:Float = dicTopRight["X"]! - let yRightTopRatio:Float = dicTopRight["Y"]! - - let xBottomRightRatio:Float = dicBottomRight["X"]! - let yBottomRightRatio:Float = dicBottomRight["Y"]! - - let xLeftBottomRatio:Float = dicBottomLeft["X"]! - let yLeftBottomRatio:Float = dicBottomLeft["Y"]! - + let dicBottomLeft = corner[3] + + let xLeftTopRatio: Float = dicTopLeft["X"]! + let yLeftTopRatio: Float = dicTopLeft["Y"]! + + let xRightTopRatio: Float = dicTopRight["X"]! + let yRightTopRatio: Float = dicTopRight["Y"]! + + let xBottomRightRatio: Float = dicBottomRight["X"]! + let yBottomRightRatio: Float = dicBottomRight["Y"]! + + let xLeftBottomRatio: Float = dicBottomLeft["X"]! + let yLeftBottomRatio: Float = dicBottomLeft["Y"]! + //由于截图只能矩形,所以截图不规则四边形的最大外围 - let xMinLeft = CGFloat( min(xLeftTopRatio, xLeftBottomRatio) ) - let xMaxRight = CGFloat( max(xRightTopRatio, xBottomRightRatio) ) - - let yMinTop = CGFloat( min(yLeftTopRatio, yRightTopRatio) ) - let yMaxBottom = CGFloat ( max(yLeftBottomRatio, yBottomRightRatio) ) - + let xMinLeft = CGFloat(min(xLeftTopRatio, xLeftBottomRatio)) + let xMaxRight = CGFloat(max(xRightTopRatio, xBottomRightRatio)) + + let yMinTop = CGFloat(min(yLeftTopRatio, yRightTopRatio)) + let yMaxBottom = CGFloat (max(yLeftBottomRatio, yBottomRightRatio)) + let imgW = srcCodeImage.size.width let imgH = srcCodeImage.size.height - + //宽高反过来计算 - let rect = CGRect(x: xMinLeft * imgH, y: yMinTop*imgW, width: (xMaxRight-xMinLeft)*imgH, height: (yMaxBottom-yMinTop)*imgW) + let rect = CGRect(x: xMinLeft * imgH, y: yMinTop * imgW, width: (xMaxRight - xMinLeft) * imgH, height: (yMaxBottom - yMinTop) * imgW) return rect } - + //MARK: ----图像处理 - + /** @brief 图像中间加logo图片 @@ -589,72 +650,72 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { @param logoSize logo图像尺寸 @return 加Logo的图像 */ - static public func addImageLogo(srcImg:UIImage,logoImg:UIImage,logoSize:CGSize )->UIImage + static public func addImageLogo(srcImg: UIImage, logoImg: UIImage, logoSize: CGSize) -> UIImage { UIGraphicsBeginImageContext(srcImg.size); srcImg.draw(in: CGRect(x: 0, y: 0, width: srcImg.size.width, height: srcImg.size.height)) - let rect = CGRect(x: srcImg.size.width/2 - logoSize.width/2, y: srcImg.size.height/2-logoSize.height/2, width:logoSize.width, height: logoSize.height); + let rect = CGRect(x: srcImg.size.width / 2 - logoSize.width / 2, y: srcImg.size.height / 2 - logoSize.height / 2, width: logoSize.width, height: logoSize.height); logoImg.draw(in: rect) let resultingImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return resultingImage!; } - + //图像缩放 - static func resizeImage(image:UIImage,quality:CGInterpolationQuality,rate:CGFloat)->UIImage? + static func resizeImage(image: UIImage, quality: CGInterpolationQuality, rate: CGFloat) -> UIImage? { - var resized:UIImage?; - let width = image.size.width * rate; - let height = image.size.height * rate; - + var resized: UIImage?; + let width = image.size.width * rate; + let height = image.size.height * rate; + UIGraphicsBeginImageContext(CGSize(width: width, height: height)); let context = UIGraphicsGetCurrentContext(); context!.interpolationQuality = quality; image.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) - + resized = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - + return resized; } - - + + //图像裁剪 - static func imageByCroppingWithStyle(srcImg:UIImage,rect:CGRect)->UIImage? + static func imageByCroppingWithStyle(srcImg: UIImage, rect: CGRect) -> UIImage? { let imageRef = srcImg.cgImage let imagePartRef = imageRef!.cropping(to: rect) let cropImage = UIImage(cgImage: imagePartRef!) - + return cropImage } //图像旋转 - static func imageRotation(image:UIImage,orientation:UIImage.Orientation)->UIImage + static func imageRotation(image: UIImage, orientation: UIImage.Orientation) -> UIImage { - var rotate:Double = 0.0; - var rect:CGRect; - var translateX:CGFloat = 0.0; - var translateY:CGFloat = 0.0; - var scaleX:CGFloat = 1.0; - var scaleY:CGFloat = 1.0; - + var rotate: Double = 0.0; + var rect: CGRect; + var translateX: CGFloat = 0.0; + var translateY: CGFloat = 0.0; + var scaleX: CGFloat = 1.0; + var scaleY: CGFloat = 1.0; + switch (orientation) { case UIImage.Orientation.left: - rotate = .pi/2; + rotate = .pi / 2; rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width); translateX = 0; translateY = -rect.size.width; - scaleY = rect.size.width/rect.size.height; - scaleX = rect.size.height/rect.size.width; + scaleY = rect.size.width / rect.size.height; + scaleX = rect.size.height / rect.size.width; break; case UIImage.Orientation.right: - rotate = 3 * .pi/2; + rotate = 3 * .pi / 2; rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width); translateX = -rect.size.height; translateY = 0; - scaleY = rect.size.width/rect.size.height; - scaleX = rect.size.height/rect.size.width; + scaleY = rect.size.width / rect.size.height; + scaleX = rect.size.height / rect.size.width; break; case UIImage.Orientation.down: rotate = .pi; @@ -669,7 +730,7 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { translateY = 0; break; } - + UIGraphicsBeginImageContext(rect.size); let context = UIGraphicsGetCurrentContext()!; //做CTM变换 @@ -677,15 +738,15 @@ open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { context.scaleBy(x: 1.0, y: -1.0); context.rotate(by: CGFloat(rotate)); context.translateBy(x: translateX, y: translateY); - + context.scaleBy(x: scaleX, y: scaleY); //绘制图片 context.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)) let newPic = UIGraphicsGetImageFromCurrentImageContext(); - + return newPic!; } - + deinit { // print("LBXScanWrapper deinit") diff --git a/o2ios/O2Platform/UI/O2CanCopyUILabel.swift b/o2ios/O2Platform/UI/O2CanCopyUILabel.swift new file mode 100644 index 0000000000000000000000000000000000000000..0ec085fc9675c27513de0edb83ac85d533769e57 --- /dev/null +++ b/o2ios/O2Platform/UI/O2CanCopyUILabel.swift @@ -0,0 +1,95 @@ +// +// O2CanCopyUILabel.swift +// O2Platform +// +// Created by FancyLou on 2020/8/26. +// Copyright © 2020 zoneland. All rights reserved. +// + +import UIKit + +// MARK: - 能够复制的UILabel +class O2CanCopyUILabel: UILabel { + + override init(frame: CGRect) { + super.init(frame: frame) + setEvent() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + override func awakeFromNib() { + super.awakeFromNib() + addLongPressGesture() + } + + //必须实现的两个方法 + override var canBecomeFirstResponder: Bool { + return true + } + + override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + if action == #selector(copyText) { + return true + } + return false + } + + private func setEvent() { + addLongPressGesture() + NotificationCenter.default.addObserver(forName: UIMenuController.willHideMenuNotification, object: nil, queue: nil) { (note) in + self.backgroundColor = UIColor.white + } + } + + + private func addLongPressGesture() { + self.isUserInteractionEnabled = true + let longTap = UILongPressGestureRecognizer.init(target: self, action: #selector(longTapRecognizer)) + // 长按手势最小触发时间 + longTap.minimumPressDuration = 1.0 + // 长按手势需要的同时敲击触碰数(手指数) + longTap.numberOfTouchesRequired = 1 + // 长按有效移动范围(从点击开始,长按移动的允许范围 单位 px +// longTap.allowableMovement = 15 + self.addGestureRecognizer(longTap) + } + + private func setMenuItems(){ + self.becomeFirstResponder() + // 如果 Menu 已经被创建那就不再重复创建 menu + if (UIMenuController.shared.isMenuVisible){ + return + } + let item1 = UIMenuItem.init(title: "复制", action: #selector(copyText)) + // 单例的形式获取menu + let menu = UIMenuController.shared + // 设置箭头方向 + menu.arrowDirection = .default + // 设置 Menu 所显示的 items + menu.menuItems = [item1] + // 设置添加上 menu 的目标控件的 rect 和目标控件 + menu.setTargetRect(self.frame, in: self.superview!) + // 令 Menu 可见 + menu.setMenuVisible(true, animated: true) + } + + + @objc private func longTapRecognizer(recognizer: UIGestureRecognizer) { + if recognizer.state == .ended{ + self.backgroundColor = UIColor.white + return + }else if recognizer.state == .began{ + self.backgroundColor = UIColor.lightGray + setMenuItems() + } + } + + @objc private func copyText() { + let pboard = UIPasteboard.general + pboard.string = self.text + } + +}