Commit 69a1a665 authored by Alex朱枝文's avatar Alex朱枝文

增加扫码逻辑

parent c0755736
......@@ -1253,6 +1253,14 @@
04B00FC72D9A420F00F640C5 /* YHMakePlanPriceAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B00FB82D9A420F00F640C5 /* YHMakePlanPriceAlertView.swift */; };
04B00FC82D9A420F00F640C5 /* YHMakePlanCusttomHeadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B00FB62D9A420F00F640C5 /* YHMakePlanCusttomHeadView.swift */; };
04B00FCA2D9AAD6100F640C5 /* YHMakePlanTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B00FC92D9AAD6100F640C5 /* YHMakePlanTopView.swift */; };
04C0ED6A2E7BAC2D00B0D182 /* LBXScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED652E7BAC2D00B0D182 /* LBXScanView.swift */; };
04C0ED6B2E7BAC2D00B0D182 /* LBXScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED662E7BAC2D00B0D182 /* LBXScanViewController.swift */; };
04C0ED6C2E7BAC2D00B0D182 /* LBXScanWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED682E7BAC2D00B0D182 /* LBXScanWrapper.swift */; };
04C0ED6D2E7BAC2D00B0D182 /* LBXScanViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED672E7BAC2D00B0D182 /* LBXScanViewStyle.swift */; };
04C0ED6E2E7BAC2D00B0D182 /* LBXPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED622E7BAC2D00B0D182 /* LBXPermissions.swift */; };
04C0ED6F2E7BAC2D00B0D182 /* LBXScanNetAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED642E7BAC2D00B0D182 /* LBXScanNetAnimation.swift */; };
04C0ED702E7BAC2D00B0D182 /* LBXScanLineAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED632E7BAC2D00B0D182 /* LBXScanLineAnimation.swift */; };
04C0ED7C2E7BEF7800B0D182 /* YHScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0ED7B2E7BEF7800B0D182 /* YHScanViewController.swift */; };
04D8FF552D9258AA00703C75 /* YHPlanScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04D8FF542D9258AA00703C75 /* YHPlanScoreView.swift */; };
04D8FF572D925A0800703C75 /* YHPlanScoreChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04D8FF562D925A0800703C75 /* YHPlanScoreChart.swift */; };
04D8FF592D925E6700703C75 /* YHPlanModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04D8FF582D925E6700703C75 /* YHPlanModel.swift */; };
......@@ -2593,6 +2601,14 @@
04B00FBA2D9A420F00F640C5 /* YHMakePlanShareAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMakePlanShareAlertView.swift; sourceTree = "<group>"; };
04B00FBB2D9A420F00F640C5 /* YHMakePlanStateHeadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMakePlanStateHeadView.swift; sourceTree = "<group>"; };
04B00FC92D9AAD6100F640C5 /* YHMakePlanTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMakePlanTopView.swift; sourceTree = "<group>"; };
04C0ED622E7BAC2D00B0D182 /* LBXPermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LBXPermissions.swift; sourceTree = "<group>"; };
04C0ED632E7BAC2D00B0D182 /* LBXScanLineAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LBXScanLineAnimation.swift; sourceTree = "<group>"; };
04C0ED642E7BAC2D00B0D182 /* LBXScanNetAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LBXScanNetAnimation.swift; sourceTree = "<group>"; };
04C0ED652E7BAC2D00B0D182 /* LBXScanView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LBXScanView.swift; sourceTree = "<group>"; };
04C0ED662E7BAC2D00B0D182 /* LBXScanViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LBXScanViewController.swift; sourceTree = "<group>"; };
04C0ED672E7BAC2D00B0D182 /* LBXScanViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LBXScanViewStyle.swift; sourceTree = "<group>"; };
04C0ED682E7BAC2D00B0D182 /* LBXScanWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LBXScanWrapper.swift; sourceTree = "<group>"; };
04C0ED7B2E7BEF7800B0D182 /* YHScanViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHScanViewController.swift; sourceTree = "<group>"; };
04D8FF542D9258AA00703C75 /* YHPlanScoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHPlanScoreView.swift; sourceTree = "<group>"; };
04D8FF562D925A0800703C75 /* YHPlanScoreChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHPlanScoreChart.swift; sourceTree = "<group>"; };
04D8FF582D925E6700703C75 /* YHPlanModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHPlanModel.swift; sourceTree = "<group>"; };
......@@ -5947,6 +5963,7 @@
045C0EE02D12CA5E00BD2DC0 /* YHVideoPlaySettingVC.swift */,
048D6ADA2D5E00DE00BC6F4C /* YHMemberCenterViewController.swift */,
04AFEF562D6C78AA0007A011 /* YHVipLevelRightViewController.swift */,
04C0ED7B2E7BEF7800B0D182 /* YHScanViewController.swift */,
);
path = C;
sourceTree = "<group>";
......@@ -6264,6 +6281,7 @@
045C0F472D12CA5E00BD2DC0 /* Tools */ = {
isa = PBXGroup;
children = (
04C0ED692E7BAC2D00B0D182 /* SwiftScan */,
045C0F0E2D12CA5E00BD2DC0 /* Analytics */,
045C0F112D12CA5E00BD2DC0 /* CustomRefreshHeader */,
045C0F232D12CA5E00BD2DC0 /* Extention */,
......@@ -6854,6 +6872,20 @@
path = MakePlan;
sourceTree = "<group>";
};
04C0ED692E7BAC2D00B0D182 /* SwiftScan */ = {
isa = PBXGroup;
children = (
04C0ED622E7BAC2D00B0D182 /* LBXPermissions.swift */,
04C0ED632E7BAC2D00B0D182 /* LBXScanLineAnimation.swift */,
04C0ED642E7BAC2D00B0D182 /* LBXScanNetAnimation.swift */,
04C0ED652E7BAC2D00B0D182 /* LBXScanView.swift */,
04C0ED662E7BAC2D00B0D182 /* LBXScanViewController.swift */,
04C0ED672E7BAC2D00B0D182 /* LBXScanViewStyle.swift */,
04C0ED682E7BAC2D00B0D182 /* LBXScanWrapper.swift */,
);
path = SwiftScan;
sourceTree = "<group>";
};
04D8FF4F2D9257F700703C75 /* Plan(方案) */ = {
isa = PBXGroup;
children = (
......@@ -7421,6 +7453,13 @@
045C0FE52D12CA5F00BD2DC0 /* YHStatusAdvantageBottomView.swift in Sources */,
045C0FE62D12CA5F00BD2DC0 /* YHLifeItemTableViewCell.swift in Sources */,
045C0FE72D12CA5F00BD2DC0 /* YHIndustryLocationCell.swift in Sources */,
04C0ED6A2E7BAC2D00B0D182 /* LBXScanView.swift in Sources */,
04C0ED6B2E7BAC2D00B0D182 /* LBXScanViewController.swift in Sources */,
04C0ED6C2E7BAC2D00B0D182 /* LBXScanWrapper.swift in Sources */,
04C0ED6D2E7BAC2D00B0D182 /* LBXScanViewStyle.swift in Sources */,
04C0ED6E2E7BAC2D00B0D182 /* LBXPermissions.swift in Sources */,
04C0ED6F2E7BAC2D00B0D182 /* LBXScanNetAnimation.swift in Sources */,
04C0ED702E7BAC2D00B0D182 /* LBXScanLineAnimation.swift in Sources */,
045C0FE82D12CA5F00BD2DC0 /* YHActivityApplyInfoItemView.swift in Sources */,
045C0FE92D12CA5F00BD2DC0 /* YHVisaRenewalItemDetailType.swift in Sources */,
045C0FEA2D12CA5F00BD2DC0 /* YHPrivacyAlertView.swift in Sources */,
......@@ -8214,6 +8253,7 @@
045C12822D12CA5F00BD2DC0 /* YHServiceCenterStepThreeTableHeadView.swift in Sources */,
045C12832D12CA5F00BD2DC0 /* YHResignRiskWarningAlertView.swift in Sources */,
045C12842D12CA5F00BD2DC0 /* YHDashLineView.swift in Sources */,
04C0ED7C2E7BEF7800B0D182 /* YHScanViewController.swift in Sources */,
045C12852D12CA5F00BD2DC0 /* YHPreviewInfoCertificateInformationItemsView.swift in Sources */,
045C12862D12CA5F00BD2DC0 /* YHOfficialApprovalResultVC.swift in Sources */,
045C12872D12CA5F00BD2DC0 /* YHStatusAdvantageVC.swift in Sources */,
......
......@@ -270,6 +270,10 @@ class YHHomeHoldViewPageViewController: YHBaseViewController, WKUIDelegate, WKNa
let view = YHHomeSearchView()
let tap = UITapGestureRecognizer(target: self, action: #selector(didSearchBarClicked))
view.addGestureRecognizer(tap)
view.didClickScanBtnEvent = { [weak self] in
let vc = YHScanViewController()
self?.navigationController?.pushViewController(vc)
}
return view
}()
......
......@@ -17,12 +17,22 @@ class YHHomeSearchView: UIView {
return lable
}()
private lazy var scanBtn: UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "qr_scan_icon"), for: .normal)
btn.YH_clickEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
btn.addTarget(self, action: #selector(didClickScanBtn(btn:)), for: .touchUpInside)
return btn
}()
var placeHolder: String = "" {
didSet {
self.placeHolderLabel.text = placeHolder
}
}
var didClickScanBtnEvent: (() -> Void)?
override init(frame: CGRect) {
super.init(frame: frame)
initView()
......@@ -47,12 +57,23 @@ private extension YHHomeSearchView {
make.width.height.equalTo(16)
}
addSubview(scanBtn)
scanBtn.snp.makeConstraints { make in
make.width.height.equalTo(24.0)
make.right.equalToSuperview().offset(-12)
make.centerY.equalToSuperview()
}
addSubview(placeHolderLabel)
placeHolderLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalTo(imageV.snp.right).offset(8)
make.height.equalTo(18)
make.right.equalToSuperview().offset(-12)
make.right.equalTo(scanBtn.snp.left).offset(-12)
}
}
@objc func didClickScanBtn(btn: UIButton) {
didClickScanBtnEvent?()
}
}
......@@ -130,6 +130,14 @@ class YHMyNewViewController: YHBaseViewController {
return btn
}()
private lazy var scanBtn: UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "qr_scan_icon"), for: .normal)
btn.YH_clickEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
btn.addTarget(self, action: #selector(didClickScanBtn(btn:)), for: .touchUpInside)
return btn
}()
private lazy var settingRedPointView: UIView = {
let view = UIView()
view.layer.cornerRadius = kCornerRadius3
......@@ -150,6 +158,7 @@ class YHMyNewViewController: YHBaseViewController {
view.addSubview(bgImgView)
view.addSubview(tableView)
view.addSubview(settingBtn)
view.addSubview(scanBtn)
view.addSubview(settingRedPointView)
bgImgView.snp.makeConstraints { make in
......@@ -176,6 +185,12 @@ class YHMyNewViewController: YHBaseViewController {
make.bottom.equalTo(settingBtn.snp.top).offset(3)
}
scanBtn.snp.makeConstraints { make in
make.width.height.equalTo(24.0)
make.right.equalTo(settingBtn.snp.left).offset(-17)
make.top.equalTo(k_Height_StatusBar+10)
}
updateUserUI()
NotificationCenter.default.addObserver(self, selector: #selector(loginSuccess), name: YhConstant.YhNotification.didLoginSuccessNotifiction, object: nil)
......@@ -303,12 +318,13 @@ class YHMyNewViewController: YHBaseViewController {
if !checkLogin() { return }
let vc = YHMySettingViewController()
self.navigationController?.pushViewController(vc)
// let vc = YHH5WebViewVC()
// vc.isFullScreenFlag = false
// vc.url = "http://192.168.34.113:10301/superAppBridge.html#/order/antom-pay?id=1864&param=eyJhbGbolyFeciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTc0NzI5MzAsImkiOjQyMzgxNzEzODcwNzAyMSwidSI6IumTtuays-mbhuWboiIsInIiOiJzdXBlcl9hcHAifQ.Phsg13OfN5XJ0cLe759U2W0hH1iTZeneimgTlnouPcg"
// self.navigationController?.pushViewController(vc)
}
@objc func didClickScanBtn(btn: UIButton) {
let vc = YHScanViewController()
self.navigationController?.pushViewController(vc)
}
}
extension YHMyNewViewController: UITableViewDelegate, UITableViewDataSource {
......
//
// YHScanViewController.swift
// galaxy
//
// Created by alexzzw on 2025/9/18.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHScanViewController: LBXScanViewController {
private let topOffset = 44.0
private let leftOffset = 48.0
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
gk_navTitleColor = .white
gk_navTitle = "扫码"
gk_navBarAlpha = 0.0
gk_navBackgroundColor = .clear
gk_backImage = UIImage(named: "back_icon_white")
setupScanStyle()
}
override func handleCodeResult(arrayResult: [LBXScanResult]) {
//
}
deinit {
//
}
}
extension YHScanViewController {
private func setupScanStyle() {
var style = LBXScanViewStyle()
style.centerUpOffset = topOffset
style.xScanRetangleOffset = leftOffset
style.photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle.Inner
style.photoframeLineW = 3
style.photoframeAngleW = 18
style.photoframeAngleH = 18
style.isNeedShowRetangle = false
style.anmiationStyle = LBXScanViewAnimationStyle.LineMove
// 扫描横线图片
style.animationImage = UIImage(named: "scan_move_line")
// 4个角的颜色
style.colorAngle = UIColor.white
// 非矩形框区域颜色
style.color_NotRecoginitonArea = UIColor.black.withAlphaComponent(0.3)
scanStyle = style
}
}
//
// LBXPermissions.swift
// swiftScan
//
// Created by xialibing on 15/12/15.
// Copyright © 2015年 xialibing. All rights reserved.
//
import UIKit
import AVFoundation
import Photos
import AssetsLibrary
class LBXPermissions: NSObject {
// MARK: - ---获取相册权限
static func authorizePhotoWith(comletion: @escaping (Bool) -> Void ) {
let granted = PHPhotoLibrary.authorizationStatus()
switch granted {
case PHAuthorizationStatus.authorized:
comletion(true)
case PHAuthorizationStatus.denied, PHAuthorizationStatus.restricted:
comletion(false)
case PHAuthorizationStatus.notDetermined:
PHPhotoLibrary.requestAuthorization({ (status) in
DispatchQueue.main.async {
comletion(status == PHAuthorizationStatus.authorized ? true:false)
}
})
case .limited:
comletion(false)
@unknown default:
comletion(false)
}
}
// MARK: - --相机权限
static func authorizeCameraWith(comletion: @escaping (Bool) -> Void ) {
let granted = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
switch granted {
case .authorized:
comletion(true)
case .denied:
comletion(false)
case .restricted:
comletion(false)
case .notDetermined:
AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (granted: Bool) in
DispatchQueue.main.async {
comletion(granted)
}
})
@unknown default:
comletion(false)
}
}
// MARK: 跳转到APP系统设置权限界面
static func jumpToSystemPrivacySetting() {
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
if #available(iOS 10, *) {
UIApplication.shared.open(appSetting, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(appSetting)
}
}
}
}
//
// LBXScanLineAnimation.swift
// swiftScan
//
// Created by lbxia on 15/12/9.
// Copyright © 2015年 xialibing. All rights reserved.
//
import UIKit
class LBXScanLineAnimation: UIImageView {
var isAnimationing = false
var animationRect: CGRect = CGRect.zero
func startAnimatingWithRect(animationRect: CGRect, parentView: UIView, image: UIImage?) {
self.image = image
self.animationRect = animationRect
parentView.addSubview(self)
self.isHidden = false
isAnimationing = true
if image != nil {
stepAnimation()
}
}
@objc func stepAnimation() {
if !isAnimationing {
return
}
guard let image = self.image else { return }
var frame: CGRect = animationRect
let hImg = image.size.height * animationRect.size.width / image.size.width
frame.origin.y -= hImg
frame.size.height = hImg
self.frame = frame
self.alpha = 0.0
UIView.animate(withDuration: 1.4, animations: { () in
self.alpha = 1.0
var frame = self.animationRect
let hImg = image.size.height * self.animationRect.size.width / image.size.width
frame.origin.y += (frame.size.height - hImg)
frame.size.height = hImg
self.frame = frame
}, completion: { (_: Bool) in
self.perform(#selector(LBXScanLineAnimation.stepAnimation), with: nil, afterDelay: 0.3)
})
}
func stopStepAnimating() {
self.isHidden = true
isAnimationing = false
}
static public func instance() -> LBXScanLineAnimation {
return LBXScanLineAnimation()
}
deinit {
stopStepAnimating()
}
}
//
// LBXScanNetAnimation.swift
// swiftScan
//
// Created by lbxia on 15/12/9.
// Copyright © 2015年 xialibing. All rights reserved.
//
import UIKit
class LBXScanNetAnimation: UIImageView {
var isAnimationing = false
var animationRect: CGRect = CGRect.zero
static public func instance() -> LBXScanNetAnimation {
return LBXScanNetAnimation()
}
func startAnimatingWithRect(animationRect: CGRect, parentView: UIView, image: UIImage?) {
self.image = image
self.animationRect = animationRect
parentView.addSubview(self)
self.isHidden = false
isAnimationing = true
if image != nil {
stepAnimation()
}
}
@objc func stepAnimation() {
if !isAnimationing {
return
}
guard let image = self.image else { return }
var frame = animationRect
let hImg = image.size.height * animationRect.size.width / image.size.width
frame.origin.y -= hImg
frame.size.height = hImg
self.frame = frame
self.alpha = 0.0
UIView.animate(withDuration: 1.2, animations: { () in
self.alpha = 1.0
var frame = self.animationRect
let hImg = image.size.height * self.animationRect.size.width / image.size.width
frame.origin.y += (frame.size.height - hImg)
frame.size.height = hImg
self.frame = frame
}, completion: { (_: Bool) in
self.perform(#selector(LBXScanNetAnimation.stepAnimation), with: nil, afterDelay: 0.3)
})
}
func stopStepAnimating() {
self.isHidden = true
isAnimationing = false
}
}
//
// LBXScanView.swift
// swiftScan
//
// Created by xialibing on 15/12/8.
// Copyright © 2015年 xialibing. All rights reserved.
//
import UIKit
open class LBXScanView: UIView {
// 扫码区域各种参数
var viewStyle: LBXScanViewStyle = LBXScanViewStyle()
// 扫码区域
var scanRetangleRect: CGRect = CGRect.zero
// 线条扫码动画封装
var scanLineAnimation: LBXScanLineAnimation?
// 网格扫码动画封装
var scanNetAnimation: LBXScanNetAnimation?
// 线条在中间位置,不移动
var scanLineStill: UIImageView?
// 启动相机时 菊花等待
var activityView: UIActivityIndicatorView?
// 启动相机中的提示文字
var labelReadying: UILabel?
// 记录动画状态
var isAnimationing: Bool = false
/**
初始化扫描界面
- parameter frame: 界面大小,一般为视频显示区域
- parameter vstyle: 界面效果参数
- returns: instancetype
*/
public init(frame: CGRect, vstyle: LBXScanViewStyle ) {
viewStyle = vstyle
switch viewStyle.anmiationStyle {
case .LineMove:
scanLineAnimation = LBXScanLineAnimation.instance()
case .NetGrid:
scanNetAnimation = LBXScanNetAnimation.instance()
case .LineStill:
scanLineStill = UIImageView()
scanLineStill?.image = viewStyle.animationImage
default:
break
}
var frameTmp = frame
frameTmp.origin = CGPoint.zero
super.init(frame: frameTmp)
backgroundColor = UIColor.clear
}
override init(frame: CGRect) {
var frameTmp = frame
frameTmp.origin = CGPoint.zero
super.init(frame: frameTmp)
backgroundColor = UIColor.clear
}
required public init?(coder aDecoder: NSCoder) {
self.init()
}
deinit {
if scanLineAnimation != nil {
scanLineAnimation!.stopStepAnimating()
}
if scanNetAnimation != nil {
scanNetAnimation!.stopStepAnimating()
}
}
/**
* 开始扫描动画
*/
func startScanAnimation() {
if isAnimationing {
return
}
isAnimationing = true
let cropRect: CGRect = getScanRectForAnimation()
switch viewStyle.anmiationStyle {
case .LineMove:
scanLineAnimation?.startAnimatingWithRect(animationRect: cropRect, parentView: self, image: viewStyle.animationImage )
case .NetGrid:
scanNetAnimation?.startAnimatingWithRect(animationRect: cropRect, parentView: self, image: viewStyle.animationImage )
case .LineStill:
let stillRect = CGRect(x: cropRect.origin.x + 20,
y: cropRect.origin.y + cropRect.size.height / 2,
width: cropRect.size.width - 40,
height: 2)
self.scanLineStill?.frame = stillRect
if let scanLineStill = scanLineStill {
self.addSubview(scanLineStill)
}
self.scanLineStill?.isHidden = false
default: break
}
}
/**
* 开始扫描动画
*/
func stopScanAnimation() {
isAnimationing = false
switch viewStyle.anmiationStyle {
case .LineMove:
scanLineAnimation?.stopStepAnimating()
case .NetGrid:
scanNetAnimation?.stopStepAnimating()
case .LineStill:
self.scanLineStill?.isHidden = true
default: break
}
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override open func draw(_ rect: CGRect) {
// Drawing code
drawScanRect()
}
// MARK: - ---- 绘制扫码效果-----
func drawScanRect() {
let XRetangleLeft = viewStyle.xScanRetangleOffset
var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2.0, height: self.frame.size.width - XRetangleLeft*2.0)
if viewStyle.whRatio != 1.0 {
let w = sizeRetangle.width
var h: CGFloat = w / viewStyle.whRatio
let hInt: Int = Int(h)
h = CGFloat(hInt)
sizeRetangle = CGSize(width: w, height: h)
}
// 扫码区域Y轴最小坐标
let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset
let YMaxRetangle = YMinRetangle + sizeRetangle.height
let XRetangleRight = self.frame.size.width - XRetangleLeft
// print("frame:%@",NSStringFromCGRect(self.frame))
let context = UIGraphicsGetCurrentContext()
// 非扫码区域半透明
// 设置非识别区域颜色
context?.setFillColor(viewStyle.color_NotRecoginitonArea.cgColor)
// 填充矩形
// 扫码区域上面填充
var rect = CGRect(x: 0, y: 0, width: self.frame.size.width, height: YMinRetangle)
context?.fill(rect)
// 扫码区域左边填充
rect = CGRect(x: 0, y: YMinRetangle, width: XRetangleLeft, height: sizeRetangle.height)
context?.fill(rect)
// 扫码区域右边填充
rect = CGRect(x: XRetangleRight, y: YMinRetangle, width: XRetangleLeft, height: sizeRetangle.height)
context?.fill(rect)
// 扫码区域下面填充
rect = CGRect(x: 0, y: YMaxRetangle, width: self.frame.size.width, height: self.frame.size.height - YMaxRetangle)
context?.fill(rect)
// 执行绘画
context?.strokePath()
if viewStyle.isNeedShowRetangle {
// 中间画矩形(正方形)
context?.setStrokeColor(viewStyle.colorRetangleLine.cgColor)
context?.setLineWidth(1)
context?.addRect(CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height))
// CGContextMoveToPoint(context, XRetangleLeft, YMinRetangle);
// CGContextAddLineToPoint(context, XRetangleLeft+sizeRetangle.width, YMinRetangle);
context?.strokePath()
}
scanRetangleRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)
// 画矩形框4格外围相框角
// 相框角的宽度和高度
let wAngle = viewStyle.photoframeAngleW
let hAngle = viewStyle.photoframeAngleH
// 4个角的 线的宽度
let linewidthAngle = viewStyle.photoframeLineW// 经验参数:6和4
// 画扫码矩形以及周边半透明黑色坐标参数
var diffAngle = linewidthAngle/3
diffAngle = linewidthAngle / 2 // 框外面4个角,与框有缝隙
diffAngle = linewidthAngle/2 // 框4个角 在线上加4个角效果
diffAngle = 0// 与矩形框重合
switch viewStyle.photoframeAngleStyle {
case .Outer:
diffAngle = linewidthAngle/3// 框外面4个角,与框紧密联系在一起
case .On:
diffAngle = 0
case .Inner:
diffAngle = -viewStyle.photoframeLineW/2
}
context?.setStrokeColor(viewStyle.colorAngle.cgColor)
context?.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
// Draw them with a 2.0 stroke width so they are a bit more visible.
context?.setLineWidth(linewidthAngle)
//
let leftX = XRetangleLeft - diffAngle
let topY = YMinRetangle - diffAngle
let rightX = XRetangleRight + diffAngle
let bottomY = YMaxRetangle + diffAngle
// 左上角水平线
context?.move(to: CGPoint(x: leftX-linewidthAngle/2, y: topY))
context?.addLine(to: CGPoint(x: leftX + wAngle, y: topY))
// 左上角垂直线
context?.move(to: CGPoint(x: leftX, y: topY-linewidthAngle/2))
context?.addLine(to: CGPoint(x: leftX, y: topY+hAngle))
// 左下角水平线
context?.move(to: CGPoint(x: leftX-linewidthAngle/2, y: bottomY))
context?.addLine(to: CGPoint(x: leftX + wAngle, y: bottomY))
// 左下角垂直线
context?.move(to: CGPoint(x: leftX, y: bottomY+linewidthAngle/2))
context?.addLine(to: CGPoint(x: leftX, y: bottomY - hAngle))
// 右上角水平线
context?.move(to: CGPoint(x: rightX+linewidthAngle/2, y: topY))
context?.addLine(to: CGPoint(x: rightX - wAngle, y: topY))
// 右上角垂直线
context?.move(to: CGPoint(x: rightX, y: topY-linewidthAngle/2))
context?.addLine(to: CGPoint(x: rightX, y: topY + hAngle))
// 右下角水平线
context?.move(to: CGPoint(x: rightX+linewidthAngle/2, y: bottomY))
context?.addLine(to: CGPoint(x: rightX - wAngle, y: bottomY))
// 右下角垂直线
context?.move(to: CGPoint(x: rightX, y: bottomY+linewidthAngle/2))
context?.addLine(to: CGPoint(x: rightX, y: bottomY - hAngle))
context?.strokePath()
}
func getScanRectForAnimation() -> CGRect {
let XRetangleLeft = viewStyle.xScanRetangleOffset
var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2)
if viewStyle.whRatio != 1 {
let w = sizeRetangle.width
var h = w / viewStyle.whRatio
let hInt: Int = Int(h)
h = CGFloat(hInt)
sizeRetangle = CGSize(width: w, height: h)
}
// 扫码区域Y轴最小坐标
let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset
// 扫码区域坐标
let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)
return cropRect
}
// 根据矩形区域,获取识别区域
static func getScanRectWithPreView(preView: UIView, style: LBXScanViewStyle) -> CGRect {
let XRetangleLeft = style.xScanRetangleOffset
var sizeRetangle = CGSize(width: preView.frame.size.width - XRetangleLeft*2, height: preView.frame.size.width - XRetangleLeft*2)
if style.whRatio != 1 {
let w = sizeRetangle.width
var h = w / style.whRatio
let hInt: Int = Int(h)
h = CGFloat(hInt)
sizeRetangle = CGSize(width: w, height: h)
}
// 扫码区域Y轴最小坐标
let YMinRetangle = preView.frame.size.height / 2.0 - sizeRetangle.height/2.0 - style.centerUpOffset
// 扫码区域坐标
let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)
// 计算兴趣区域
var rectOfInterest: CGRect
// ref:http://www.cocoachina.com/ios/20141225/10763.html
let size = preView.bounds.size
let p1 = size.height/size.width
let p2: CGFloat = 1920.0/1080.0 // 使用了1080p的图像输出
if p1 < p2 {
let fixHeight = size.width * 1920.0 / 1080.0
let fixPadding = (fixHeight - size.height)/2
rectOfInterest = CGRect(x: (cropRect.origin.y + fixPadding)/fixHeight,
y: cropRect.origin.x/size.width,
width: cropRect.size.height/fixHeight,
height: cropRect.size.width/size.width)
} else {
let fixWidth = size.height * 1080.0 / 1920.0
let fixPadding = (fixWidth - size.width)/2
rectOfInterest = CGRect(x: cropRect.origin.y/size.height,
y: (cropRect.origin.x + fixPadding)/fixWidth,
width: cropRect.size.height/size.height,
height: cropRect.size.width/fixWidth)
}
return rectOfInterest
}
func getRetangeSize() -> CGSize {
let XRetangleLeft = viewStyle.xScanRetangleOffset
var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2)
let w = sizeRetangle.width
var h = w / viewStyle.whRatio
let hInt: Int = Int(h)
h = CGFloat(hInt)
sizeRetangle = CGSize(width: w, height: h)
return sizeRetangle
}
func deviceStartReadying(readyStr: String) {
let XRetangleLeft = viewStyle.xScanRetangleOffset
let sizeRetangle = getRetangeSize()
// 扫码区域Y轴最小坐标
let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset
// 设备启动状态提示
if activityView == nil {
let activityView = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
self.activityView = activityView
activityView.center = CGPoint(x: XRetangleLeft + sizeRetangle.width/2 - 50, y: YMinRetangle + sizeRetangle.height/2)
// activityView = UIActivityIndicatorView.Style.whiteLarge
addSubview(activityView)
let labelReadyRect = CGRect(x: activityView.frame.origin.x + activityView.frame.size.width + 10, y: activityView.frame.origin.y, width: 100, height: 30)
// print("%@",NSStringFromCGRect(labelReadyRect))
let labelReadying = UILabel(frame: labelReadyRect)
self.labelReadying = labelReadying
labelReadying.text = readyStr
labelReadying.backgroundColor = UIColor.clear
labelReadying.textColor = UIColor.white
labelReadying.font = UIFont.systemFont(ofSize: 18.0)
addSubview(labelReadying)
}
activityView?.startAnimating()
}
func deviceStopReadying() {
if activityView != nil {
activityView?.stopAnimating()
activityView?.removeFromSuperview()
labelReadying?.removeFromSuperview()
activityView = nil
labelReadying = nil
}
}
}
//
// LBXScanViewController.swift
// swiftScan
//
// Created by lbxia on 15/12/8.
// Copyright © 2015年 xialibing. All rights reserved.
//
import UIKit
import Foundation
import AVFoundation
protocol LBXScanViewControllerDelegate: AnyObject {
func scanFinished(scanResult: LBXScanResult, error: String?)
}
protocol QRRectDelegate: AnyObject {
func drawwed()
}
class LBXScanViewController: YHBaseViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// 返回扫码结果,也可以通过继承本控制器,改写该handleCodeResult方法即可
open weak var scanResultDelegate: LBXScanViewControllerDelegate?
open var delegate: QRRectDelegate?
open var scanObj: LBXScanWrapper?
open var scanStyle: LBXScanViewStyle? = LBXScanViewStyle()
open var qRScanView: LBXScanView?
// 启动区域识别功能
open var isOpenInterestRect = false
// 识别码的类型
public var arrayCodeType: [AVMetadataObject.ObjectType]?
// 是否需要识别后的当前图像
public var isNeedCodeImage = false
// 相机启动提示文字
public var readyString: String = "loading"
override open func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.black
drawScanView()
// self.edgesForExtendedLayout = UIRectEdge(rawValue: 0)
}
open func setNeedCodeImage(needCodeImg: Bool) {
isNeedCodeImage = needCodeImg
}
// 设置框内识别
open func setOpenInterestRect(isOpen: Bool) {
isOpenInterestRect = isOpen
}
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
LBXPermissions.authorizeCameraWith { [weak self] granted in
if granted {
self?.perform(#selector(LBXScanViewController.startScan), with: nil, afterDelay: 0.3)
} else {
// 跳转到系统设置
YHCommonAlertView.show("", "没有相机权限,是否跳转设置页面开启?", "取消", "确定", fullGuestureEnable: false) {
} callBack: {
let localUrl = URL(string: UIApplication.openSettingsURLString)
if let url = localUrl {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
}
}
}
@objc open func startScan() {
if scanObj == nil {
var cropRect = CGRect.zero
if isOpenInterestRect {
guard let style = scanStyle else { return }
cropRect = LBXScanView.getScanRectWithPreView(preView: self.view, style: style)
}
// 指定识别几种码
if arrayCodeType == nil {
arrayCodeType = [.qr, .ean13, .code39, .code128]
}
guard let codeTypes = arrayCodeType else { return }
scanObj = LBXScanWrapper(videoPreView: self.view, objType: codeTypes, isCaptureImg: isNeedCodeImage, cropRect: cropRect, success: { [weak self] arrayResult in
if let strongSelf = self {
// 停止扫描动画
strongSelf.qRScanView?.stopScanAnimation()
strongSelf.handleCodeResult(arrayResult: arrayResult)
}
})
}
// 结束相机等待提示
qRScanView?.deviceStopReadying()
// 开始扫描动画
qRScanView?.startScanAnimation()
// 相机运行
scanObj?.start()
}
open func stopScan() {
qRScanView?.stopScanAnimation()
scanObj?.stop()
}
open func drawScanView() {
if qRScanView == nil {
guard let style = scanStyle else { return }
let qRScanView = LBXScanView(frame: self.view.frame, vstyle: style)
self.qRScanView = qRScanView
self.view.addSubview(qRScanView)
delegate?.drawwed()
}
qRScanView?.deviceStartReadying(readyStr: readyString)
}
// 处理扫码结果,如果是继承本控制器的,可以重写该方法,作出相应地处理,或者设置delegate作出相应处理
open func handleCodeResult(arrayResult: [LBXScanResult]) {
if let delegate = scanResultDelegate {
self.navigationController?.popViewController(animated: true)
let result: LBXScanResult = arrayResult[0]
delegate.scanFinished(scanResult: result, error: nil)
} else {
for result: LBXScanResult in arrayResult {
debugPrint("%@", result.strScanned ?? "")
}
let result: LBXScanResult = arrayResult[0]
YHCommonAlertView.show(result.strBarCodeType, result.strScanned, "", "确定", fullGuestureEnable: false) {
} callBack: {
}
}
}
override open func viewWillDisappear(_ animated: Bool) {
NSObject.cancelPreviousPerformRequests(withTarget: self)
stopScan()
}
open func openPhotoAlbum() {
LBXPermissions.authorizePhotoWith { [weak self] granted in
if granted {
let picker = UIImagePickerController()
picker.sourceType = .photoLibrary
picker.delegate = self
picker.allowsEditing = true
self?.present(picker, animated: true, completion: nil)
} else {
YHCommonAlertView.show("", "没有相册权限,是否跳转设置页面开启?", "取消", "确定", fullGuestureEnable: false) {
} callBack: {
let localUrl = URL(string: UIApplication.openSettingsURLString)
if let url = localUrl {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
}
}
}
// MARK: - 相册选择图片识别二维码 (条形码没有找到系统方法)
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
picker.dismiss(animated: true, completion: nil)
var image: UIImage? = info[.editedImage] as? UIImage
if image == nil {
image = info[.originalImage] as? UIImage
}
if let validImage = image {
let arrayResult = LBXScanWrapper.recognizeQRImage(image: validImage)
if arrayResult.count > 0 {
handleCodeResult(arrayResult: arrayResult)
return
}
}
YHCommonAlertView.show("", "暂无法识别图片中二维码", "", "确定", fullGuestureEnable: false) {
} callBack: {
}
}
deinit {
}
}
//
// LBXScanViewStyle.swift
// swiftScan
//
// Created by xialibing on 15/12/8.
// Copyright © 2015年 xialibing. All rights reserved.
//
import UIKit
/// 扫码区域动画效果
public enum LBXScanViewAnimationStyle {
case LineMove // 线条上下移动
case NetGrid // 网格
case LineStill // 线条停止在扫码区域中央
case None // 无动画
}
/// 扫码区域4个角位置类型
public enum LBXScanViewPhotoframeAngleStyle {
case Inner // 内嵌,一般不显示矩形框情况下
case Outer // 外嵌,包围在矩形框的4个角
case On // 在矩形框的4个角上,覆盖
}
public struct LBXScanViewStyle {
// MARK: - -中心位置矩形框
/// 是否需要绘制扫码矩形框,默认YES
public var isNeedShowRetangle: Bool = true
/**
* 默认扫码区域为正方形,如果扫码区域不是正方形,设置宽高比
*/
public var whRatio: CGFloat = 1.0
/**
@brief 矩形框(视频显示透明区)域向上移动偏移量,0表示扫码透明区域在当前视图中心位置,如果负值表示扫码区域下移
*/
public var centerUpOffset: CGFloat = 44
/**
* 矩形框(视频显示透明区)域离界面左边及右边距离,默认60
*/
public var xScanRetangleOffset: CGFloat = 60
/**
@brief 矩形框线条颜色,默认白色
*/
public var colorRetangleLine = UIColor.white
// MARK: - 矩形框(扫码区域)周围4个角
/**
@brief 扫码区域的4个角类型
*/
public var photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle.Outer
// 4个角的颜色
public var colorAngle = UIColor(red: 0.0, green: 167.0 / 255.0, blue: 231.0 / 255.0, alpha: 1.0)
// 扫码区域4个角的宽度和高度
public var photoframeAngleW: CGFloat = 24.0
public var photoframeAngleH: CGFloat = 24.0
/**
@brief 扫码区域4个角的线条宽度,默认6,建议8到4之间
*/
public var photoframeLineW: CGFloat = 6
// MARK: - ---动画效果
/**
@brief 扫码动画效果:线条或网格
*/
public var anmiationStyle = LBXScanViewAnimationStyle.LineMove
/**
* 动画效果的图像,如线条或网格的图像
*/
public var animationImage: UIImage?
// MARK: - 非识别区域颜色,默认 RGBA (0,0,0,0.5),范围(0--1)
public var color_NotRecoginitonArea: UIColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
public init() {
}
}
//
// LBXScanWrapper.swift
// swiftScan
//
// Created by lbxia on 15/12/10.
// Copyright © 2015年 xialibing. All rights reserved.
//
import AVFoundation
import UIKit
public struct LBXScanResult {
// 码内容
public var strScanned: String? = ""
// 扫码图像
public var imgScanned: UIImage?
// 码的类型
public var strBarCodeType: String? = ""
// 码在图像中的位置
public var arrayCorner: [AnyObject]?
public init(str: String?, img: UIImage?, barCodeType: String?, corner: [AnyObject]?) {
strScanned = str
imgScanned = img
strBarCodeType = barCodeType
arrayCorner = corner
}
}
open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate {
let device = AVCaptureDevice.default(for: AVMediaType.video)
var input: AVCaptureDeviceInput?
var output: AVCaptureMetadataOutput
let session = AVCaptureSession()
var previewLayer: AVCaptureVideoPreviewLayer?
var stillImageOutput: AVCaptureStillImageOutput?
// 存储返回结果
var arrayResult: [LBXScanResult] = []
// 扫码结果返回block
var successBlock: ([LBXScanResult]) -> Void
// 是否需要拍照
var isNeedCaptureImage: Bool
// 当前扫码结果是否处理
var isNeedScanResult: Bool = true
private let sessionQueue = DispatchQueue(label: "com.lbxscan.session", qos: .userInitiated)
init(videoPreView: UIView, objType: [AVMetadataObject.ObjectType] = [.qr], isCaptureImg: Bool, cropRect: CGRect = CGRect.zero, success: @escaping (([LBXScanResult]) -> Void)) {
successBlock = success
output = AVCaptureMetadataOutput()
isNeedCaptureImage = isCaptureImg
stillImageOutput = AVCaptureStillImageOutput()
super.init()
guard let device = device else { return }
do {
input = try AVCaptureDeviceInput(device: device)
} catch let error as NSError {
print("AVCaptureDeviceInput(): \(error)")
return
}
guard let validInput = input else { return }
if session.canAddInput(validInput) {
session.addInput(validInput)
}
if session.canAddOutput(output) {
session.addOutput(output)
}
if let stillOutput = stillImageOutput, session.canAddOutput(stillOutput) {
session.addOutput(stillOutput)
}
let outputSettings: [String: Any] = [AVVideoCodecKey: AVVideoCodecType.jpeg]
stillImageOutput?.outputSettings = outputSettings
session.sessionPreset = AVCaptureSession.Preset.high
// 参数设置
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
output.metadataObjectTypes = objType
if !cropRect.equalTo(CGRect.zero) {
// 启动相机后,直接修改该参数无效
output.rectOfInterest = cropRect
}
previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
var frame: CGRect = videoPreView.frame
frame.origin = CGPoint.zero
previewLayer?.frame = frame
if let layer = previewLayer {
videoPreView.layer.insertSublayer(layer, at: 0)
}
if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(AVCaptureDevice.FocusMode.continuousAutoFocus) {
do {
try device.lockForConfiguration()
device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus
device.unlockForConfiguration()
} catch let error as NSError {
print("device.lockForConfiguration(): \(error)")
}
}
}
public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if !isNeedScanResult {
// 上一帧处理中
return
}
isNeedScanResult = false
arrayResult.removeAll()
// 识别扫码类型
for current in metadataObjects {
if let code = current as? AVMetadataMachineReadableCodeObject {
// 码类型
let codeType = code.type
// 码内容
let codeContent = code.stringValue
arrayResult.append(LBXScanResult(str: codeContent, img: UIImage(), barCodeType: codeType.rawValue, corner: code.corners as [AnyObject]?))
}
}
if arrayResult.count > 0 {
if isNeedCaptureImage {
captureImage()
} else {
stop()
successBlock(arrayResult)
}
} else {
isNeedScanResult = true
}
}
func start() {
sessionQueue.async {
if !self.session.isRunning {
self.isNeedScanResult = true
self.session.startRunning()
}
}
}
func stop() {
sessionQueue.async {
if self.session.isRunning {
self.isNeedScanResult = false
self.session.stopRunning()
}
}
}
// MARK: - ---拍照
open func captureImage() {
guard let stillOutput = stillImageOutput else {
stop()
successBlock(arrayResult)
return
}
let stillImageConnection: AVCaptureConnection? = connectionWithMediaType(mediaType: AVMediaType.video as AVMediaType, connections: stillOutput.connections as [AnyObject])
guard let connection = stillImageConnection else {
stop()
successBlock(arrayResult)
return
}
stillOutput.captureStillImageAsynchronously(from: connection, completionHandler: { imageDataSampleBuffer, _ in
self.stop()
if let sampleBuffer = imageDataSampleBuffer, let imageData: Data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer) {
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? {
for connection in connections {
guard let connectionTmp = connection as? AVCaptureConnection else { continue }
for port in connectionTmp.inputPorts {
if port.mediaType == mediaType {
return connectionTmp
}
}
}
return nil
}
// MARK: 切换识别区域
open func changeScanRect(cropRect: CGRect) {
sessionQueue.async {
if self.session.isRunning {
self.session.stopRunning()
}
DispatchQueue.main.async {
self.output.rectOfInterest = cropRect
self.sessionQueue.async {
self.session.startRunning()
}
}
}
}
// MARK: 切换识别码的类型
open func changeScanType(objType: [AVMetadataObject.ObjectType]) {
// 待测试中途修改是否有效
output.metadataObjectTypes = objType
}
open func isGetFlash() -> Bool {
guard let device = device else { return false }
return device.hasFlash && device.hasTorch
}
/**
打开或关闭闪关灯
- parameter torch: true:打开闪关灯 false:关闭闪光灯
*/
open func setTorch(torch: Bool) {
if isGetFlash() {
guard let device = input?.device else { return }
do {
try device.lockForConfiguration()
device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off
device.unlockForConfiguration()
} catch let error as NSError {
print("device.lockForConfiguration(): \(error)")
}
}
}
/**
------闪光灯打开或关闭
*/
open func changeTorch() {
if isGetFlash() {
guard let device = input?.device else { return }
do {
try device.lockForConfiguration()
var torch = false
if device.torchMode == AVCaptureDevice.TorchMode.on {
torch = false
} else if device.torchMode == AVCaptureDevice.TorchMode.off {
torch = true
}
device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off
device.unlockForConfiguration()
} catch let error as NSError {
print("device.lockForConfiguration(): \(error)")
}
}
}
// MARK: - -----获取系统默认支持的码的类型
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
]
types.append(AVMetadataObject.ObjectType.interleaved2of5)
types.append(AVMetadataObject.ObjectType.itf14)
types.append(AVMetadataObject.ObjectType.dataMatrix)
return types as [AVMetadataObject.ObjectType]
}
static func isSysIos8Later() -> Bool {
if #available(iOS 8, *) {
return true
}
return false
}
/**
识别二维码码图像
- parameter image: 二维码图像
- returns: 返回识别结果
*/
public static func recognizeQRImage(image: UIImage) -> [LBXScanResult] {
var returnResult: [LBXScanResult] = []
if LBXScanWrapper.isSysIos8Later() {
guard let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]),
let cgImage = image.cgImage else {
return returnResult
}
let img = CIImage(cgImage: cgImage)
let features: [CIFeature]? = detector.features(in: img, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
if let validFeatures = features, validFeatures.count > 0 {
let feature = validFeatures[0]
if let featureTmp = feature as? CIQRCodeFeature {
let scanResult = featureTmp.messageString
let result = LBXScanResult(str: scanResult, img: image, barCodeType: AVMetadataObject.ObjectType.qr.rawValue, corner: nil)
returnResult.append(result)
}
}
}
return returnResult
}
// MARK: - - - 生成二维码,背景色及二维码颜色设置
public static func createCode(codeType: String, codeString: String, size: CGSize, qrColor: UIColor, bkColor: UIColor) -> UIImage? {
guard let stringData = codeString.data(using: String.Encoding.utf8),
let qrFilter = CIFilter(name: codeType) else {
return nil
}
qrFilter.setValue(stringData, forKey: "inputMessage")
qrFilter.setValue("M", forKey: "inputCorrectionLevel")
guard let outputImage = qrFilter.outputImage,
let colorFilter = CIFilter(name: "CIFalseColor", parameters: ["inputImage": outputImage, "inputColor0": CIColor(cgColor: qrColor.cgColor), "inputColor1": CIColor(cgColor: bkColor.cgColor)]),
let qrImage = colorFilter.outputImage,
let cgImage = CIContext().createCGImage(qrImage, from: qrImage.extent) else {
return nil
}
UIGraphicsBeginImageContext(size)
guard let context = UIGraphicsGetCurrentContext() else {
UIGraphicsEndImageContext()
return nil
}
context.interpolationQuality = CGInterpolationQuality.none
context.scaleBy(x: 1.0, y: -1.0)
context.draw(cgImage, in: context.boundingBoxOfClipPath)
let codeImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return codeImage
}
public static func createCode128(codeString: String, size: CGSize, qrColor: UIColor, bkColor: UIColor) -> UIImage? {
guard let stringData = codeString.data(using: String.Encoding.utf8),
let qrFilter = CIFilter(name: "CICode39BarcodeGenerator") else {
return nil
}
qrFilter.setDefaults()
qrFilter.setValue(stringData, forKey: "inputMessage")
guard let outputImage = qrFilter.outputImage else { return nil }
let context = CIContext()
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil }
let image = UIImage(cgImage: cgImage, scale: 1.0, orientation: .up)
// Resize without interpolating
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? {
let rect: CGRect = getConcreteCodeRectFromImage(srcCodeImage: srcCodeImage, codeResult: codeResult)
if rect.isEmpty {
return nil
}
let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect)
if let validImg = img {
let imgRotation = imageRotation(image: validImg, orientation: .right)
return imgRotation
}
return nil
}
// 根据二维码的区域截取二维码区域图像
public static func getConcreteCodeImage(srcCodeImage: UIImage, rect: CGRect) -> UIImage? {
if rect.isEmpty {
return nil
}
let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect)
if let validImg = img {
let imgRotation = imageRotation(image: validImg, orientation: .right)
return imgRotation
}
return nil
}
// 获取二维码的图像区域
public static func getConcreteCodeRectFromImage(srcCodeImage: UIImage, codeResult: LBXScanResult) -> CGRect {
guard let corners = codeResult.arrayCorner, corners.count >= 4 else {
return CGRect.zero
}
guard let corner = corners as? [[String: Float]] else {
return CGRect.zero
}
let dicTopLeft = corner[0]
let dicTopRight = corner[1]
let dicBottomRight = corner[2]
let dicBottomLeft = corner[3]
guard let xLeftTopRatio = dicTopLeft["X"],
let yLeftTopRatio = dicTopLeft["Y"],
let xRightTopRatio = dicTopRight["X"],
let yRightTopRatio = dicTopRight["Y"],
let xBottomRightRatio = dicBottomRight["X"],
let yBottomRightRatio = dicBottomRight["Y"],
let xLeftBottomRatio = dicBottomLeft["X"],
let yLeftBottomRatio = dicBottomLeft["Y"] else {
return CGRect.zero
}
// 由于截图只能矩形,所以截图不规则四边形的最大外围
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)
return rect
}
// MARK: - ---图像处理
/**
@brief 图像中间加logo图片
@param srcImg 原图像
@param LogoImage logo图像
@param logoSize logo图像尺寸
@return 加Logo的图像
*/
public static 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)
logoImg.draw(in: rect)
let resultingImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultingImage!
}
// 图像缩放
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
UIGraphicsBeginImageContext(CGSize(width: width, height: height))
guard let context = UIGraphicsGetCurrentContext() else {
UIGraphicsEndImageContext()
return nil
}
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? {
guard let imageRef = srcImg.cgImage,
let imagePartRef = imageRef.cropping(to: rect) else {
return nil
}
let cropImage = UIImage(cgImage: imagePartRef)
return cropImage
}
// 图像旋转
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
switch orientation {
case .left:
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
case .right:
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
case .down:
rotate = .pi
rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
translateX = -rect.size.width
translateY = -rect.size.height
default:
rotate = 0.0
rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
translateX = 0
translateY = 0
}
UIGraphicsBeginImageContext(rect.size)
guard let context = UIGraphicsGetCurrentContext(),
let cgImage = image.cgImage else {
UIGraphicsEndImageContext()
return image
}
// 做CTM变换
context.translateBy(x: 0.0, y: rect.size.height)
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(cgImage, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height))
let newPic = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newPic ?? image
}
deinit {
// print("LBXScanWrapper deinit")
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "qr_scan_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "qr_scan_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "scan_move_line@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "scan_move_line@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment