Commit ac21171fa6f58dd74475c90965648baf2806c343

Authored by “张旭”
1 parent 8d49f8c6

实名认证逻辑

Showing 22 changed files with 1565 additions and 3 deletions
metaCode.xcodeproj/project.pbxproj
... ... @@ -92,6 +92,13 @@
92 92 1938BD432B4E34B200390A34 /* CNHomepageDuanJuDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1938BD422B4E34B200390A34 /* CNHomepageDuanJuDetailViewController.swift */; };
93 93 1938BD452B4E49CA00390A34 /* CNLiveDuanjuTitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1938BD442B4E49CA00390A34 /* CNLiveDuanjuTitleCell.swift */; };
94 94 1938BD472B4FD5A500390A34 /* CNLiveIntegralAlertTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1938BD462B4FD5A500390A34 /* CNLiveIntegralAlertTool.swift */; };
  95 + 194134782C17F83B004FC047 /* CNLiveFaceAuthController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194134772C17F83B004FC047 /* CNLiveFaceAuthController.swift */; };
  96 + 1941347A2C18001A004FC047 /* CGImage+FaceCrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194134792C18001A004FC047 /* CGImage+FaceCrop.swift */; };
  97 + 1941347C2C180030004FC047 /* UIImage_extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1941347B2C18002F004FC047 /* UIImage_extension.swift */; };
  98 + 1941347E2C1815E7004FC047 /* AVExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1941347D2C1815E7004FC047 /* AVExtensions.swift */; };
  99 + 194134802C18160C004FC047 /* CNLiveAuthPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1941347F2C18160C004FC047 /* CNLiveAuthPreviewView.swift */; };
  100 + 194134842C193B17004FC047 /* CNLiveDigitalAuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194134832C193B17004FC047 /* CNLiveDigitalAuthViewModel.swift */; };
  101 + 194134862C196F59004FC047 /* CNLiveAuthTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194134852C196F59004FC047 /* CNLiveAuthTool.swift */; };
95 102 194455A12B6B3310006C5A2A /* CNWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194455A02B6B3310006C5A2A /* CNWebView.swift */; };
96 103 19565EC02AF4DE0E007D6727 /* CNLiveShopDetailBtnsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19565EBF2AF4DE0E007D6727 /* CNLiveShopDetailBtnsCell.swift */; };
97 104 19565EC22AF616CA007D6727 /* CNLiveShopDetailOrderAssetCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19565EC12AF616CA007D6727 /* CNLiveShopDetailOrderAssetCell.swift */; };
... ... @@ -383,6 +390,13 @@
383 390 1938BD422B4E34B200390A34 /* CNHomepageDuanJuDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNHomepageDuanJuDetailViewController.swift; sourceTree = "<group>"; };
384 391 1938BD442B4E49CA00390A34 /* CNLiveDuanjuTitleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNLiveDuanjuTitleCell.swift; sourceTree = "<group>"; };
385 392 1938BD462B4FD5A500390A34 /* CNLiveIntegralAlertTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNLiveIntegralAlertTool.swift; sourceTree = "<group>"; };
  393 + 194134772C17F83B004FC047 /* CNLiveFaceAuthController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNLiveFaceAuthController.swift; sourceTree = "<group>"; };
  394 + 194134792C18001A004FC047 /* CGImage+FaceCrop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGImage+FaceCrop.swift"; sourceTree = "<group>"; };
  395 + 1941347B2C18002F004FC047 /* UIImage_extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImage_extension.swift; sourceTree = "<group>"; };
  396 + 1941347D2C1815E7004FC047 /* AVExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AVExtensions.swift; sourceTree = "<group>"; };
  397 + 1941347F2C18160C004FC047 /* CNLiveAuthPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CNLiveAuthPreviewView.swift; sourceTree = "<group>"; };
  398 + 194134832C193B17004FC047 /* CNLiveDigitalAuthViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNLiveDigitalAuthViewModel.swift; sourceTree = "<group>"; };
  399 + 194134852C196F59004FC047 /* CNLiveAuthTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNLiveAuthTool.swift; sourceTree = "<group>"; };
386 400 194455A02B6B3310006C5A2A /* CNWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNWebView.swift; sourceTree = "<group>"; };
387 401 19565EBF2AF4DE0E007D6727 /* CNLiveShopDetailBtnsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNLiveShopDetailBtnsCell.swift; sourceTree = "<group>"; };
388 402 19565EC12AF616CA007D6727 /* CNLiveShopDetailOrderAssetCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CNLiveShopDetailOrderAssetCell.swift; sourceTree = "<group>"; };
... ... @@ -937,6 +951,25 @@
937 951 path = VerticalViews;
938 952 sourceTree = "<group>";
939 953 };
  954 + 194134812C181A7E004FC047 /* DigitalAuth */ = {
  955 + isa = PBXGroup;
  956 + children = (
  957 + 1941347D2C1815E7004FC047 /* AVExtensions.swift */,
  958 + 1941347B2C18002F004FC047 /* UIImage_extension.swift */,
  959 + 194134792C18001A004FC047 /* CGImage+FaceCrop.swift */,
  960 + 194134852C196F59004FC047 /* CNLiveAuthTool.swift */,
  961 + );
  962 + path = DigitalAuth;
  963 + sourceTree = "<group>";
  964 + };
  965 + 194134822C193AF0004FC047 /* DigitalAuth */ = {
  966 + isa = PBXGroup;
  967 + children = (
  968 + 194134832C193B17004FC047 /* CNLiveDigitalAuthViewModel.swift */,
  969 + );
  970 + path = DigitalAuth;
  971 + sourceTree = "<group>";
  972 + };
