提交 506721ee 编写于 作者: F fancy

改进扫码功能

上级 bc30cb4d
......@@ -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 = "<group>"; };
B167F7642362C60200F182B8 /* CloudFileMoveFolderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudFileMoveFolderCell.swift; sourceTree = "<group>"; };
B167F7652362C60200F182B8 /* CloudFileMoveFolderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CloudFileMoveFolderCell.xib; sourceTree = "<group>"; };
B168BDC624F5FFA10070329F /* ScanQRViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanQRViewController.swift; sourceTree = "<group>"; };
B168BDC824F5FFA80070329F /* ScanRecoObj.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanRecoObj.swift; sourceTree = "<group>"; };
B168BDCA24F607520070329F /* QRCodeResultViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeResultViewController.swift; sourceTree = "<group>"; };
B168BDCB24F607520070329F /* QRCodeResultViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QRCodeResultViewController.xib; sourceTree = "<group>"; };
B168BDCE24F62BA60070329F /* O2CanCopyUILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2CanCopyUILabel.swift; sourceTree = "<group>"; };
B169E3D321095EC9007156B3 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
B17126C021084A2F00369F15 /* calendar.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = calendar.storyboard; sourceTree = "<group>"; };
B1750072233C6907003DA7B9 /* BlockPan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockPan.swift; sourceTree = "<group>"; };
......@@ -2869,6 +2879,14 @@
path = Presentr;
sourceTree = "<group>";
};
B168BD9724F5FF410070329F /* m */ = {
isa = PBXGroup;
children = (
B168BDC824F5FFA80070329F /* ScanRecoObj.swift */,
);
path = m;
sourceTree = "<group>";
};
B18FFF3323756B24001B2887 /* JpushAPI */ = {
isa = PBXGroup;
children = (
......@@ -3020,6 +3038,7 @@
090253DD1F0E8BA6006B1609 /* O2LoadingView.swift */,
B1298EB323682C21006E9236 /* O2SlideImageAFSource.swift */,
B1E95F4D2373DDC4004876B7 /* ZoomImageView.swift */,
B168BDCE24F62BA60070329F /* O2CanCopyUILabel.swift */,
);
path = UI;
sourceTree = "<group>";
......@@ -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 = "<group>";
......@@ -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 */,
......
//
// 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)
}
}
})
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="QRCodeResultViewController" customModule="O2Platform" customModuleProvider="target">
<connections>
<outlet property="loginBtn" destination="Mlx-RO-T3c" id="HM9-OP-5au"/>
<outlet property="loginImage" destination="olQ-EX-sHG" id="Yga-6S-bdU"/>
<outlet property="loginStackView" destination="shd-Ph-gXJ" id="dSK-Ke-Ii7"/>
<outlet property="resultLabel" destination="5Ld-tZ-JIj" id="lzN-Zq-00j"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView hidden="YES" opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="shd-Ph-gXJ">
<rect key="frame" x="20" y="345" width="374" height="126.5"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="scan_login" translatesAutoresizingMaskIntoConstraints="NO" id="olQ-EX-sHG">
<rect key="frame" x="137" y="0.0" width="100" height="82"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="即将在电脑上登录O2网页版" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fjt-Bn-lCd">
<rect key="frame" x="80.5" y="106" width="213.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mlx-RO-T3c">
<rect key="frame" x="20" y="555.5" width="374" height="44"/>
<color key="backgroundColor" red="0.98431372549999996" green="0.2784313725" blue="0.2784313725" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="qEv-jR-Lo4"/>
</constraints>
<state key="normal" title="确认登陆">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="22"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="tap2Login:" destination="-1" eventType="touchUpInside" id="sKR-oX-f12"/>
</connections>
</button>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="扫码结果显示区域" textAlignment="center" lineBreakMode="characterWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Ld-tZ-JIj" customClass="O2CanCopyUILabel" customModule="O2Platform" customModuleProvider="target">
<rect key="frame" x="20" y="438" width="374" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="shd-Ph-gXJ" secondAttribute="trailing" constant="20" id="3cR-iT-orK"/>
<constraint firstItem="5Ld-tZ-JIj" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="5pd-eX-10e"/>
<constraint firstItem="shd-Ph-gXJ" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" constant="-40" id="B5u-rm-amT"/>
<constraint firstItem="Mlx-RO-T3c" firstAttribute="top" secondItem="shd-Ph-gXJ" secondAttribute="bottom" constant="84" id="VSJ-Pd-VE4"/>
<constraint firstItem="5Ld-tZ-JIj" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="llV-Ck-pyb"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="Mlx-RO-T3c" secondAttribute="trailing" constant="20" id="p6E-wY-2NL"/>
<constraint firstItem="Mlx-RO-T3c" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="20" id="pTq-4b-LGf"/>
<constraint firstItem="5Ld-tZ-JIj" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="20" id="qWY-1X-zHf"/>
<constraint firstItem="shd-Ph-gXJ" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="tpJ-8n-4jM"/>
<constraint firstAttribute="trailing" secondItem="5Ld-tZ-JIj" secondAttribute="trailing" constant="20" id="u4R-Tu-M6E"/>
<constraint firstItem="shd-Ph-gXJ" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="20" id="wzP-ep-TKF"/>
</constraints>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<point key="canvasLocation" x="132" y="153"/>
</view>
</objects>
<resources>
<image name="scan_login" width="100" height="82"/>
</resources>
</document>
......@@ -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")
......
//
// 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)
}
}
}
}
}
}
//
// 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
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "scan_login_content_2.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "QRCodeScanLine@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "photos_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "right@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "scan_back@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -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()
......
//
// 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
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册