940 973 19565EC32AF8ACC2007D6727 /* GoodsManager */ = {
941 974 isa = PBXGroup;
942 975 children = (
... ... @@ -1058,6 +1091,7 @@
1058 1091 children = (
1059 1092 19C0F7732BDF71CA0097CC8F /* CNLiveDigitalAuthController.swift */,
1060 1093 19C735CD2C12D19D00CACF4E /* CNLiveRealNameAuthController.swift */,
  1094 + 194134772C17F83B004FC047 /* CNLiveFaceAuthController.swift */,
1061 1095 );
1062 1096 path = DigitalAuth;
1063 1097 sourceTree = "<group>";
... ... @@ -1076,6 +1110,7 @@
1076 1110 19C735C92C10647B00CACF4E /* CNLiveDigitalAuthCell.swift */,
1077 1111 19C735CB2C11A1F400CACF4E /* CNLiveDigitalThreeCollectCell.swift */,
1078 1112 19C735CF2C12F5F600CACF4E /* CNLiveRealNameCell.swift */,
  1113 + 1941347F2C18160C004FC047 /* CNLiveAuthPreviewView.swift */,
1079 1114 );
1080 1115 path = DigitalAuthViews;
1081 1116 sourceTree = "<group>";
... ... @@ -1083,6 +1118,7 @@
1083 1118 19CC4D502ADD126D00308E2A /* Util */ = {
1084 1119 isa = PBXGroup;
1085 1120 children = (
  1121 + 194134812C181A7E004FC047 /* DigitalAuth */,
1086 1122 1917330B2B109691007BCC04 /* CNLivePlayer */,
1087 1123 19CC4D512ADD129800308E2A /* CNUpSlideAnimationTransition.swift */,
1088 1124 19D2CE622AE8B65D00691757 /* CNLiveDownSlideTool.swift */,
... ... @@ -1502,6 +1538,7 @@
1502 1538 7C83B43C2A52B899004BF4A4 /* ViewModel */ = {
1503 1539 isa = PBXGroup;
1504 1540 children = (
  1541 + 194134822C193AF0004FC047 /* DigitalAuth */,
1505 1542 191733462B2855D6007BCC04 /* ShopCoupon */,
1506 1543 191732F62B08AAD4007BCC04 /* ShopClassify */,
1507 1544 19565ED12AF9FE56007D6727 /* GoodsManager */,
... ... @@ -1828,6 +1865,7 @@
1828 1865 1938BD2E2B48021E00390A34 /* CNHomePageBaseNavigationController.swift in Sources */,
1829 1866 1917332F2B16C7C9007BCC04 /* CNLiveTimer.swift in Sources */,
1830 1867 7CD675292A270F6A008354C1 /* WJColorChange.m in Sources */,
  1868 + 194134862C196F59004FC047 /* CNLiveAuthTool.swift in Sources */,
1831 1869 1938BD172B3D6C7F00390A34 /* CNLiveAuthViewController.swift in Sources */,
1832 1870 19864E412BF6090400586E72 /* CNLiveDuanJuDetailCell.swift in Sources */,
1833 1871 79F4B11C2A33036E00993C03 /* CNLiveMineInfoAlertView.swift in Sources */,
... ... @@ -1862,6 +1900,7 @@
1862 1900 1917333C2B27FE75007BCC04 /* CNLiveShopClassilyManageHeaderView.swift in Sources */,
1863 1901 191733252B146078007BCC04 /* CNLivePlayerSlider.swift in Sources */,
1864 1902 19BB202B2AF382150094D962 /* CNLiveAdaptTool.swift in Sources */,
  1903 + 1941347C2C180030004FC047 /* UIImage_extension.swift in Sources */,
1865 1904 79E672032AFDFA9C00F2F768 /* CNLiveMineSignOutViewController.swift in Sources */,
1866 1905 19565ECD2AF8D256007D6727 /* CNLiveShopGoodsListClassifyView.swift in Sources */,
1867 1906 19E302672BC8FEBC00F6B8BD /* CNLliveShopDetailCloseCell.swift in Sources */,
... ... @@ -1879,6 +1918,7 @@
1879 1918 7C83B4442A541F36004BF4A4 /* NSDate+CNLiveExtensions.swift in Sources */,
1880 1919 1917332B2B15CDC6007BCC04 /* CNLivePlayerLandscapeViewController.swift in Sources */,
1881 1920 798916F12B0491A400D08302 /* CNLiveShopBodyTransListView.swift in Sources */,
  1921 + 194134802C18160C004FC047 /* CNLiveAuthPreviewView.swift in Sources */,
1882 1922 7CD675482A274167008354C1 /* PGIndexBannerSubiew.m in Sources */,
1883 1923 191733562B2FE855007BCC04 /* CNLivePickerReusingModel.swift in Sources */,
1884 1924 790D019F2A2DD66E00C5978D /* CNLiveLoginViewModel.swift in Sources */,
... ... @@ -1977,6 +2017,7 @@
1977 2017 191733112B109EDC007BCC04 /* CNLivePlayerControlView.swift in Sources */,
1978 2018 1917331F2B10A1BE007BCC04 /* CNLivePlayerEnum.swift in Sources */,
1979 2019 191733382B27FD66007BCC04 /* CNLiveShopClassifySectionHeaderView.swift in Sources */,
  2020 + 194134782C17F83B004FC047 /* CNLiveFaceAuthController.swift in Sources */,
1980 2021 1938BD222B467C1700390A34 /* CNLivePlayletHomePageCell.swift in Sources */,
1981 2022 1938BD3F2B4BE7C500390A34 /* CNLiveAlbumMsgModel.swift in Sources */,
1982 2023 798A44FF2A30A0A800815935 /* CNLiveInputFeedBackViewController.swift in Sources */,
... ... @@ -1990,6 +2031,7 @@
1990 2031 796308752B4B90700090D22A /* CNLiveShortVideoHomeModel.swift in Sources */,
1991 2032 19CC4D542ADD163D00308E2A /* CNUpSlideCardPresentationController.swift in Sources */,
1992 2033 790D01A32A2DDBA300C5978D /* CNLiveConfigManager.swift in Sources */,
  2034 + 1941347A2C18001A004FC047 /* CGImage+FaceCrop.swift in Sources */,
1993 2035 7939B0752B47F1B9005AE981 /* CNLiveShortVideoListTableViewCell.swift in Sources */,
1994 2036 794294A72B4D18EC008AAFA0 /* CNLiveShortVideoContentListView.swift in Sources */,
1995 2037 19565EC52AF8AD1F007D6727 /* CNLiveShopProductManagerController.swift in Sources */,
... ... @@ -1997,6 +2039,7 @@
1997 2039 796308732B4B8EB80090D22A /* CNLiveShortVideoViewModel.swift in Sources */,
1998 2040 19E9D1602B3A98C200D0FB30 /* CNLiveShopAssociatedGoodsTableViewCell.swift in Sources */,
1999 2041 191732CE2B04AACD007BCC04 /* CNLiveShopTextView.swift in Sources */,
  2042 + 194134842C193B17004FC047 /* CNLiveDigitalAuthViewModel.swift in Sources */,
2000 2043 7C2703332A36CC660061EC50 /* CNLiveGlobalBlock.swift in Sources */,
2001 2044 7987BF3D2A497644000A1ABC /* CNLiveMineQuestViewController.swift in Sources */,
2002 2045 798A44EA2A30135D00815935 /* CNLiveMineBodyListView.swift in Sources */,
... ... @@ -2047,6 +2090,7 @@
2047 2090 19C0F7742BDF71CA0097CC8F /* CNLiveDigitalAuthController.swift in Sources */,
2048 2091 798A44EE2A305AAC00815935 /* CNLiveBaseNavigationController.swift in Sources */,
2049 2092 1938BD412B4BEC6900390A34 /* CNLiveAlbumListModel.swift in Sources */,
  2093 + 1941347E2C1815E7004FC047 /* AVExtensions.swift in Sources */,
2050 2094 7CD675422A2740F8008354C1 /* CNCustomIndexView.swift in Sources */,
2051 2095 191733292B148BF7007BCC04 /* CNLivePlayerVolumeBrightnessView.swift in Sources */,
2052 2096 7C4931292A2D8BA8000E44B4 /* CNLiveBaseTabBarViewController.swift in Sources */,
... ...
metaCode/Classes/Main/HomePage/Controller/DigitalAuth/CNLiveFaceAuthController.swift 0 → 100644
  1 +//
  2 +// CNLiveFaceAuthController.swift
  3 +// metaCode
  4 +//
  5 +// Created by zx on 2024/6/11.
  6 +// 人脸识别页面
  7 +
  8 +import Foundation
  9 +import SnapKit
  10 +import QMUIKit
  11 +import UIKit
  12 +import AVFoundation
  13 +import Vision
  14 +
  15 +class CNLiveFaceAuthController:CNLiveBaseViewController{
  16 +
  17 + lazy var previewView :CNLiveAuthPreviewView = {
  18 + let previewView = CNLiveAuthPreviewView(frame: CGRect(x: 0, y: KNavigationHeight, width: KSreenWidth, height: KSreenHeight-KNavigationHeight))
  19 + return previewView
  20 + }()
  21 +
  22 + // VNRequest: Either Retangles or Landmarks
  23 + private var faceDetectionRequest: VNRequest!
  24 +
  25 + // TODO: Decide camera position --- front or back
  26 + private var devicePosition: AVCaptureDevice.Position = .front
  27 +
  28 + // Session Management
  29 + private enum SessionSetupResult {
  30 + case success
  31 + case notAuthorized
  32 + case configurationFailed
  33 + }
  34 +
  35 + private let session = AVCaptureSession()
  36 + private var isSessionRunning = false
  37 +
  38 + // Communicate with the session and other session objects on this queue.
  39 + private let sessionQueue = DispatchQueue(label: "session queue", attributes: [], target: nil)
  40 +
  41 + private var setupResult: SessionSetupResult = .success
  42 +
  43 + private var videoDeviceInput: AVCaptureDeviceInput!
  44 +
  45 + private var videoDataOutput: AVCaptureVideoDataOutput!
  46 + private var videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue")
  47 +
  48 + private var requests = [VNRequest]()
  49 +
  50 + private let circleWidth:CGFloat = 250
  51 +
  52 + var realName:String = ""
  53 + var realNumber:String = ""
  54 +
  55 + lazy var circleBgView :UIView = {
  56 + let circleBgView = UIView(frame: self.previewView.bounds)
  57 + circleBgView.backgroundColor = .white
  58 + return circleBgView
  59 + }()
  60 + lazy var circleBgImgView :UIImageView = {
  61 + let iconImgView = UIImageView()
  62 + iconImgView.image = UIImage(named: "auth_faceAuth")
  63 + return iconImgView
  64 + }()
  65 + lazy var descTitleLabel: UILabel = {
  66 + let titleLabel = UILabel()
  67 + titleLabel.textColor = HexColor("#333333")
  68 + titleLabel.textAlignment = .center
  69 + titleLabel.numberOfLines = 2
  70 + titleLabel.font = Font_18
  71 + return titleLabel
  72 + }()
  73 + lazy var yanzhengBtn: UIButton = {
  74 + let nextBtn = UIButton(type: .custom)
  75 + nextBtn.setTitle("立即验证", for: .normal)
  76 + nextBtn.setTitleColor(.white, for: .normal)
  77 + nextBtn.backgroundColor = HexColor("#1961FE")
  78 + nextBtn.layer.cornerRadius = 22
  79 + nextBtn.clipsToBounds = true
  80 + nextBtn.addTarget(self, action: #selector(yanzhengBtnAction), for: .touchUpInside)
  81 + return nextBtn
  82 + }()
  83 +
  84 + lazy var xieyiLabel: UILabel = {
  85 + let label = UILabel()
  86 + let attrString = NSMutableAttributedString(string: "查看《人脸识别用户信息采集协议》")
  87 + label.frame = CGRect(x: 91.5, y: 558, width: 192, height: 16.5)
  88 + label.numberOfLines = 0
  89 + let attr: [NSAttributedString.Key : Any] = [.font: Font_12,.foregroundColor: UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)]
  90 + attrString.addAttributes(attr, range: NSRange(location: 0, length: attrString.length))
  91 + view.addSubview(label)
  92 + let strSubAttr1: [NSMutableAttributedString.Key: Any] = [.font: Font_12,.foregroundColor: UIColor(red: 0.2, green: 0.2, blue: 0.2,alpha:1.000000)]
  93 + attrString.addAttributes(strSubAttr1, range: NSRange(location: 0, length: 2))
  94 + let strSubAttr2: [NSMutableAttributedString.Key: Any] = [.font: Font_12,.foregroundColor: UIColor(red: 0.1, green: 0.38, blue: 1,alpha:1.000000)]
  95 + attrString.addAttributes(strSubAttr2, range: NSRange(location: 2, length: 14))
  96 + label.attributedText = attrString
  97 + label.textAlignment = .center
  98 + label.isUserInteractionEnabled = true
  99 + label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(xieyiAction)))
  100 + return label
  101 + }()
  102 +
  103 + //是否上传图片中
  104 + var isUploadingImage:Bool = false
  105 +
  106 + override func viewDidLoad() {
  107 + super.viewDidLoad()
  108 +
  109 + navigationBarHidden = false
  110 + titleName = "元码数字认证"
  111 +
  112 + self.view.addSubview(self.previewView)
  113 + // Set up the video preview view.
  114 + previewView.session = session
  115 +
  116 + // Set up Vision Request
  117 + faceDetectionRequest = VNDetectFaceRectanglesRequest(completionHandler: self.handleFaces) // Default
  118 +
  119 + setupVision()
  120 +
  121 + /*
  122 + Check video authorization status. Video access is required and audio
  123 + access is optional. If audio access is denied, audio is not recorded
  124 + during movie recording.
  125 + */
  126 + switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video){
  127 + case .authorized:
  128 + // The user has previously granted access to the camera.
  129 + break
  130 +
  131 + case .notDetermined:
  132 + /*
  133 + The user has not yet been presented with the option to grant
  134 + video access. We suspend the session queue to delay session
  135 + setup until the access request has completed.
  136 + */
  137 + sessionQueue.suspend()
  138 + AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { [unowned self] granted in
  139 + if !granted {
  140 + self.setupResult = .notAuthorized
  141 + }
  142 + self.sessionQueue.resume()
  143 + })
  144 +
  145 +
  146 + default:
  147 + // The user has previously denied access.
  148 + setupResult = .notAuthorized
  149 + }
  150 +
  151 + /*
  152 + Setup the capture session.
  153 + In general it is not safe to mutate an AVCaptureSession or any of its
  154 + inputs, outputs, or connections from multiple threads at the same time.
  155 +
  156 + Why not do all of this on the main queue?
  157 + Because AVCaptureSession.startRunning() is a blocking call which can
  158 + take a long time. We dispatch session setup to the sessionQueue so
  159 + that the main queue isn't blocked, which keeps the UI responsive.
  160 + */
  161 +
  162 + sessionQueue.async { [unowned self] in
  163 + self.configureSession()
  164 + }
  165 +
  166 + self.previewView.addSubview(self.circleBgView)
  167 + //通过设置mask透明区域来实现镂空效果
  168 + let path:UIBezierPath = UIBezierPath.init(rect: UIScreen.main.bounds)
  169 + //rect 镂空的区域
  170 + let rect = CGRectMake((self.view.bounds.width-circleWidth)/2, 90,circleWidth, circleWidth)
  171 + let appendPath: UIBezierPath = UIBezierPath.init(roundedRect: rect, cornerRadius: circleWidth/2)
  172 + path.append(appendPath.reversing())
  173 + let shapeLayer: CAShapeLayer = CAShapeLayer()
  174 + shapeLayer.path = path.cgPath;
  175 + self.circleBgView.layer.mask = shapeLayer
  176 +
  177 + self.circleBgImgView.frame = rect
  178 + self.previewView.addSubview(self.circleBgImgView)
  179 +
  180 + self.previewView.addSubview(self.descTitleLabel)
  181 + self.descTitleLabel.text = String(format: "验证 %@ 的人脸完成身份认证, 以继续使用服务", self.realName)
  182 + self.descTitleLabel.snp.makeConstraints { make in
  183 + make.top.equalTo(self.circleBgImgView.snp.bottom).offset(15)
  184 + make.left.equalTo(15)
  185 + make.right.equalToSuperview().offset(-15)
  186 + make.height.equalTo(50)
  187 + }
  188 +
  189 + self.previewView.addSubview(self.yanzhengBtn)
  190 + self.yanzhengBtn.snp.makeConstraints { make in
  191 + make.top.equalTo(self.descTitleLabel.snp.bottom).offset(90)
  192 + make.height.equalTo(44)
  193 + make.left.equalTo(30)
  194 + make.right.equalToSuperview().offset(-30)
  195 + }
  196 +
  197 + self.previewView.addSubview(self.xieyiLabel)
  198 + self.xieyiLabel.snp.makeConstraints { make in
  199 + make.top.equalTo(self.yanzhengBtn.snp.bottom).offset(15)
  200 + make.height.equalTo(17)
  201 + make.left.equalTo(30)
  202 + make.right.equalToSuperview().offset(-30)
  203 + }
  204 +
  205 + }
  206 +
  207 + override func viewWillAppear(_ animated: Bool) {
  208 + super.viewWillAppear(animated)
  209 +
  210 + sessionQueue.async { [unowned self] in
  211 + switch self.setupResult {
  212 + case .success:
  213 + // Only setup observers and start the session running if setup succeeded.
  214 + self.addObservers()
  215 + self.session.startRunning()
  216 + self.isSessionRunning = self.session.isRunning
  217 +
  218 + case .notAuthorized:
  219 + DispatchQueue.main.async { [unowned self] in
  220 + let message = NSLocalizedString("AVCamBarcode doesn't have permission to use the camera, please change privacy settings", comment: "Alert message when the user has denied access to the camera")
  221 + let alertController = UIAlertController(title: "AppleFaceDetection", message: message, preferredStyle: .alert)
  222 + alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil))
  223 + alertController.addAction(UIAlertAction(title: NSLocalizedString("Settings", comment: "Alert button to open Settings"), style: .`default`, handler: { action in
  224 + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
  225 + }))
  226 +
  227 + self.present(alertController, animated: true, completion: nil)
  228 + }
  229 +
  230 + case .configurationFailed:
  231 + DispatchQueue.main.async { [unowned self] in
  232 + let message = NSLocalizedString("Unable to capture media", comment: "Alert message when something goes wrong during capture session configuration")
  233 + let alertController = UIAlertController(title: "AppleFaceDetection", message: message, preferredStyle: .alert)
  234 + alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil))
  235 +
  236 + self.present(alertController, animated: true, completion: nil)
  237 + }
  238 + }
  239 + }
  240 + }
  241 +
  242 + override func viewWillDisappear(_ animated: Bool) {
  243 + sessionQueue.async { [unowned self] in
  244 + if self.setupResult == .success {
  245 + self.session.stopRunning()
  246 + self.isSessionRunning = self.session.isRunning
  247 + self.removeObservers()
  248 + }
  249 + }
  250 +
  251 + super.viewWillDisappear(animated)
  252 + }
  253 +
  254 + @objc func yanzhengBtnAction(){
  255 + self.circleBgImgView.isHidden = true
  256 + }
  257 +
  258 + @objc func xieyiAction(){
  259 + let webVC = CNWebViewController()
  260 + webVC.url = URL(string: "https://wjj.ys1.cnliveimg.com/term/ymRlsbyhxxcjxy.html")
  261 + self.navigationController?.pushViewController(webVC, animated: true)
  262 +
  263 + }
  264 +
  265 + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
  266 + super.viewWillTransition(to: size, with: coordinator)
  267 +
  268 + if let videoPreviewLayerConnection = previewView.videoPreviewLayer.connection {
  269 + let deviceOrientation = UIDevice.current.orientation
  270 + guard let newVideoOrientation = deviceOrientation.videoOrientation, deviceOrientation.isPortrait || deviceOrientation.isLandscape else {
  271 + return
  272 + }
  273 +
  274 + videoPreviewLayerConnection.videoOrientation = newVideoOrientation
  275 +
  276 + }
  277 + }
  278 +
  279 + // Segmente Control to switch over FaceOnly or FaceLandmark
  280 + func UpdateDetectionType(_ sender: UISegmentedControl) {
  281 + faceDetectionRequest = sender.selectedSegmentIndex == 0 ? VNDetectFaceRectanglesRequest(completionHandler: handleFaces) : VNDetectFaceLandmarksRequest(completionHandler: handleFaceLandmarks)
  282 +
  283 + setupVision()
  284 + }
  285 +
  286 + //zx
  287 + var screenImage:UIImage?
  288 + lazy var imageView :UIImageView = {
  289 + let imageView = UIImageView()
  290 + return imageView
  291 + }()
  292 +// lazy var faceImageView :UIImageView = {
  293 +// let imageView = UIImageView()
  294 +// return imageView
  295 +// }()
  296 +
  297 + //zx
  298 + var tempPixelBuffer:CVImageBuffer?
  299 +
  300 +
  301 + func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage? {
  302 + guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
  303 + return nil
  304 + }
  305 +
  306 + // 锁定像素缓冲区基地址
  307 + CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
  308 +
  309 + // 获取像素缓冲区的宽度和高度
  310 + let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
  311 + let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
  312 + let width = CVPixelBufferGetWidth(imageBuffer)
  313 + let height = CVPixelBufferGetHeight(imageBuffer)
  314 +
  315 + // 创建CIImage
  316 + let colorSpace = CGColorSpaceCreateDeviceRGB()
  317 + let bitmapInfo: CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
  318 + let context = CGContext(
  319 + data: baseAddress,
  320 + width: width,
  321 + height: height,
  322 + bitsPerComponent: 8,
  323 + bytesPerRow: bytesPerRow,
  324 + space: colorSpace,
  325 + bitmapInfo: bitmapInfo.rawValue
  326 + )
  327 +
  328 + guard let cgImage = context?.makeImage() else {
  329 + CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
  330 + return nil
  331 + }
  332 +
  333 + // 解锁像素缓冲区基地址
  334 + CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
  335 +
  336 + // 创建UIImage
  337 + let image = UIImage(cgImage: cgImage)
  338 + return image
  339 + }
  340 + func takeScreenshot(view: UIView? = nil) -> UIImage? {
  341 + // 获取屏幕截图
  342 + let bounds = UIScreen.main.bounds
  343 + let scale = UIScreen.main.scale
  344 +
  345 + // 开始图形上下文
  346 + UIGraphicsBeginImageContextWithOptions(bounds.size, false, scale)
  347 +
  348 + // 如果没有指定view,则截取整个屏幕
  349 + if let context = UIGraphicsGetCurrentContext() {
  350 + if let view = view {
  351 + // 截取view的截图
  352 + view.layer.render(in: context)
  353 + } else {
  354 + // 截取整个屏幕的截图
  355 + UIApplication.shared.keyWindow?.layer.render(in: context)
  356 + }
  357 +
  358 + // 从图形上下文获取图片
  359 + let image = UIGraphicsGetImageFromCurrentImageContext()
  360 +
  361 + // 结束图形上下文
  362 + UIGraphicsEndImageContext()
  363 +
  364 + return image
  365 + }
  366 +
  367 + return nil
  368 + }
  369 +}
  370 +
  371 +// Video Sessions
  372 +extension CNLiveFaceAuthController {
  373 + private func configureSession() {
  374 + if setupResult != .success { return }
  375 +
  376 + session.beginConfiguration()
  377 + session.sessionPreset = .high
  378 +
  379 + // Add video input.
  380 + addVideoDataInput()
  381 +
  382 + // Add video output.
  383 + addVideoDataOutput()
  384 +
  385 + session.commitConfiguration()
  386 +
  387 + }
  388 +
  389 + private func addVideoDataInput() {
  390 + do {
  391 + var defaultVideoDevice: AVCaptureDevice!
  392 +
  393 + if devicePosition == .front {
  394 + if let frontCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .front) {
  395 + defaultVideoDevice = frontCameraDevice
  396 + }
  397 + }
  398 + else {
  399 + // Choose the back dual camera if available, otherwise default to a wide angle camera.
  400 + if let dualCameraDevice = AVCaptureDevice.default(.builtInDualCamera, for: AVMediaType.video, position: .back) {
  401 + defaultVideoDevice = dualCameraDevice
  402 + }
  403 +
  404 + else if let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back) {
  405 + defaultVideoDevice = backCameraDevice
  406 + }
  407 + }
  408 +
  409 +
  410 + let videoDeviceInput = try AVCaptureDeviceInput(device: defaultVideoDevice!)
  411 +
  412 + if session.canAddInput(videoDeviceInput) {
  413 + session.addInput(videoDeviceInput)
  414 + self.videoDeviceInput = videoDeviceInput
  415 + DispatchQueue.main.async {
  416 + /*
  417 + Why are we dispatching this to the main queue?
  418 + Because AVCaptureVideoPreviewLayer is the backing layer for PreviewView and UIView
  419 + can only be manipulated on the main thread.
  420 + Note: As an exception to the above rule, it is not necessary to serialize video orientation changes
  421 + on the AVCaptureVideoPreviewLayer’s connection with other session manipulation.
  422 +
  423 + Use the status bar orientation as the initial video orientation. Subsequent orientation changes are
  424 + handled by CameraViewController.viewWillTransition(to:with:).
  425 + */
  426 + let statusBarOrientation = UIApplication.shared.statusBarOrientation
  427 + var initialVideoOrientation: AVCaptureVideoOrientation = .portrait
  428 + if statusBarOrientation != .unknown {
  429 + if let videoOrientation = statusBarOrientation.videoOrientation {
  430 + initialVideoOrientation = videoOrientation
  431 + }
  432 + }
  433 + self.previewView.videoPreviewLayer.connection!.videoOrientation = initialVideoOrientation
  434 + }
  435 + }
  436 +
  437 + }
  438 + catch {
  439 + print("Could not add video device input to the session")
  440 + setupResult = .configurationFailed
  441 + session.commitConfiguration()
  442 + return
  443 + }
  444 + }
  445 +
  446 + private func addVideoDataOutput() {
  447 + videoDataOutput = AVCaptureVideoDataOutput()
  448 + videoDataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as String): Int(kCVPixelFormatType_32BGRA)]
  449 +
  450 +
  451 + if session.canAddOutput(videoDataOutput) {
  452 + videoDataOutput.alwaysDiscardsLateVideoFrames = true
  453 + videoDataOutput.setSampleBufferDelegate(self, queue: videoDataOutputQueue)
  454 + session.addOutput(videoDataOutput)
  455 + }
  456 + else {
  457 + print("Could not add metadata output to the session")
  458 + setupResult = .configurationFailed
  459 + session.commitConfiguration()
  460 + return
  461 + }
  462 + }
  463 +}
  464 +
  465 +// MARK: -- Observers and Event Handlers
  466 +extension CNLiveFaceAuthController {
  467 + private func addObservers() {
  468 + /*
  469 + Observe the previewView's regionOfInterest to update the AVCaptureMetadataOutput's
  470 + rectOfInterest when the user finishes resizing the region of interest.
  471 + */
  472 + NotificationCenter.default.addObserver(self, selector: #selector(sessionRuntimeError), name: Notification.Name("AVCaptureSessionRuntimeErrorNotification"), object: session)
  473 +
  474 + /*
  475 + A session can only run when the app is full screen. It will be interrupted
  476 + in a multi-app layout, introduced in iOS 9, see also the documentation of
  477 + AVCaptureSessionInterruptionReason. Add observers to handle these session
  478 + interruptions and show a preview is paused message. See the documentation
  479 + of AVCaptureSessionWasInterruptedNotification for other interruption reasons.
  480 + */
  481 + NotificationCenter.default.addObserver(self, selector: #selector(sessionWasInterrupted), name: Notification.Name("AVCaptureSessionWasInterruptedNotification"), object: session)
  482 + NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionEnded), name: Notification.Name("AVCaptureSessionInterruptionEndedNotification"), object: session)
  483 + }
  484 +
  485 + private func removeObservers() {
  486 + NotificationCenter.default.removeObserver(self)
  487 + }
  488 +
  489 + @objc func sessionRuntimeError(_ notification: Notification) {
  490 + guard let errorValue = notification.userInfo?[AVCaptureSessionErrorKey] as? NSError else { return }
  491 +
  492 + let error = AVError(_nsError: errorValue)
  493 + print("Capture session runtime error: \(error)")
  494 +
  495 + /*
  496 + Automatically try to restart the session running if media services were
  497 + reset and the last start running succeeded. Otherwise, enable the user
  498 + to try to resume the session running.
  499 + */
  500 + if error.code == .mediaServicesWereReset {
  501 + sessionQueue.async { [unowned self] in
  502 + if self.isSessionRunning {
  503 + self.session.startRunning()
  504 + self.isSessionRunning = self.session.isRunning
  505 + }
  506 + }
  507 + }
  508 + }
  509 +
  510 + @objc func sessionWasInterrupted(_ notification: Notification) {
  511 + /*
  512 + In some scenarios we want to enable the user to resume the session running.
  513 + For example, if music playback is initiated via control center while
  514 + using AVCamBarcode, then the user can let AVCamBarcode resume
  515 + the session running, which will stop music playback. Note that stopping
  516 + music playback in control center will not automatically resume the session
  517 + running. Also note that it is not always possible to resume, see `resumeInterruptedSession(_:)`.
  518 + */
  519 + if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?, let reasonIntegerValue = userInfoValue.integerValue, let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {
  520 + print("Capture session was interrupted with reason \(reason)")
  521 + }
  522 + }
  523 +
  524 + @objc func sessionInterruptionEnded(_ notification: Notification) {
  525 + print("Capture session interruption ended")
  526 + }
  527 +}
  528 +
  529 +// MARK: -- Helpers
  530 +extension CNLiveFaceAuthController {
  531 + func setupVision() {
  532 + self.requests = [faceDetectionRequest]
  533 + }
  534 +
  535 + func handleFaces(request: VNRequest, error: Error?) {
  536 + DispatchQueue.main.async {
  537 + //perform all the UI updates on the main queue
  538 + guard let results = request.results as? [VNFaceObservation] else { return }
  539 + self.previewView.removeMask()
  540 +
  541 + //zx
  542 + let ciImage = CIImage(cvPixelBuffer: self.tempPixelBuffer!)
  543 + let context = CIContext(options: nil)
  544 + if let cgImage = context.createCGImage(ciImage, from: ciImage.extent) {
  545 + let uiImage = UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: .leftMirrored)
  546 +
  547 + for face in results {
  548 +// self.previewView.drawFaceboundingBox(face: face)
  549 +
  550 +
  551 + //zx
  552 + let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -self.previewView.frame.height)
  553 + let translate = CGAffineTransform.identity.scaledBy(x: self.previewView.frame.width, y: self.previewView.frame.height)
  554 + // The coordinates are normalized to the dimensions of the processed image, with the origin at the image's lower-left corner.
  555 + let facebounds = face.boundingBox.applying(translate).applying(transform)
  556 + print("11111facebounds:\(facebounds)")
  557 +
  558 + //是否在镂空区域内
  559 + let circleRect = CGRectMake((self.view.bounds.width-self.circleWidth)/2, 90,self.circleWidth, self.circleWidth)
  560 + if circleRect.contains(facebounds) {
  561 + print("人脸在圆形区域内")
  562 + if self.isUploadingImage == false{
  563 +
  564 + self.cropImageAndUpload(uiImage: uiImage, facebounds: facebounds, cgImage: cgImage)
  565 + }
  566 + } else {
  567 + print("人脸不在圆形区域内")
  568 + }
  569 +
  570 + //眨眼部分逻辑
  571 + /*
  572 + let landmarkRequest = VNDetectFaceLandmarksRequest { (request, error) in
  573 + guard let results = request.results as? [VNFaceObservation],
  574 + let faceObservation = results.first,
  575 + let landmarks = faceObservation.landmarks else { return }
  576 + faceObservation.landmarks?.rightEye?.normalizedPoints
  577 + let leftEyePoints = landmarks.leftEye
  578 + let rightEyePoints = landmarks.rightEye
  579 +
  580 + // 简化处理:假设每个眼睛都有两个关键点,分别代表眼睛的左上角和右上角
  581 + if let leftEyeTop = leftEyePoints?.normalizedPoints.first,let leftEyeBottom = leftEyePoints?.normalizedPoints.last
  582 + ,let rightEyeTop = rightEyePoints?.normalizedPoints.first
  583 + ,let rightEyeBottom = rightEyePoints?.normalizedPoints.last{
  584 +
  585 +
  586 + let leftEyeHeight = leftEyeBottom.x - leftEyeTop.x
  587 + let leftEyeWidth = leftEyeBottom.y - leftEyeTop.y
  588 + print("11111leftH:\(leftEyeHeight)")
  589 + print("11111leftW:\(leftEyeWidth)")
  590 + // 计算眼睛之间的距离(简化为两个眼睛中心点之间的距离)
  591 + let leftEyeCenter = CGPoint(x: (leftEyeTop.x + leftEyeBottom.x) / 2, y: (leftEyeTop.y + leftEyeBottom.y) / 2)
  592 + let rightEyeCenter = CGPoint(x: (rightEyeTop.x + rightEyeBottom.x) / 2, y: (rightEyeTop.y + rightEyeBottom.y) / 2)
  593 + let eyeDistance = hypot(rightEyeCenter.x - leftEyeCenter.x, rightEyeCenter.y - leftEyeCenter.y)
  594 +
  595 +
  596 + // 你需要维护一个状态来跟踪之前帧的眼睛距离
  597 + // 如果eyeDistance突然变小并且小于某个阈值,则认为发生了眨眼
  598 + // 这里需要你自己实现逻辑来保存和比较之前的距离
  599 + print("11111eyeDistance:\(eyeDistance)")
  600 + // 示例逻辑(需要完善)
  601 +// if eyeDistance < someBlinkThreshold {
  602 +// // 检测到眨眼
  603 +// }
  604 + }
  605 + }
  606 + // 使用Vision框架处理图像并传入landmarkRequest
  607 + try? VNImageRequestHandler(ciImage: ciImage, options: [:]).perform([landmarkRequest])
  608 + */
  609 +
  610 +// self.view.addSubview(self.imageView)
  611 +// self.imageView.isHidden = true
  612 +// self.imageView.image = uiImage
  613 +// self.imageView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height)
  614 +
  615 +
  616 + }
  617 +
  618 + }
  619 + }
  620 + }
  621 +
  622 + func handleFaceLandmarks(request: VNRequest, error: Error?) {
  623 + DispatchQueue.main.async {
  624 + //perform all the UI updates on the main queue
  625 + guard let results = request.results as? [VNFaceObservation] else { return }
  626 + self.previewView.removeMask()
  627 + for face in results {
  628 + self.previewView.drawFaceWithLandmarks(face: face)
  629 + }
  630 + }
  631 + }
  632 +
  633 + func cropImageAndUpload(uiImage:UIImage ,facebounds:CGRect ,cgImage:CGImage){
  634 + self.isUploadingImage = true
  635 +
  636 + let sacle = uiImage.size.width/self.view.bounds.width
  637 + let scaleFaceBounds = CGRect(x: 0, y: 0, width: facebounds.width*sacle, height: facebounds.height*sacle*self.view.bounds.height/self.view.bounds.width)
  638 + let scaleUiImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .leftMirrored)
  639 + self.cropFacesToAvatarsWithExtendedArea(image: scaleUiImage) { avatarImage in
  640 + if let avatarImage = avatarImage {
  641 + // 使用裁剪后的头像
  642 + print("11111Avatar image: \(avatarImage)")
  643 + let data = avatarImage.compressImage()
  644 + print("11111Image size: \(data.count/1024) KB")
  645 +
  646 + CNLiveAuthTool.shared.authToolHiddenBlock = {
  647 + self.isUploadingImage = false
  648 + }
  649 +
  650 + CNLiveDigitalAuthViewModel.postFaceImageDataWithParams(name: self.realName, idNo: self.realNumber, fileData: data) { result, status in
  651 + if status == .success{
  652 + //
  653 +
  654 + }else{
  655 + //提示认证失败
  656 + CNLiveAuthTool.shared.showAuthFailAlert()
  657 + }
  658 + }
  659 +
  660 +// self.view.addSubview(self.faceImageView)
  661 +// self.faceImageView.image = avatarImage
  662 +// self.faceImageView.frame = CGRect(x: 50, y: 200, width: avatarImage.size.width, height: avatarImage.size.height)
  663 +// self.faceImageView.backgroundColor = .red
  664 + } else {
  665 + print("No faces detected or error occurred.")
  666 + }
  667 + }
  668 +
  669 + }
  670 + //zx
  671 + func cropFacesToAvatarsWithExtendedArea(image: UIImage, completion: @escaping (UIImage?) -> Void) {
  672 + guard let cgImage = image.cgImage else {
  673 + completion(nil)
  674 + return
  675 + }
  676 +
  677 + let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
  678 +
  679 + let faceDetectionRequest = VNDetectFaceRectanglesRequest { (request, error) in
  680 + guard let observations = request.results as? [VNFaceObservation], !observations.isEmpty else {
  681 + completion(nil)
  682 + return
  683 + }
  684 +
  685 + let faceObservation = observations.first // 假设我们只处理第一张脸
  686 +
  687 + // 扩大边界框以包含更多区域(如脖子和头发)
  688 + // 这可以通过调整边界框的origin和size来实现
  689 + let leftRightScale = 0.1
  690 + let upDownScale = 0.2
  691 + let minX = max(faceObservation!.boundingBox.minX - upDownScale, 0.0)// 向上调整边界以包括头发
  692 + let minY = max(faceObservation!.boundingBox.minY - leftRightScale, 0.0) // 向左调整边界
  693 + let maxX = min(faceObservation!.boundingBox.maxX + upDownScale, 1.0)// 向下调整边界以包括脖子
  694 + let maxY = min(faceObservation!.boundingBox.maxY + leftRightScale, 1.0) // 向右调整边界
  695 +
  696 + let extendedBoundingBox = CGRectMake(minX, minY, maxX - minX, maxY - minY)
  697 +
  698 + // 将归一化坐标转换为像素坐标
  699 + let scale:CGFloat = image.scale
  700 + let cropRect = CGRectMake(extendedBoundingBox.minX * CGFloat(cgImage.width) * scale, extendedBoundingBox.minY * CGFloat(cgImage.height) * scale, extendedBoundingBox.width * CGFloat(cgImage.width) * scale, extendedBoundingBox.height * CGFloat(cgImage.height) * scale)
  701 +
  702 + // 使用Core Graphics裁剪图片
  703 + if let croppedCGImage = cgImage.cropping(to: cropRect) {
  704 + let croppedImage = UIImage(cgImage: croppedCGImage, scale: UIScreen.main.scale, orientation: image.imageOrientation)
  705 + DispatchQueue.main.async {
  706 + completion(croppedImage)
  707 + }
  708 + } else {
  709 + DispatchQueue.main.async {
  710 + completion(nil)
  711 + }
  712 + }
  713 + }
  714 +
  715 + // 执行请求
  716 + do {
  717 + try imageRequestHandler.perform([faceDetectionRequest])
  718 + } catch {
  719 + DispatchQueue.main.async {
  720 + completion(nil)
  721 + }
  722 +
  723 + }
  724 + }
  725 +}
  726 +
  727 +// Camera Settings & Orientation
  728 +extension CNLiveFaceAuthController {
  729 + func availableSessionPresets() -> [String] {
  730 + let allSessionPresets = [AVCaptureSession.Preset.photo,
  731 + AVCaptureSession.Preset.low,
  732 + AVCaptureSession.Preset.medium,
  733 + AVCaptureSession.Preset.high,
  734 + AVCaptureSession.Preset.cif352x288,
  735 + AVCaptureSession.Preset.vga640x480,
  736 + AVCaptureSession.Preset.hd1280x720,
  737 + AVCaptureSession.Preset.iFrame960x540,
  738 + AVCaptureSession.Preset.iFrame1280x720,
  739 + AVCaptureSession.Preset.hd1920x1080,
  740 + AVCaptureSession.Preset.hd4K3840x2160]
  741 +
  742 + var availableSessionPresets = [String]()
  743 + for sessionPreset in allSessionPresets {
  744 + if session.canSetSessionPreset(sessionPreset) {
  745 + availableSessionPresets.append(sessionPreset.rawValue)
  746 + }
  747 + }
  748 +
  749 + return availableSessionPresets
  750 + }
  751 +
  752 + func exifOrientationFromDeviceOrientation() -> UInt32 {
  753 + enum DeviceOrientation: UInt32 {
  754 + case top0ColLeft = 1
  755 + case top0ColRight = 2
  756 + case bottom0ColRight = 3
  757 + case bottom0ColLeft = 4
  758 + case left0ColTop = 5
  759 + case right0ColTop = 6
  760 + case right0ColBottom = 7
  761 + case left0ColBottom = 8
  762 + }
  763 + var exifOrientation: DeviceOrientation
  764 +
  765 + switch UIDevice.current.orientation {
  766 + case .portraitUpsideDown:
  767 + exifOrientation = .left0ColBottom
  768 + case .landscapeLeft:
  769 + exifOrientation = devicePosition == .front ? .bottom0ColRight : .top0ColLeft
  770 + case .landscapeRight:
  771 + exifOrientation = devicePosition == .front ? .top0ColLeft : .bottom0ColRight
  772 + default:
  773 + exifOrientation = devicePosition == .front ? .left0ColTop : .right0ColTop
  774 + }
  775 + return exifOrientation.rawValue
  776 + }
  777 +}
  778 +
  779 +// MARK: - AVCaptureVideoDataOutputSampleBufferDelegate
  780 +extension CNLiveFaceAuthController: AVCaptureVideoDataOutputSampleBufferDelegate {
  781 + func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
  782 +
  783 + guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
  784 + let exifOrientation = CGImagePropertyOrientation(rawValue: exifOrientationFromDeviceOrientation()) else { return }
  785 +
  786 + //zx
  787 + self.tempPixelBuffer = pixelBuffer
  788 +
  789 + var requestOptions: [VNImageOption : Any] = [:]
  790 +
  791 + if let cameraIntrinsicData = CMGetAttachment(sampleBuffer, key: kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, attachmentModeOut: nil) {
  792 + requestOptions = [.cameraIntrinsics : cameraIntrinsicData]
  793 + }
  794 +
  795 + let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: exifOrientation, options: requestOptions)
  796 +
  797 + do {
  798 + try imageRequestHandler.perform(requests)
  799 + }
  800 +
  801 + catch {
  802 + print(error)
  803 + }
  804 + }
  805 +}
  806 +
  807 +
... ...
metaCode/Classes/Main/HomePage/Controller/DigitalAuth/CNLiveRealNameAuthController.swift
... ... @@ -100,6 +100,10 @@ class CNLiveRealNameAuthController:CNLiveBaseViewController,UITableViewDataSourc
100 100 nextBtn.addTarget(self, action: #selector(nextBtnAction), for: .touchUpInside)
101 101 return nextBtn
102 102 }()
  103 +
  104 + var nameTextStr:String = ""
  105 + var numberTextStr:String = ""
  106 +
103 107 override func viewDidLoad() {
104 108 super.viewDidLoad()
105 109 navigationBarHidden = false
... ... @@ -196,7 +200,19 @@ class CNLiveRealNameAuthController:CNLiveBaseViewController,UITableViewDataSourc
196 200 }
197 201  
198 202 @objc func nextBtnAction(){
199   -
  203 + if self.nameTextStr.isEmpty{
  204 + showMessage(message: "请输入姓名")
  205 + return
  206 + }
  207 + if self.numberTextStr.isEmpty{
  208 + showMessage(message: "请输入证件号码")
  209 + return
  210 + }
  211 + //进入人脸识别页面
  212 + let vc = CNLiveFaceAuthController()
  213 + vc.realName = self.nameTextStr
  214 + vc.realNumber = self.numberTextStr
  215 + self.navigationController?.pushViewController(vc, animated: true)
200 216 }
201 217  
202 218 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
... ... @@ -210,6 +226,18 @@ class CNLiveRealNameAuthController:CNLiveBaseViewController,UITableViewDataSourc
210 226 return cell
211 227 }
212 228 cell.type = indexPath.row+100
  229 + cell.tfTextUpdateBlock = { cell,type,textStr in
  230 + if type == 10{
  231 + self.nameTextStr = textStr
  232 + }else if type == 11{
  233 + self.numberTextStr = textStr
  234 + }else{
  235 +
  236 + }
  237 + }
  238 + cell.tfReturnBlock = {
  239 + self.view.endEditing(true)
  240 + }
213 241 return cell
214 242 }
215 243  
... ...
metaCode/Classes/Main/HomePage/View/DigitalAuthViews/CNLiveAuthPreviewView.swift 0 → 100644
  1 +//
  2 +// CNLiveAuthPreviewView.swift
  3 +// VisionDetection
  4 +//
  5 +// Created by Wei Chieh Tseng on 09/06/2017.
  6 +// Copyright © 2017 Willjay. All rights reserved.
  7 +//
  8 +
  9 +import UIKit
  10 +import Vision
  11 +import AVFoundation
  12 +
  13 +class CNLiveAuthPreviewView: UIView {
  14 +
  15 + private var maskLayer = [CAShapeLayer]()
  16 +
  17 + // MARK: AV capture properties
  18 + var videoPreviewLayer: AVCaptureVideoPreviewLayer {
  19 + return layer as! AVCaptureVideoPreviewLayer
  20 + }
  21 +
  22 + var session: AVCaptureSession? {
  23 + get {
  24 + return videoPreviewLayer.session
  25 + }
  26 +
  27 + set {
  28 + videoPreviewLayer.session = newValue
  29 + }
  30 + }
  31 +
  32 + override class var layerClass: AnyClass {
  33 + return AVCaptureVideoPreviewLayer.self
  34 + }
  35 +
  36 + // Create a new layer drawing the bounding box
  37 + private func createLayer(in rect: CGRect) -> CAShapeLayer{
  38 +
  39 + let mask = CAShapeLayer()
  40 + mask.frame = rect
  41 + mask.cornerRadius = 10
  42 + mask.opacity = 0.75
  43 + mask.borderColor = UIColor.yellow.cgColor
  44 + mask.borderWidth = 2.0
  45 +
  46 + maskLayer.append(mask)
  47 + layer.insertSublayer(mask, at: 1)
  48 +
  49 + return mask
  50 + }
  51 +
  52 + func drawFaceboundingBox(face : VNFaceObservation) {
  53 +
  54 + let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -frame.height)
  55 +
  56 + let translate = CGAffineTransform.identity.scaledBy(x: frame.width, y: frame.height)
  57 +
  58 + // The coordinates are normalized to the dimensions of the processed image, with the origin at the image's lower-left corner.
  59 + let facebounds = face.boundingBox.applying(translate).applying(transform)
  60 +
  61 + _ = createLayer(in: facebounds)
  62 +
  63 + }
  64 +
  65 + func drawFaceWithLandmarks(face: VNFaceObservation) {
  66 +
  67 + let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -frame.height)
  68 +
  69 + let translate = CGAffineTransform.identity.scaledBy(x: frame.width, y: frame.height)
  70 +
  71 + // The coordinates are normalized to the dimensions of the processed image, with the origin at the image's lower-left corner.
  72 + let facebounds = face.boundingBox.applying(translate).applying(transform)
  73 +
  74 + // Draw the bounding rect
  75 + let faceLayer = createLayer(in: facebounds)
  76 +
  77 + // Draw the landmarks
  78 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.nose)!, isClosed:false)
  79 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.noseCrest)!, isClosed:false)
  80 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.medianLine)!, isClosed:false)
  81 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.leftEye)!)
  82 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.leftPupil)!)
  83 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.leftEyebrow)!, isClosed:false)
  84 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightEye)!)
  85 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightPupil)!)
  86 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightEye)!)
  87 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightEyebrow)!, isClosed:false)
  88 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.innerLips)!)
  89 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.outerLips)!)
  90 + drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.faceContour)!, isClosed: false)
  91 + }
  92 +
  93 +
  94 +
  95 + func drawLandmarks(on targetLayer: CALayer, faceLandmarkRegion: VNFaceLandmarkRegion2D, isClosed: Bool = true) {
  96 + let rect: CGRect = targetLayer.frame
  97 + var points: [CGPoint] = []
  98 +
  99 + for i in 0..<faceLandmarkRegion.pointCount {
  100 + let point = faceLandmarkRegion.normalizedPoints[i]
  101 + points.append(point)
  102 + }
  103 +
  104 + let landmarkLayer = drawPointsOnLayer(rect: rect, landmarkPoints: points, isClosed: isClosed)
  105 +
  106 + // Change scale, coordinate systems, and mirroring
  107 + landmarkLayer.transform = CATransform3DMakeAffineTransform(
  108 + CGAffineTransform.identity
  109 + .scaledBy(x: rect.width, y: -rect.height)
  110 + .translatedBy(x: 0, y: -1)
  111 + )
  112 +
  113 + targetLayer.insertSublayer(landmarkLayer, at: 1)
  114 + }
  115 +
  116 + func drawPointsOnLayer(rect:CGRect, landmarkPoints: [CGPoint], isClosed: Bool = true) -> CALayer {
  117 + let linePath = UIBezierPath()
  118 + linePath.move(to: landmarkPoints.first!)
  119 +
  120 + for point in landmarkPoints.dropFirst() {
  121 + linePath.addLine(to: point)
  122 + }
  123 +
  124 + if isClosed {
  125 + linePath.addLine(to: landmarkPoints.first!)
  126 + }
  127 +
  128 + let lineLayer = CAShapeLayer()
  129 + lineLayer.path = linePath.cgPath
  130 + lineLayer.fillColor = nil
  131 + lineLayer.opacity = 1.0
  132 + lineLayer.strokeColor = UIColor.green.cgColor
  133 + lineLayer.lineWidth = 0.02
  134 +
  135 + return lineLayer
  136 + }
  137 +
  138 + func removeMask() {
  139 + for mask in maskLayer {
  140 + mask.removeFromSuperlayer()
  141 + }
  142 + maskLayer.removeAll()
  143 + }
  144 +
  145 +}
... ...
metaCode/Classes/Main/HomePage/View/DigitalAuthViews/CNLiveRealNameCell.swift
... ... @@ -10,8 +10,14 @@ import Kingfisher
10 10 import SnapKit
11 11 import QMUIKit
12 12  
  13 +typealias TFReturnBlock = () -> Void//
  14 +
  15 +typealias TFTextUpdateBlock = (_ currentCell: CNLiveRealNameCell ,_ type:Int ,_ textStr:String) -> Void//type: 10姓名 11证件号码
13 16 class CNLiveRealNameCell: UITableViewCell, QMUITextFieldDelegate {
14 17  
  18 + var tfTextUpdateBlock:TFTextUpdateBlock?
  19 + var tfReturnBlock:TFReturnBlock?
  20 +
15 21 lazy var nameLabel: UILabel = {
16 22 let titleLabel = UILabel()
17 23 titleLabel.textColor = HexColor("#333333")
... ... @@ -30,6 +36,7 @@ class CNLiveRealNameCell: UITableViewCell, QMUITextFieldDelegate {
30 36 searchTF.textColor = HexColor("#999999")
31 37 searchTF.font = Font_16
32 38 searchTF.delegate = self
  39 + searchTF.returnKeyType = .done
33 40 return searchTF
34 41 }()
35 42 lazy var nameDescLabel: UILabel = {
... ... @@ -103,10 +110,16 @@ class CNLiveRealNameCell: UITableViewCell, QMUITextFieldDelegate {
103 110  
104 111 // MARK: - UITextFieldDelegate
105 112 func textFieldDidEndEditing(_ textField: UITextField) {
106   -
  113 + if self.nameLabel.text == "本人姓名"{
  114 + self.tfTextUpdateBlock?(self, 10, self.nameTF.text ?? "")
  115 + }else if self.nameLabel.text == "证件号码"{
  116 + self.tfTextUpdateBlock?(self, 11, self.nameTF.text ?? "")
  117 + }else{
  118 +
  119 + }
107 120 }
108 121 func textFieldShouldReturn(_ textField: UITextField) -> Bool {
109   -// self.view.endEditing(true)
  122 + self.tfReturnBlock?()
110 123 return true
111 124 }
112 125  
... ...
metaCode/Classes/Main/HomePage/ViewModel/DigitalAuth/CNLiveDigitalAuthViewModel.swift 0 → 100644
  1 +//
  2 +// CNLiveDigitalAuthViewModel.swift
  3 +// metaCode
  4 +//
  5 +// Created by zx on 2024/6/12.
  6 +// 上传头像-实名认知
  7 +
  8 +import Foundation
  9 +import CNLiveRequestBastKit
  10 +
  11 +class CNLiveDigitalAuthViewModel:NSObject{
  12 +
  13 + /*
  14 + 上传人脸实名图片
  15 + 参数 name 姓名
  16 + 参数 idNo 身份证号
  17 + 参数 fileData 图片data
  18 + */
  19 + static func postFaceImageDataWithParams(name:String,idNo:String,fileData:Data,resultBlock: @escaping downSlideResultBlock) {
  20 + let param = NSMutableDictionary()
  21 + param.setValue(UserInfoManager.shared.userInfo.uid, forKey: "sid")
  22 + param.setValue(UserInfoManager.shared.userInfo.uid, forKey: "name")
  23 + param.setValue(UserInfoManager.shared.userInfo.uid, forKey: "idNo")
  24 +// param.setValue(UserInfoManager.shared.userInfo.uid, forKey: "file")
  25 +
  26 + CNLiveNetworking.setAllowRequestDefaultArgument(true)
  27 + CNLiveNetworking.setupShowDataResult(false)
  28 + CNLiveNetworking.setupDSRequest(true)
  29 + let custom_param = NSMutableDictionary()
  30 + custom_param.setValue(CNLiveMCEnvironmentManager.shared.appId, forKey: "appId")
  31 + custom_param.setValue(CNLiveMCEnvironmentManager.shared.appKey, forKey: "appKey")
  32 + CNLiveNetworking.setupCustomParam(custom_param as? [String : String])
  33 + CNLiveNetworking.uploadNetwork(withURLString: APPBaseCMSUrl+"/toolApi/verify", param: param as! [AnyHashable : Any]) { formData in
  34 + formData?.appendPart(withForm: fileData, name: "file")//APPBaseCMSUrl APPBaseMCUrl
  35 + } progressBlock: { progress in
  36 + print("progress\(progress?.completedUnitCount)")
  37 + } completionBlock: { task, any, error in
  38 + print("completionBlock")
  39 +
  40 + if (error != nil){
  41 + resultBlock(NSNull(),.failed)
  42 + }else{
  43 + resultBlock(true,.success)
  44 + }
  45 +
  46 + }
  47 +
  48 + }
  49 +
  50 +}
  51 +
... ...
metaCode/Classes/Main/Util/DigitalAuth/AVExtensions.swift 0 → 100644
  1 +//
  2 +// AVExtensions.swift
  3 +// VisionDetection
  4 +//
  5 +// Created by Wei Chieh Tseng on 2/5/19.
  6 +// Copyright © 2019 Willjay. All rights reserved.
  7 +//
  8 +
  9 +import AVKit
  10 +
  11 +// Extension for Orientation
  12 +extension UIDeviceOrientation {
  13 + var videoOrientation: AVCaptureVideoOrientation? {
  14 + switch self {
  15 + case .portrait: return .portrait
  16 + case .portraitUpsideDown: return .portraitUpsideDown
  17 + case .landscapeLeft: return .landscapeRight
  18 + case .landscapeRight: return .landscapeLeft
  19 + default: return nil
  20 + }
  21 + }
  22 +}
  23 +
  24 +extension UIInterfaceOrientation {
  25 + var videoOrientation: AVCaptureVideoOrientation? {
  26 + switch self {
  27 + case .portrait: return .portrait
  28 + case .portraitUpsideDown: return .portraitUpsideDown
  29 + case .landscapeLeft: return .landscapeLeft
  30 + case .landscapeRight: return .landscapeRight
  31 + default: return nil
  32 + }
  33 + }
  34 +}
... ...
metaCode/Classes/Main/Util/DigitalAuth/CGImage+FaceCrop.swift 0 → 100644
  1 +//
  2 +// CGImage+FaceCrop.swift
  3 +//
  4 +// Created by Kirill Kudaev on 9/9/19.
  5 +// Copyright © 2019 Ancestry. All rights reserved.
  6 +//
  7 +
  8 +import Foundation
  9 +import Vision
  10 +
  11 +public extension CGImage {
  12 + @available(iOS 11.0, *)
  13 + func faceCrop(margin: CGFloat = 200, completion: @escaping (FaceCropResult) -> Void) {
  14 + let req = VNDetectFaceRectanglesRequest { request, error in
  15 + if let error = error {
  16 + completion(.failure(error))
  17 + return
  18 + }
  19 +
  20 + guard let results = request.results, !results.isEmpty else {
  21 + completion(.notFound)
  22 + return
  23 + }
  24 +
  25 + var faces: [VNFaceObservation] = []
  26 + for result in results {
  27 + guard let face = result as? VNFaceObservation else { continue }
  28 + faces.append(face)
  29 + }
  30 +
  31 + let croppingRect = self.getCroppingRect(for: faces, margin: margin)
  32 + let faceImage = self.cropping(to: croppingRect)
  33 +
  34 + guard let result = faceImage else {
  35 + completion(.notFound)
  36 + return
  37 + }
  38 + completion(.success(result))
  39 + }
  40 +
  41 + do {
  42 + try VNImageRequestHandler(cgImage: self, options: [:]).perform([req])
  43 + } catch let error {
  44 + completion(.failure(error))
  45 + }
  46 + }
  47 +
  48 + @available(iOS 11.0, *)
  49 + private func getCroppingRect(for faces: [VNFaceObservation], margin: CGFloat) -> CGRect {
  50 + var totalX = CGFloat(0)
  51 + var totalY = CGFloat(0)
  52 + var totalW = CGFloat(0)
  53 + var totalH = CGFloat(0)
  54 + var minX = CGFloat.greatestFiniteMagnitude
  55 + var minY = CGFloat.greatestFiniteMagnitude
  56 + let numFaces = CGFloat(faces.count)
  57 +
  58 + for face in faces {
  59 + let w = face.boundingBox.width * CGFloat(width)
  60 + let h = face.boundingBox.height * CGFloat(height)
  61 + let x = face.boundingBox.origin.x * CGFloat(width)
  62 + let y = (1 - face.boundingBox.origin.y) * CGFloat(height) - h
  63 + totalX += x
  64 + totalY += y
  65 + totalW += w
  66 + totalH += h
  67 + minX = .minimum(minX, x)
  68 + minY = .minimum(minY, y)
  69 + }
  70 +
  71 + let avgX = totalX / numFaces
  72 + let avgY = totalY / numFaces
  73 + let avgW = totalW / numFaces
  74 + let avgH = totalH / numFaces
  75 +
  76 + let offset = margin + avgX - minX
  77 +
  78 + return CGRect(x: avgX - offset, y: avgY - offset, width: avgW + (offset * 2), height: avgH + (offset * 2))
  79 + }
  80 +}
  81 +
  82 +public enum FaceCropResult {
  83 + case success(CGImage)
  84 + case notFound
  85 + case failure(Error)
  86 +}
... ...
metaCode/Classes/Main/Util/DigitalAuth/CNLiveAuthTool.swift 0 → 100644
  1 +//
  2 +// CNLiveAuthTool.swift
  3 +// metaCode
  4 +//
  5 +// Created by zx on 2024/6/12.
  6 +//
  7 +
  8 +import Foundation
  9 +import QMUIKit
  10 +import SnapKit
  11 +
  12 +typealias AuthToolHiddenBlock = () -> Void
  13 +
  14 +class CNLiveAuthTool:NSObject{
  15 + static let shared = CNLiveAuthTool()
  16 +
  17 + var authToolHiddenBlock:AuthToolHiddenBlock?
  18 + let delegate = UIApplication.shared.delegate as! AppDelegate
  19 +
  20 + lazy var bgBtnView: UIButton = {
  21 + let bgBtnView = UIButton(frame: self.delegate.window!.bounds)
  22 + bgBtnView.backgroundColor = HexColor("#000000")
  23 + bgBtnView.alpha = 0.6
  24 +// bgBtnView.addTarget(self, action: #selector(bgBtnViewAction), for: .touchUpInside)
  25 + return bgBtnView
  26 + }()
  27 +
  28 + lazy var bottomBgView :UIView = {
  29 + let bottomBgView = UIView(frame: CGRect(x: (KSreenWidth-adapt_length(length: 273))/2, y: (KSreenHeight-adapt_length(length: 597))/2, width: adapt_length(length: 273), height: adapt_length(length: 597)))
  30 + bottomBgView.backgroundColor = HexColor("#FFFFFF")
  31 + bottomBgView.layer.cornerRadius = 13
  32 + bottomBgView.clipsToBounds = true
  33 + bottomBgView.tag = integralAlertToolTag
  34 + return bottomBgView
  35 + }()
  36 +
  37 + lazy var failTitleLabel: UILabel = {
  38 + let titleLabel = UILabel.init()
  39 + titleLabel.textColor = HexColor("#333333")
  40 + titleLabel.textAlignment = .center
  41 + titleLabel.font = BoldFont_16
  42 + titleLabel.text = "实名认证失败"
  43 + return titleLabel
  44 + }()
  45 +
  46 + lazy var jianchaView :UIView = {
  47 + let jianchaView = UIView(frame: CGRect(x: adapt_length(length: 14), y: adapt_length(length: 58), width: KSreenWidth-2*adapt_length(length: 14), height: adapt_length(length: 77)))
  48 + jianchaView.backgroundColor = HexColor("#F7F7F7")
  49 + jianchaView.layer.cornerRadius = 9.5
  50 + jianchaView.clipsToBounds = true
  51 + jianchaView.layer.borderWidth = 0.5
  52 + jianchaView.layer.borderColor = HexColor("#E1E1E1")?.cgColor
  53 + return jianchaView
  54 + }()
  55 +
  56 + //请检查
  57 + lazy var jianchaTitleLabel: UILabel = {
  58 + let titleLabel = UILabel.init()
  59 + titleLabel.textColor = HexColor("#333333")
  60 + titleLabel.textAlignment = .center
  61 + titleLabel.font = Font_12
  62 + titleLabel.text = "请检查"
  63 + return titleLabel
  64 + }()
  65 +
  66 + lazy var firstImgView :UIImageView = {
  67 + let iconImgView = UIImageView(frame: CGRect(x: adapt_length(length: 13), y: adapt_length(length: 35), width: adapt_length(length: 11), height: adapt_length(length: 11)))
  68 + iconImgView.image = UIImage(named: "auth_1")
  69 + return iconImgView
  70 + }()
  71 + lazy var firstTitleLabel: UILabel = {
  72 + let titleLabel = UILabel.init()
  73 + titleLabel.textColor = HexColor("#333333")
  74 + titleLabel.font = Font_10
  75 + titleLabel.text = "请确保人脸与身份信息证件照相符"
  76 + return titleLabel
  77 + }()
  78 +
  79 +// lazy var secImgView :UIImageView = {
  80 +// let iconImgView = UIImageView(frame: CGRect(x: adapt_length(length: 13), y: adapt_length(length: 54), width: adapt_length(length: 11), height: adapt_length(length: 11)))
  81 +// iconImgView.image = UIImage(named: "auth_2")
  82 +// return iconImgView
  83 +// }()
  84 +// lazy var secTitleLabel: UILabel = {
  85 +// let titleLabel = UILabel.init()
  86 +// titleLabel.textColor = HexColor("#333333")
  87 +// titleLabel.font = Font_10
  88 +// titleLabel.text = "请确保人脸与身份信息证件照相符"
  89 +// return titleLabel
  90 +// }()
  91 +
  92 + lazy var detailImgView :UIImageView = {
  93 + let iconImgView = UIImageView()
  94 + iconImgView.image = UIImage(named: "auth_fail")
  95 + return iconImgView
  96 + }()
  97 +
  98 + lazy var xiugaiBtn: UIButton = {
  99 + let payBtn = UIButton(type: .custom)
  100 + payBtn.setTitleColor(HexColor("#FFFFFF"), for: .normal)
  101 + payBtn.setTitle("去修改", for: .normal)
  102 + payBtn.titleLabel?.font = Font_18
  103 + payBtn.backgroundColor = HexColor("#1961FE")
  104 + payBtn.layer.cornerRadius = 21
  105 + payBtn.clipsToBounds = true
  106 + payBtn.addTarget(self, action: #selector(xiugaiBtnAction), for: .touchUpInside)
  107 + return payBtn
  108 + }()
  109 +
  110 + // MARK: - show
  111 + ///弹出实名失败弹窗
  112 + func showAuthFailAlert(){
  113 + delegate.window?.addSubview(self.bgBtnView)
  114 + delegate.window?.addSubview(self.bottomBgView)
  115 + self.bottomBgView.addSubview(self.xiugaiBtn)
  116 + self.xiugaiBtn.snp.makeConstraints { make in
  117 + make.height.equalTo(adapt_length(length: 39))
  118 + make.width.equalTo(adapt_length(length: 160))
  119 + make.bottom.equalToSuperview().offset(adapt_length(length: -15))
  120 + make.centerX.equalToSuperview()
  121 + }
  122 +
  123 + self.bottomBgView.addSubview(self.failTitleLabel)
  124 + self.failTitleLabel.snp.makeConstraints { make in
  125 + make.top.equalTo(adapt_length(length: 20))
  126 + make.left.equalTo(15)
  127 + make.right.equalToSuperview().offset(-15)
  128 + make.height.equalTo(adapt_length(length: 23))
  129 + }
  130 +
  131 + self.bottomBgView.addSubview(self.jianchaView)
  132 +
  133 + self.jianchaView.addSubview(self.jianchaTitleLabel)
  134 + self.jianchaTitleLabel.snp.makeConstraints { make in
  135 + make.left.equalTo(adapt_length(length: 13))
  136 + make.top.equalTo(adapt_length(length: 10))
  137 + }
  138 +
  139 + self.jianchaView.addSubview(self.firstImgView)
  140 + self.firstImgView.snp.makeConstraints { make in
  141 + make.left.equalTo(adapt_length(length: 13))
  142 + make.top.equalTo(adapt_length(length: 35))
  143 + make.width.equalTo(adapt_length(length: 11))
  144 + make.height.equalTo(adapt_length(length: 11))
  145 + }
  146 +
  147 + self.jianchaView.addSubview(self.firstTitleLabel)
  148 + self.firstTitleLabel.snp.makeConstraints { make in
  149 + make.centerY.equalTo(self.firstImgView.snp.centerY)
  150 + make.left.equalTo(self.firstImgView.snp.right).offset(adapt_length(length: 1.5))
  151 + }
  152 +
  153 + self.bottomBgView.addSubview(self.detailImgView)
  154 + self.detailImgView.snp.makeConstraints { make in
  155 + make.left.equalTo(adapt_length(length: 15))
  156 + make.top.equalTo(self.jianchaView.snp.bottom).offset(adapt_length(length: 10))
  157 + make.width.equalTo(adapt_length(length: 243))
  158 + make.height.equalTo(adapt_length(length: 384))
  159 + }
  160 + }
  161 +
  162 + //隐藏弹窗
  163 + func hiddenIntegralAlert(){
  164 + self.authToolHiddenBlock?()
  165 + delegate.window?.subviews.forEach({ view in
  166 + if view.tag == integralAlertToolTag{
  167 + UIView.animate(withDuration: 0.25) {
  168 + view.removeFromSuperview()
  169 + self.bgBtnView.removeFromSuperview()
  170 + self.bottomBgView.removeFromSuperview()
  171 + }
  172 + }
  173 + })
  174 + }
  175 +
  176 + // MARK: - BtnAction
  177 + @objc func xiugaiBtnAction(){
  178 + self.hiddenIntegralAlert()
  179 + }
  180 +
  181 +}
... ...
metaCode/Classes/Main/Util/DigitalAuth/UIImage_extension.swift 0 → 100644
  1 +//
  2 +// UIImage_extension.swift
  3 +// VisionDetection
  4 +//
  5 +// Created by zx on 2024/5/11.
  6 +// Copyright © 2024 Willjay. All rights reserved.
  7 +//
  8 +
  9 +
  10 +import UIKit
  11 +enum ImageCopmpressSize {
  12 + //压缩到100KB(1024*100字节)以内
  13 + case one
  14 + //压缩到200KB(1024*100字节)以内
  15 + case two
  16 + //压缩到300KB(1024*100字节)以内
  17 + case three
  18 +}
  19 +
  20 +
  21 +extension UIImage {
  22 +
  23 + //不大于100KB
  24 + func compressImage()->Data{
  25 + compressImage(size: .one)
  26 + }
  27 +
  28 +
  29 + func compressImage(size:ImageCopmpressSize) -> Data{
  30 + switch size {
  31 + case .one:
  32 + return compressImage(maxLength: 1024*100)
  33 + case .two:
  34 + return compressImage(maxLength: 1024*200)
  35 + case .three:
  36 + return compressImage(maxLength: 1024*300)
  37 + default:
  38 + break
  39 + }
  40 + }
  41 +
  42 +
  43 + // 图片压缩 byte
  44 + func compressImage(maxLength: Int) -> Data {
  45 + // let tempMaxLength: Int = maxLength / 8
  46 + let tempMaxLength: Int = maxLength
  47 + var compression: CGFloat = 1
  48 +
  49 + guard var data = self.jpegData(compressionQuality: compression), data.count > tempMaxLength else { return self.jpegData(compressionQuality: compression)! }
  50 +
  51 + // 压缩大小
  52 + var max: CGFloat = 1
  53 + var min: CGFloat = 0
  54 + for _ in 0..<6 {
  55 + compression = (max + min) / 2
  56 + data = self.jpegData(compressionQuality: compression)!
  57 + if CGFloat(data.count) < CGFloat(tempMaxLength) * 0.9 {
  58 + min = compression
  59 + } else if data.count > tempMaxLength {
  60 + max = compression
  61 + } else {
  62 + break
  63 + }
  64 + }
  65 + var resultImage: UIImage = UIImage(data: data)!
  66 + if data.count < tempMaxLength { return data }
  67 +
  68 + // 压缩大小
  69 + var lastDataLength: Int = 0
  70 + while data.count > tempMaxLength && data.count != lastDataLength {
  71 + lastDataLength = data.count
  72 + let ratio: CGFloat = CGFloat(tempMaxLength) / CGFloat(data.count)
  73 + print("Ratio =", ratio)
  74 + let size: CGSize = CGSize(width: Int(resultImage.size.width * sqrt(ratio)),
  75 + height: Int(resultImage.size.height * sqrt(ratio)))
  76 + UIGraphicsBeginImageContext(size)
  77 + resultImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
  78 + resultImage = UIGraphicsGetImageFromCurrentImageContext()!
  79 + UIGraphicsEndImageContext()
  80 + data = resultImage.jpegData(compressionQuality: compression)!
  81 + }
  82 + return data
  83 + }
  84 +
  85 +}
... ...
metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_1.imageset/Contents.json 0 → 100644
  1 +{
  2 + "images" : [
  3 + {
  4 + "idiom" : "universal",
  5 + "scale" : "1x"
  6 + },
  7 + {
  8 + "filename" : "auth_1@2x.png",
  9 + "idiom" : "universal",
  10 + "scale" : "2x"
  11 + },
  12 + {
  13 + "filename" : "auth_1@3x.png",
  14 + "idiom" : "universal",
  15 + "scale" : "3x"
  16 + }
  17 + ],
  18 + "info" : {
  19 + "author" : "xcode",
  20 + "version" : 1
  21 + }
  22 +}
... ...
metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_1.imageset/auth_1@2x.png 0 → 100644

511 Bytes

metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_1.imageset/auth_1@3x.png 0 → 100644

886 Bytes

metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_2.imageset/Contents.json 0 → 100644
  1 +{
  2 + "images" : [
  3 + {
  4 + "idiom" : "universal",
  5 + "scale" : "1x"
  6 + },
  7 + {
  8 + "filename" : "auth_2@2x.png",
  9 + "idiom" : "universal",
  10 + "scale" : "2x"
  11 + },
  12 + {
  13 + "filename" : "auth_2@3x.png",
  14 + "idiom" : "universal",
  15 + "scale" : "3x"
  16 + }
  17 + ],
  18 + "info" : {
  19 + "author" : "xcode",
  20 + "version" : 1
  21 + }
  22 +}
... ...
metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_2.imageset/auth_2@2x.png 0 → 100644

714 Bytes

metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_2.imageset/auth_2@3x.png 0 → 100644

1.26 KB

metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_faceAuth.imageset/Contents.json 0 → 100644
  1 +{
  2 + "images" : [
  3 + {
  4 + "idiom" : "universal",
  5 + "scale" : "1x"
  6 + },
  7 + {
  8 + "filename" : "auth_faceAuth@2x.png",
  9 + "idiom" : "universal",
  10 + "scale" : "2x"
  11 + },
  12 + {
  13 + "filename" : "auth_faceAuth@3x.png",
  14 + "idiom" : "universal",
  15 + "scale" : "3x"
  16 + }
  17 + ],
  18 + "info" : {
  19 + "author" : "xcode",
  20 + "version" : 1
  21 + }
  22 +}
... ...
metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_faceAuth.imageset/auth_faceAuth@2x.png 0 → 100644

26.3 KB

metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_faceAuth.imageset/auth_faceAuth@3x.png 0 → 100644

51.2 KB

metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_fail.imageset/Contents.json 0 → 100644
  1 +{
  2 + "images" : [
  3 + {
  4 + "idiom" : "universal",
  5 + "scale" : "1x"
  6 + },
  7 + {
  8 + "filename" : "auth_fail@2x.png",
  9 + "idiom" : "universal",
  10 + "scale" : "2x"
  11 + },
  12 + {
  13 + "filename" : "auth_fail@3x.png",
  14 + "idiom" : "universal",
  15 + "scale" : "3x"
  16 + }
  17 + ],
  18 + "info" : {
  19 + "author" : "xcode",
  20 + "version" : 1
  21 + }
  22 +}
... ...
metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_fail.imageset/auth_fail@2x.png 0 → 100644

220 KB

metaCode/Supporting Files/Assets.xcassets/AuthPage/auth_fail.imageset/auth_fail@3x.png 0 → 100644

435 KB