├── .gitignore ├── HMQRCodeScanner.podspec ├── HMQRCodeScanner.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── HMQRCodeScanner ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── avatar.imageset │ │ ├── Contents.json │ │ └── avatar.jpg ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── QRCode │ ├── HMScanerCardViewController.h │ ├── HMScanerCardViewController.m │ ├── HMScanner.bundle │ │ ├── QRCodeScanLine@2x.png │ │ ├── ScanQR1@2x.png │ │ ├── ScanQR2@2x.png │ │ ├── ScanQR3@2x.png │ │ └── ScanQR4@2x.png │ ├── HMScanner.h │ ├── HMScanner.m │ ├── HMScannerBorder.h │ ├── HMScannerBorder.m │ ├── HMScannerController.h │ ├── HMScannerController.m │ ├── HMScannerMaskView.h │ ├── HMScannerMaskView.m │ ├── HMScannerViewController.h │ └── HMScannerViewController.m ├── ViewController.h ├── ViewController.m └── main.m ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | -------------------------------------------------------------------------------- /HMQRCodeScanner.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "HMQRCodeScanner" 3 | s.version = "1.0.6" 4 | s.summary = "包含 UI 界面的轻量级二维码扫描及生成框架" 5 | s.homepage = "https://github.com/liufan321/HMQRCodeScanner" 6 | s.license = "MIT" 7 | s.author = { "Fan Liu" => "liufan321@gmail.com" } 8 | s.platform = :ios, "8.0" 9 | s.source = { :git => "https://github.com/liufan321/HMQRCodeScanner.git", :tag => s.version } 10 | s.source_files = "HMQRCodeScanner/QRCode/*.{h,m}" 11 | s.resources = "HMQRCodeScanner/QRCode/HMScanner.bundle" 12 | s.framework = "AVFoundation" 13 | s.requires_arc = true 14 | end 15 | -------------------------------------------------------------------------------- /HMQRCodeScanner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C20FE0431C38B77500EF2C5E /* HMScannerMaskView.m in Sources */ = {isa = PBXBuildFile; fileRef = C20FE0421C38B77500EF2C5E /* HMScannerMaskView.m */; }; 11 | C2BC28A81C377B0900CD8839 /* HMScannerBorder.m in Sources */ = {isa = PBXBuildFile; fileRef = C2BC28A71C377B0900CD8839 /* HMScannerBorder.m */; }; 12 | C2BC28AB1C37809500CD8839 /* HMScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = C2BC28AA1C37809500CD8839 /* HMScanner.m */; }; 13 | C2CABF7A1C3958AE00CE5230 /* HMScanerCardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C2CABF791C3958AE00CE5230 /* HMScanerCardViewController.m */; }; 14 | C2CABF7C1C39834E00CE5230 /* HMScanner.bundle in Resources */ = {isa = PBXBuildFile; fileRef = C2CABF7B1C39834E00CE5230 /* HMScanner.bundle */; }; 15 | C2D6BCFC1C377219000EC35D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C2D6BCFB1C377219000EC35D /* main.m */; }; 16 | C2D6BCFF1C377219000EC35D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C2D6BCFE1C377219000EC35D /* AppDelegate.m */; }; 17 | C2D6BD021C377219000EC35D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C2D6BD011C377219000EC35D /* ViewController.m */; }; 18 | C2D6BD051C377219000EC35D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C2D6BD031C377219000EC35D /* Main.storyboard */; }; 19 | C2D6BD071C377219000EC35D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C2D6BD061C377219000EC35D /* Assets.xcassets */; }; 20 | C2D6BD0A1C377219000EC35D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C2D6BD081C377219000EC35D /* LaunchScreen.storyboard */; }; 21 | C2D6BD141C3772D9000EC35D /* HMScannerController.m in Sources */ = {isa = PBXBuildFile; fileRef = C2D6BD131C3772D9000EC35D /* HMScannerController.m */; }; 22 | C2D6BD171C37733E000EC35D /* HMScannerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C2D6BD161C37733E000EC35D /* HMScannerViewController.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | C20FE0411C38B77500EF2C5E /* HMScannerMaskView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMScannerMaskView.h; sourceTree = ""; }; 27 | C20FE0421C38B77500EF2C5E /* HMScannerMaskView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMScannerMaskView.m; sourceTree = ""; }; 28 | C2BC28A61C377B0900CD8839 /* HMScannerBorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMScannerBorder.h; sourceTree = ""; }; 29 | C2BC28A71C377B0900CD8839 /* HMScannerBorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMScannerBorder.m; sourceTree = ""; }; 30 | C2BC28A91C37809500CD8839 /* HMScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMScanner.h; sourceTree = ""; }; 31 | C2BC28AA1C37809500CD8839 /* HMScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMScanner.m; sourceTree = ""; }; 32 | C2CABF781C3958AE00CE5230 /* HMScanerCardViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMScanerCardViewController.h; sourceTree = ""; }; 33 | C2CABF791C3958AE00CE5230 /* HMScanerCardViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMScanerCardViewController.m; sourceTree = ""; }; 34 | C2CABF7B1C39834E00CE5230 /* HMScanner.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = HMScanner.bundle; sourceTree = ""; }; 35 | C2D6BCF71C377219000EC35D /* HMQRCodeScanner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HMQRCodeScanner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | C2D6BCFB1C377219000EC35D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | C2D6BCFD1C377219000EC35D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 38 | C2D6BCFE1C377219000EC35D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 39 | C2D6BD001C377219000EC35D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 40 | C2D6BD011C377219000EC35D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 41 | C2D6BD041C377219000EC35D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | C2D6BD061C377219000EC35D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | C2D6BD091C377219000EC35D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | C2D6BD0B1C377219000EC35D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | C2D6BD121C3772D9000EC35D /* HMScannerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMScannerController.h; sourceTree = ""; }; 46 | C2D6BD131C3772D9000EC35D /* HMScannerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMScannerController.m; sourceTree = ""; }; 47 | C2D6BD151C37733E000EC35D /* HMScannerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HMScannerViewController.h; sourceTree = ""; }; 48 | C2D6BD161C37733E000EC35D /* HMScannerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HMScannerViewController.m; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | C2D6BCF41C377219000EC35D /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | C2D6BCEE1C377219000EC35D = { 63 | isa = PBXGroup; 64 | children = ( 65 | C2D6BCF91C377219000EC35D /* HMQRCodeScanner */, 66 | C2D6BCF81C377219000EC35D /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | C2D6BCF81C377219000EC35D /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | C2D6BCF71C377219000EC35D /* HMQRCodeScanner.app */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | C2D6BCF91C377219000EC35D /* HMQRCodeScanner */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | C2D6BD111C377251000EC35D /* QRCode */, 82 | C2D6BCFD1C377219000EC35D /* AppDelegate.h */, 83 | C2D6BCFE1C377219000EC35D /* AppDelegate.m */, 84 | C2D6BD001C377219000EC35D /* ViewController.h */, 85 | C2D6BD011C377219000EC35D /* ViewController.m */, 86 | C2D6BD031C377219000EC35D /* Main.storyboard */, 87 | C2D6BD061C377219000EC35D /* Assets.xcassets */, 88 | C2D6BD081C377219000EC35D /* LaunchScreen.storyboard */, 89 | C2D6BD0B1C377219000EC35D /* Info.plist */, 90 | C2D6BCFA1C377219000EC35D /* Supporting Files */, 91 | ); 92 | path = HMQRCodeScanner; 93 | sourceTree = ""; 94 | }; 95 | C2D6BCFA1C377219000EC35D /* Supporting Files */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | C2D6BCFB1C377219000EC35D /* main.m */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | C2D6BD111C377251000EC35D /* QRCode */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | C2D6BD121C3772D9000EC35D /* HMScannerController.h */, 107 | C2D6BD131C3772D9000EC35D /* HMScannerController.m */, 108 | C2D6BD151C37733E000EC35D /* HMScannerViewController.h */, 109 | C2D6BD161C37733E000EC35D /* HMScannerViewController.m */, 110 | C2CABF781C3958AE00CE5230 /* HMScanerCardViewController.h */, 111 | C2CABF791C3958AE00CE5230 /* HMScanerCardViewController.m */, 112 | C2BC28A61C377B0900CD8839 /* HMScannerBorder.h */, 113 | C2BC28A71C377B0900CD8839 /* HMScannerBorder.m */, 114 | C20FE0411C38B77500EF2C5E /* HMScannerMaskView.h */, 115 | C20FE0421C38B77500EF2C5E /* HMScannerMaskView.m */, 116 | C2BC28A91C37809500CD8839 /* HMScanner.h */, 117 | C2BC28AA1C37809500CD8839 /* HMScanner.m */, 118 | C2CABF7B1C39834E00CE5230 /* HMScanner.bundle */, 119 | ); 120 | path = QRCode; 121 | sourceTree = ""; 122 | }; 123 | /* End PBXGroup section */ 124 | 125 | /* Begin PBXNativeTarget section */ 126 | C2D6BCF61C377219000EC35D /* HMQRCodeScanner */ = { 127 | isa = PBXNativeTarget; 128 | buildConfigurationList = C2D6BD0E1C377219000EC35D /* Build configuration list for PBXNativeTarget "HMQRCodeScanner" */; 129 | buildPhases = ( 130 | C2D6BCF31C377219000EC35D /* Sources */, 131 | C2D6BCF41C377219000EC35D /* Frameworks */, 132 | C2D6BCF51C377219000EC35D /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = HMQRCodeScanner; 139 | productName = HMQRCodeScanner; 140 | productReference = C2D6BCF71C377219000EC35D /* HMQRCodeScanner.app */; 141 | productType = "com.apple.product-type.application"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | C2D6BCEF1C377219000EC35D /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | CLASSPREFIX = HM; 150 | LastUpgradeCheck = 0810; 151 | ORGANIZATIONNAME = itheima; 152 | TargetAttributes = { 153 | C2D6BCF61C377219000EC35D = { 154 | CreatedOnToolsVersion = 7.2; 155 | DevelopmentTeam = J9XHDK444E; 156 | }; 157 | }; 158 | }; 159 | buildConfigurationList = C2D6BCF21C377219000EC35D /* Build configuration list for PBXProject "HMQRCodeScanner" */; 160 | compatibilityVersion = "Xcode 3.2"; 161 | developmentRegion = English; 162 | hasScannedForEncodings = 0; 163 | knownRegions = ( 164 | en, 165 | Base, 166 | ); 167 | mainGroup = C2D6BCEE1C377219000EC35D; 168 | productRefGroup = C2D6BCF81C377219000EC35D /* Products */; 169 | projectDirPath = ""; 170 | projectRoot = ""; 171 | targets = ( 172 | C2D6BCF61C377219000EC35D /* HMQRCodeScanner */, 173 | ); 174 | }; 175 | /* End PBXProject section */ 176 | 177 | /* Begin PBXResourcesBuildPhase section */ 178 | C2D6BCF51C377219000EC35D /* Resources */ = { 179 | isa = PBXResourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | C2D6BD0A1C377219000EC35D /* LaunchScreen.storyboard in Resources */, 183 | C2D6BD071C377219000EC35D /* Assets.xcassets in Resources */, 184 | C2D6BD051C377219000EC35D /* Main.storyboard in Resources */, 185 | C2CABF7C1C39834E00CE5230 /* HMScanner.bundle in Resources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXResourcesBuildPhase section */ 190 | 191 | /* Begin PBXSourcesBuildPhase section */ 192 | C2D6BCF31C377219000EC35D /* Sources */ = { 193 | isa = PBXSourcesBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | C2D6BD021C377219000EC35D /* ViewController.m in Sources */, 197 | C2D6BD141C3772D9000EC35D /* HMScannerController.m in Sources */, 198 | C2D6BD171C37733E000EC35D /* HMScannerViewController.m in Sources */, 199 | C2D6BCFF1C377219000EC35D /* AppDelegate.m in Sources */, 200 | C20FE0431C38B77500EF2C5E /* HMScannerMaskView.m in Sources */, 201 | C2CABF7A1C3958AE00CE5230 /* HMScanerCardViewController.m in Sources */, 202 | C2BC28AB1C37809500CD8839 /* HMScanner.m in Sources */, 203 | C2D6BCFC1C377219000EC35D /* main.m in Sources */, 204 | C2BC28A81C377B0900CD8839 /* HMScannerBorder.m in Sources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXSourcesBuildPhase section */ 209 | 210 | /* Begin PBXVariantGroup section */ 211 | C2D6BD031C377219000EC35D /* Main.storyboard */ = { 212 | isa = PBXVariantGroup; 213 | children = ( 214 | C2D6BD041C377219000EC35D /* Base */, 215 | ); 216 | name = Main.storyboard; 217 | sourceTree = ""; 218 | }; 219 | C2D6BD081C377219000EC35D /* LaunchScreen.storyboard */ = { 220 | isa = PBXVariantGroup; 221 | children = ( 222 | C2D6BD091C377219000EC35D /* Base */, 223 | ); 224 | name = LaunchScreen.storyboard; 225 | sourceTree = ""; 226 | }; 227 | /* End PBXVariantGroup section */ 228 | 229 | /* Begin XCBuildConfiguration section */ 230 | C2D6BD0C1C377219000EC35D /* Debug */ = { 231 | isa = XCBuildConfiguration; 232 | buildSettings = { 233 | ALWAYS_SEARCH_USER_PATHS = NO; 234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 235 | CLANG_CXX_LIBRARY = "libc++"; 236 | CLANG_ENABLE_MODULES = YES; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 241 | CLANG_WARN_EMPTY_BODY = YES; 242 | CLANG_WARN_ENUM_CONVERSION = YES; 243 | CLANG_WARN_INFINITE_RECURSION = YES; 244 | CLANG_WARN_INT_CONVERSION = YES; 245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 246 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 247 | CLANG_WARN_UNREACHABLE_CODE = YES; 248 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 249 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 250 | COPY_PHASE_STRIP = NO; 251 | DEBUG_INFORMATION_FORMAT = dwarf; 252 | ENABLE_STRICT_OBJC_MSGSEND = YES; 253 | ENABLE_TESTABILITY = YES; 254 | GCC_C_LANGUAGE_STANDARD = gnu99; 255 | GCC_DYNAMIC_NO_PIC = NO; 256 | GCC_NO_COMMON_BLOCKS = YES; 257 | GCC_OPTIMIZATION_LEVEL = 0; 258 | GCC_PREPROCESSOR_DEFINITIONS = ( 259 | "DEBUG=1", 260 | "$(inherited)", 261 | ); 262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 264 | GCC_WARN_UNDECLARED_SELECTOR = YES; 265 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 266 | GCC_WARN_UNUSED_FUNCTION = YES; 267 | GCC_WARN_UNUSED_VARIABLE = YES; 268 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 269 | MTL_ENABLE_DEBUG_INFO = YES; 270 | ONLY_ACTIVE_ARCH = YES; 271 | SDKROOT = iphoneos; 272 | TARGETED_DEVICE_FAMILY = "1,2"; 273 | }; 274 | name = Debug; 275 | }; 276 | C2D6BD0D1C377219000EC35D /* Release */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ALWAYS_SEARCH_USER_PATHS = NO; 280 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 281 | CLANG_CXX_LIBRARY = "libc++"; 282 | CLANG_ENABLE_MODULES = YES; 283 | CLANG_ENABLE_OBJC_ARC = YES; 284 | CLANG_WARN_BOOL_CONVERSION = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 287 | CLANG_WARN_EMPTY_BODY = YES; 288 | CLANG_WARN_ENUM_CONVERSION = YES; 289 | CLANG_WARN_INFINITE_RECURSION = YES; 290 | CLANG_WARN_INT_CONVERSION = YES; 291 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 292 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 293 | CLANG_WARN_UNREACHABLE_CODE = YES; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 298 | ENABLE_NS_ASSERTIONS = NO; 299 | ENABLE_STRICT_OBJC_MSGSEND = YES; 300 | GCC_C_LANGUAGE_STANDARD = gnu99; 301 | GCC_NO_COMMON_BLOCKS = YES; 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 309 | MTL_ENABLE_DEBUG_INFO = NO; 310 | SDKROOT = iphoneos; 311 | TARGETED_DEVICE_FAMILY = "1,2"; 312 | VALIDATE_PRODUCT = YES; 313 | }; 314 | name = Release; 315 | }; 316 | C2D6BD0F1C377219000EC35D /* Debug */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 320 | DEVELOPMENT_TEAM = J9XHDK444E; 321 | INFOPLIST_FILE = HMQRCodeScanner/Info.plist; 322 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 323 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 324 | PRODUCT_BUNDLE_IDENTIFIER = com.itheima.HMQRCodeScanner; 325 | PRODUCT_NAME = "$(TARGET_NAME)"; 326 | }; 327 | name = Debug; 328 | }; 329 | C2D6BD101C377219000EC35D /* Release */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 333 | DEVELOPMENT_TEAM = J9XHDK444E; 334 | INFOPLIST_FILE = HMQRCodeScanner/Info.plist; 335 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 336 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 337 | PRODUCT_BUNDLE_IDENTIFIER = com.itheima.HMQRCodeScanner; 338 | PRODUCT_NAME = "$(TARGET_NAME)"; 339 | }; 340 | name = Release; 341 | }; 342 | /* End XCBuildConfiguration section */ 343 | 344 | /* Begin XCConfigurationList section */ 345 | C2D6BCF21C377219000EC35D /* Build configuration list for PBXProject "HMQRCodeScanner" */ = { 346 | isa = XCConfigurationList; 347 | buildConfigurations = ( 348 | C2D6BD0C1C377219000EC35D /* Debug */, 349 | C2D6BD0D1C377219000EC35D /* Release */, 350 | ); 351 | defaultConfigurationIsVisible = 0; 352 | defaultConfigurationName = Release; 353 | }; 354 | C2D6BD0E1C377219000EC35D /* Build configuration list for PBXNativeTarget "HMQRCodeScanner" */ = { 355 | isa = XCConfigurationList; 356 | buildConfigurations = ( 357 | C2D6BD0F1C377219000EC35D /* Debug */, 358 | C2D6BD101C377219000EC35D /* Release */, 359 | ); 360 | defaultConfigurationIsVisible = 0; 361 | defaultConfigurationName = Release; 362 | }; 363 | /* End XCConfigurationList section */ 364 | }; 365 | rootObject = C2D6BCEF1C377219000EC35D /* Project object */; 366 | } 367 | -------------------------------------------------------------------------------- /HMQRCodeScanner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HMQRCodeScanner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /HMQRCodeScanner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /HMQRCodeScanner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /HMQRCodeScanner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HMQRCodeScanner/Assets.xcassets/avatar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "avatar.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /HMQRCodeScanner/Assets.xcassets/avatar.imageset/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufan321/HMQRCodeScanner/ec455fd7eee7a0f992e3b151062f3883d6797a5a/HMQRCodeScanner/Assets.xcassets/avatar.imageset/avatar.jpg -------------------------------------------------------------------------------- /HMQRCodeScanner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /HMQRCodeScanner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /HMQRCodeScanner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPhotoLibraryUsageDescription 6 | 请授权访问相册 7 | NSCameraUsageDescription 8 | 请授权访问相机 9 | CFBundleDevelopmentRegion 10 | en 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1 27 | LSRequiresIPhoneOS 28 | 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIMainStoryboardFile 32 | Main 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanerCardViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HMScanerCardViewController.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/3. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HMScanerCardViewController : UIViewController 12 | 13 | - (instancetype)initWithCardName:(NSString *)cardName avatar:(UIImage *)avatar; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanerCardViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HMScanerCardViewController.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/3. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "HMScanerCardViewController.h" 10 | #import "HMScanner.h" 11 | 12 | @interface HMScanerCardViewController() 13 | /// 名片字符串 14 | @property (nonatomic) NSString *cardName; 15 | /// 头像图片 16 | @property (nonatomic) UIImage *avatar; 17 | @end 18 | 19 | @implementation HMScanerCardViewController { 20 | UIImageView *cardImageView; 21 | } 22 | 23 | #pragma mark - 构造函数 24 | - (instancetype)initWithCardName:(NSString *)cardName avatar:(UIImage *)avatar { 25 | self = [super init]; 26 | if (self) { 27 | self.cardName = cardName; 28 | self.avatar = avatar; 29 | } 30 | return self; 31 | } 32 | 33 | #pragma mark - 设置界面 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | 37 | self.view.backgroundColor = [UIColor darkGrayColor]; 38 | [self prepareNavigationBar]; 39 | 40 | CGFloat width = self.view.bounds.size.width - 80; 41 | cardImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, width, width)]; 42 | cardImageView.center = self.view.center; 43 | 44 | [self.view addSubview:cardImageView]; 45 | 46 | [HMScanner qrImageWithString:self.cardName avatar:self.avatar completion:^(UIImage *image) { 47 | cardImageView.image = image; 48 | }]; 49 | } 50 | 51 | /// 准备导航栏 52 | - (void)prepareNavigationBar { 53 | // 1> 背景颜色 54 | [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithWhite:0.1 alpha:1.0]]; 55 | self.navigationController.navigationBar.translucent = YES; 56 | self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init]; 57 | 58 | // 2> 标题 59 | self.title = @"我的名片"; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanner.bundle/QRCodeScanLine@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufan321/HMQRCodeScanner/ec455fd7eee7a0f992e3b151062f3883d6797a5a/HMQRCodeScanner/QRCode/HMScanner.bundle/QRCodeScanLine@2x.png -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufan321/HMQRCodeScanner/ec455fd7eee7a0f992e3b151062f3883d6797a5a/HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR1@2x.png -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufan321/HMQRCodeScanner/ec455fd7eee7a0f992e3b151062f3883d6797a5a/HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR2@2x.png -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufan321/HMQRCodeScanner/ec455fd7eee7a0f992e3b151062f3883d6797a5a/HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR3@2x.png -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufan321/HMQRCodeScanner/ec455fd7eee7a0f992e3b151062f3883d6797a5a/HMQRCodeScanner/QRCode/HMScanner.bundle/ScanQR4@2x.png -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanner.h: -------------------------------------------------------------------------------- 1 | // 2 | // HMScanner.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /// 二维码/条码扫描器 12 | @interface HMScanner : NSObject 13 | 14 | /// 使用视图实例化扫描器,扫描预览窗口会添加到指定视图中 15 | /// 16 | /// @param view 指定的视图 17 | /// @param scanFrame 扫描范围 18 | /// @param completion 完成回调 19 | /// 20 | /// @return 扫描器 21 | + (instancetype)scanerWithView:(UIView *)view scanFrame:(CGRect)scanFrame completion:(void (^)(NSString *stringValue))completion; 22 | 23 | /// 扫描图像 24 | /// 25 | /// @param image 包含二维码的图像 26 | /// @remark 目前只支持 64 位的 iOS 设备 27 | + (void)scaneImage:(UIImage *)image completion:(void (^)(NSArray *values))completion; 28 | 29 | /// 使用 string / 头像 异步生成二维码图像 30 | /// 31 | /// @param string 二维码图像的字符串 32 | /// @param avatar 头像图像,默认比例 0.2 33 | /// @param completion 完成回调 34 | + (void)qrImageWithString:(NSString *)string avatar:(UIImage *)avatar completion:(void (^)(UIImage *image))completion; 35 | 36 | /// 使用 string / 头像 异步生成二维码图像,并且指定头像占二维码图像的比例 37 | /// 38 | /// @param string 二维码图像的字符串 39 | /// @param avatar 头像图像 40 | /// @param scale 头像占二维码图像的比例 41 | /// @param completion 完成回调 42 | + (void)qrImageWithString:(NSString *)string avatar:(UIImage *)avatar scale:(CGFloat)scale completion:(void (^)(UIImage *))completion; 43 | 44 | /// 开始扫描 45 | - (void)startScan; 46 | /// 停止扫描 47 | - (void)stopScan; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScanner.m: -------------------------------------------------------------------------------- 1 | // 2 | // HMScanner.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "HMScanner.h" 10 | #import 11 | 12 | /// 最大检测次数 13 | #define kMaxDetectedCount 20 14 | 15 | @interface HMScanner() 16 | /// 父视图弱引用 17 | @property (nonatomic, weak) UIView *parentView; 18 | /// 扫描范围 19 | @property (nonatomic) CGRect scanFrame; 20 | /// 完成回调 21 | @property (nonatomic, copy) void (^completionCallBack)(NSString *); 22 | @end 23 | 24 | @implementation HMScanner { 25 | /// 拍摄会话 26 | AVCaptureSession *session; 27 | /// 预览图层 28 | AVCaptureVideoPreviewLayer *previewLayer; 29 | /// 绘制图层 30 | CALayer *drawLayer; 31 | /// 当前检测计数 32 | NSInteger currentDetectedCount; 33 | } 34 | 35 | #pragma mark - 生成二维码 36 | + (void)qrImageWithString:(NSString *)string avatar:(UIImage *)avatar completion:(void (^)(UIImage *))completion { 37 | [self qrImageWithString:string avatar:avatar scale:0.20 completion:completion]; 38 | } 39 | 40 | + (void)qrImageWithString:(NSString *)string avatar:(UIImage *)avatar scale:(CGFloat)scale completion:(void (^)(UIImage *))completion { 41 | 42 | NSAssert(completion != nil, @"必须传入完成回调"); 43 | 44 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 45 | 46 | CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; 47 | 48 | [qrFilter setDefaults]; 49 | [qrFilter setValue:[string dataUsingEncoding:NSUTF8StringEncoding] forKey:@"inputMessage"]; 50 | 51 | CIImage *ciImage = qrFilter.outputImage; 52 | 53 | CGAffineTransform transform = CGAffineTransformMakeScale(10, 10); 54 | CIImage *transformedImage = [ciImage imageByApplyingTransform:transform]; 55 | 56 | CIContext *context = [CIContext contextWithOptions:nil]; 57 | CGImageRef cgImage = [context createCGImage:transformedImage fromRect:transformedImage.extent]; 58 | UIImage *qrImage = [UIImage imageWithCGImage:cgImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]; 59 | CGImageRelease(cgImage); 60 | 61 | if (avatar != nil) { 62 | qrImage = [self qrcodeImage:qrImage addAvatar:avatar scale:scale]; 63 | } 64 | 65 | dispatch_async(dispatch_get_main_queue(), ^{ completion(qrImage); }); 66 | }); 67 | } 68 | 69 | + (UIImage *)qrcodeImage:(UIImage *)qrImage addAvatar:(UIImage *)avatar scale:(CGFloat)scale { 70 | 71 | CGFloat screenScale = [UIScreen mainScreen].scale; 72 | CGRect rect = CGRectMake(0, 0, qrImage.size.width * screenScale, qrImage.size.height * screenScale); 73 | 74 | UIGraphicsBeginImageContextWithOptions(rect.size, YES, screenScale); 75 | 76 | [qrImage drawInRect:rect]; 77 | 78 | CGSize avatarSize = CGSizeMake(rect.size.width * scale, rect.size.height * scale); 79 | CGFloat x = (rect.size.width - avatarSize.width) * 0.5; 80 | CGFloat y = (rect.size.height - avatarSize.height) * 0.5; 81 | [avatar drawInRect:CGRectMake(x, y, avatarSize.width, avatarSize.height)]; 82 | 83 | UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); 84 | 85 | UIGraphicsEndImageContext(); 86 | 87 | return [UIImage imageWithCGImage:result.CGImage scale:screenScale orientation:UIImageOrientationUp]; 88 | } 89 | 90 | #pragma mark - 扫描图像方法 91 | + (void)scaneImage:(UIImage *)image completion:(void (^)(NSArray *))completion { 92 | 93 | NSAssert(completion != nil, @"必须传入完成回调"); 94 | 95 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 96 | 97 | CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; 98 | 99 | CIImage *ciImage = [[CIImage alloc] initWithImage:image]; 100 | 101 | NSArray *features = [detector featuresInImage:ciImage]; 102 | 103 | NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:features.count]; 104 | for (CIQRCodeFeature *feature in features) { 105 | [arrayM addObject:feature.messageString]; 106 | } 107 | 108 | dispatch_async(dispatch_get_main_queue(), ^{ 109 | completion(arrayM.copy); 110 | }); 111 | }); 112 | } 113 | 114 | #pragma mark - 构造函数 115 | + (instancetype)scanerWithView:(UIView *)view scanFrame:(CGRect)scanFrame completion:(void (^)(NSString *))completion { 116 | NSAssert(completion != nil, @"必须传入完成回调"); 117 | 118 | return [[self alloc] initWithView:view scanFrame:scanFrame completion:completion]; 119 | } 120 | 121 | - (instancetype)initWithView:(UIView *)view scanFrame:(CGRect)scanFrame completion:(void (^)(NSString *))completion { 122 | self = [super init]; 123 | 124 | if (self) { 125 | self.parentView = view; 126 | self.scanFrame = scanFrame; 127 | self.completionCallBack = completion; 128 | 129 | [self setupSession]; 130 | } 131 | return self; 132 | } 133 | 134 | #pragma mark - 公共方法 135 | /// 开始扫描 136 | - (void)startScan { 137 | if ([session isRunning]) { 138 | return; 139 | } 140 | currentDetectedCount = 0; 141 | 142 | [session startRunning]; 143 | } 144 | 145 | - (void)stopScan { 146 | if (![session isRunning]) { 147 | return; 148 | } 149 | [session stopRunning]; 150 | } 151 | 152 | #pragma mark - AVCaptureMetadataOutputObjectsDelegate 153 | - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { 154 | 155 | [self clearDrawLayer]; 156 | 157 | for (id obj in metadataObjects) { 158 | // 判断检测到的对象类型 159 | if (![obj isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) { 160 | return; 161 | } 162 | 163 | // 转换对象坐标 164 | AVMetadataMachineReadableCodeObject *dataObject = (AVMetadataMachineReadableCodeObject *)[previewLayer transformedMetadataObjectForMetadataObject:obj]; 165 | 166 | // 判断扫描范围 167 | if (!CGRectContainsRect(self.scanFrame, dataObject.bounds)) { 168 | continue; 169 | } 170 | 171 | if (currentDetectedCount++ < kMaxDetectedCount) { 172 | // 绘制边角 173 | [self drawCornersShape:dataObject]; 174 | } else { 175 | [self stopScan]; 176 | 177 | // 完成回调 178 | if (self.completionCallBack != nil) { 179 | self.completionCallBack(dataObject.stringValue); 180 | } 181 | } 182 | } 183 | } 184 | 185 | /// 清空绘制图层 186 | - (void)clearDrawLayer { 187 | if (drawLayer.sublayers.count == 0) { 188 | return; 189 | } 190 | 191 | [drawLayer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 192 | } 193 | 194 | /// 绘制条码形状 195 | /// 196 | /// @param dataObject 识别到的数据对象 197 | - (void)drawCornersShape:(AVMetadataMachineReadableCodeObject *)dataObject { 198 | 199 | if (dataObject.corners.count == 0) { 200 | return; 201 | } 202 | 203 | CAShapeLayer *layer = [CAShapeLayer layer]; 204 | 205 | layer.lineWidth = 4; 206 | layer.strokeColor = [UIColor greenColor].CGColor; 207 | layer.fillColor = [UIColor clearColor].CGColor; 208 | layer.path = [self cornersPath:dataObject.corners]; 209 | 210 | [drawLayer addSublayer:layer]; 211 | } 212 | 213 | /// 使用 corners 数组生成绘制路径 214 | /// 215 | /// @param corners corners 数组 216 | /// 217 | /// @return 绘制路径 218 | - (CGPathRef)cornersPath:(NSArray *)corners { 219 | 220 | UIBezierPath *path = [UIBezierPath bezierPath]; 221 | CGPoint point = CGPointZero; 222 | 223 | // 1. 移动到第一个点 224 | NSInteger index = 0; 225 | CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)corners[index++], &point); 226 | [path moveToPoint:point]; 227 | 228 | // 2. 遍历剩余的点 229 | while (index < corners.count) { 230 | CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)corners[index++], &point); 231 | [path addLineToPoint:point]; 232 | } 233 | 234 | // 3. 关闭路径 235 | [path closePath]; 236 | 237 | return path.CGPath; 238 | } 239 | 240 | #pragma mark - 扫描相关方法 241 | /// 设置绘制图层和预览图层 242 | - (void)setupLayers { 243 | 244 | if (self.parentView == nil) { 245 | NSLog(@"父视图不存在"); 246 | return; 247 | } 248 | 249 | if (session == nil) { 250 | NSLog(@"拍摄会话不存在"); 251 | return; 252 | } 253 | 254 | // 绘制图层 255 | drawLayer = [CALayer layer]; 256 | 257 | drawLayer.frame = self.parentView.bounds; 258 | 259 | [self.parentView.layer insertSublayer:drawLayer atIndex:0]; 260 | 261 | // 预览图层 262 | previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; 263 | 264 | previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; 265 | previewLayer.frame = self.parentView.bounds; 266 | 267 | [self.parentView.layer insertSublayer:previewLayer atIndex:0]; 268 | } 269 | 270 | /// 设置扫描会话 271 | - (void)setupSession { 272 | 273 | // 1> 输入设备 274 | AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 275 | AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; 276 | 277 | if (videoInput == nil) { 278 | NSLog(@"创建输入设备失败"); 279 | return; 280 | } 281 | 282 | // 2> 数据输出 283 | AVCaptureMetadataOutput *dataOutput = [[AVCaptureMetadataOutput alloc] init]; 284 | 285 | // 3> 拍摄会话 - 判断能够添加设备 286 | session = [[AVCaptureSession alloc] init]; 287 | if (![session canAddInput:videoInput]) { 288 | NSLog(@"无法添加输入设备"); 289 | session = nil; 290 | 291 | return; 292 | } 293 | if (![session canAddOutput:dataOutput]) { 294 | NSLog(@"无法添加输入设备"); 295 | session = nil; 296 | 297 | return; 298 | } 299 | 300 | // 4> 添加输入/输出设备 301 | [session addInput:videoInput]; 302 | [session addOutput:dataOutput]; 303 | 304 | // 5> 设置扫描类型 305 | dataOutput.metadataObjectTypes = dataOutput.availableMetadataObjectTypes; 306 | [dataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; 307 | 308 | // 6> 设置预览图层会话 309 | [self setupLayers]; 310 | } 311 | 312 | @end 313 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerBorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerBorder.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /// 扫描框视图 12 | @interface HMScannerBorder : UIView 13 | 14 | /// 开始扫描动画 15 | - (void)startScannerAnimating; 16 | /// 停止扫描动画 17 | - (void)stopScannerAnimating; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerBorder.m: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerBorder.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "HMScannerBorder.h" 10 | 11 | @implementation HMScannerBorder { 12 | /// 冲击波图像 13 | UIImageView *scannerLine; 14 | } 15 | 16 | - (instancetype)initWithFrame:(CGRect)frame { 17 | self = [super initWithFrame:frame]; 18 | if (self) { 19 | [self prepareUI]; 20 | } 21 | return self; 22 | } 23 | 24 | #pragma mark - 扫描动画方法 25 | /// 开始扫描动画 26 | - (void)startScannerAnimating { 27 | 28 | [self stopScannerAnimating]; 29 | 30 | [UIView animateWithDuration:3.0 31 | delay:0.0 32 | options:UIViewAnimationOptionCurveEaseInOut 33 | animations:^{ 34 | [UIView setAnimationRepeatCount:MAXFLOAT]; 35 | 36 | scannerLine.center = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height); 37 | } completion:nil]; 38 | } 39 | 40 | /// 停止扫描动画 41 | - (void)stopScannerAnimating { 42 | [scannerLine.layer removeAllAnimations]; 43 | scannerLine.center = CGPointMake(self.bounds.size.width * 0.5, 0); 44 | } 45 | 46 | #pragma mark - 设置界面 47 | - (void)prepareUI { 48 | self.clipsToBounds = YES; 49 | 50 | // 图像文件包 51 | NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 52 | NSURL *url = [bundle URLForResource:@"HMScanner" withExtension:@"bundle"]; 53 | NSBundle *imageBundle = [NSBundle bundleWithURL:url]; 54 | 55 | // 冲击波图像 56 | scannerLine = [[UIImageView alloc] initWithImage:[self imageWithName:@"QRCodeScanLine" bundle:imageBundle]]; 57 | 58 | scannerLine.frame = CGRectMake(0, 0, self.bounds.size.width, scannerLine.bounds.size.height); 59 | scannerLine.center = CGPointMake(self.bounds.size.width * 0.5, 0); 60 | 61 | [self addSubview:scannerLine]; 62 | 63 | // 加载边框图像 64 | for (NSInteger i = 1; i < 5; i++) { 65 | NSString *imgName = [NSString stringWithFormat:@"ScanQR%zd", i]; 66 | UIImageView *img = [[UIImageView alloc] initWithImage:[self imageWithName:imgName bundle:imageBundle]]; 67 | 68 | [self addSubview:img]; 69 | 70 | CGFloat offsetX = self.bounds.size.width - img.bounds.size.width; 71 | CGFloat offsetY = self.bounds.size.height - img.bounds.size.height; 72 | 73 | switch (i) { 74 | case 2: 75 | img.frame = CGRectOffset(img.frame, offsetX, 0); 76 | break; 77 | case 3: 78 | img.frame = CGRectOffset(img.frame, 0, offsetY); 79 | break; 80 | case 4: 81 | img.frame = CGRectOffset(img.frame, offsetX, offsetY); 82 | break; 83 | default: 84 | break; 85 | } 86 | } 87 | } 88 | 89 | - (UIImage *)imageWithName:(NSString *)imageName bundle:(NSBundle *)imageBundle { 90 | NSString *fileName = [NSString stringWithFormat:@"%@@2x", imageName]; 91 | NSString *path = [imageBundle pathForResource:fileName ofType:@"png"]; 92 | 93 | return [[UIImage imageWithContentsOfFile:path] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerController.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | 扫描控制器 13 | 14 | 作用: 15 | 16 | * 提供一个导航控制器,扫描 `二维码 / 条形码` 17 | * 能够生成指定 `字符串` + `avatar(可选)` 的二维码名片 18 | * 能够识别相册图片中的二维码(iOS 64 位设备) 19 | 20 | 使用: 21 | 22 | @code 23 | NSString *cardName = @"天涯刀哥 - 傅红雪"; 24 | UIImage *avatar = [UIImage imageNamed:@"avatar"]; 25 | 26 | // 实例化控制器,并指定完成回调 27 | HMScannerController *scanner = [HMScannerController scannerWithCardName:cardName avatar:avatar completion:^(NSString *stringValue) { 28 | 29 | self.scanResultLabel.text = stringValue; 30 | }]; 31 | 32 | // 设置导航标题样式 33 | [scanner setTitleColor:[UIColor whiteColor] tintColor:[UIColor greenColor]]; 34 | 35 | // 展现扫描控制器 36 | [self showDetailViewController:scanner sender:nil]; 37 | 38 | @endcode 39 | */ 40 | @interface HMScannerController : UINavigationController 41 | 42 | /// 使用 `名片字符串` 实例化扫描导航控制器 43 | /// 44 | /// @param cardName 名片字符串 45 | /// @param avatar 头像图像 46 | /// @param completion 完成回调 47 | /// 48 | /// @return 扫描导航控制器 49 | + (instancetype)scannerWithCardName:(NSString *)cardName avatar:(UIImage *)avatar completion:(void (^)(NSString *stringValue))completion; 50 | 51 | /// 使用 名片字符串 / 头像 异步生成二维码图像,并且指定头像占二维码图像的比例 52 | /// 53 | /// @param string 名片字符串 54 | /// @param avatar 头像图像 55 | /// @param scale 头像占二维码图像的比例 56 | /// @param completion 完成回调 57 | + (void)cardImageWithCardName:(NSString *)cardName avatar:(UIImage *)avatar scale:(CGFloat)scale completion:(void (^)(UIImage *image))completion; 58 | 59 | /// 设置导航栏标题颜色和 tintColor 60 | /// 61 | /// @param titleColor 标题颜色 62 | /// @param tintColor tintColor 63 | - (void)setTitleColor:(UIColor *)titleColor tintColor:(UIColor *)tintColor; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerController.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "HMScannerController.h" 10 | #import "HMScannerViewController.h" 11 | #import "HMScanner.h" 12 | 13 | @implementation HMScannerController 14 | 15 | + (void)cardImageWithCardName:(NSString *)cardName avatar:(UIImage *)avatar scale:(CGFloat)scale completion:(void (^)(UIImage *))completion { 16 | [HMScanner qrImageWithString:cardName avatar:avatar scale:scale completion:completion]; 17 | } 18 | 19 | + (instancetype)scannerWithCardName:(NSString *)cardName avatar:(UIImage *)avatar completion:(void (^)(NSString *))completion { 20 | NSAssert(completion != nil, @"必须传入完成回调"); 21 | 22 | return [[self alloc] initWithCardName:cardName avatar:avatar completion:completion]; 23 | } 24 | 25 | - (instancetype)initWithCardName:(NSString *)cardName avatar:(UIImage *)avatar completion:(void (^)(NSString *))completion { 26 | self = [super init]; 27 | if (self) { 28 | HMScannerViewController *scanner = [[HMScannerViewController alloc] initWithCardName:cardName avatar:avatar completion:completion]; 29 | 30 | [self setTitleColor:[UIColor whiteColor] tintColor:[UIColor greenColor]]; 31 | 32 | [self pushViewController:scanner animated:NO]; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)setTitleColor:(UIColor *)titleColor tintColor:(UIColor *)tintColor { 38 | [self.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName: titleColor}]; 39 | self.navigationBar.tintColor = tintColor; 40 | } 41 | 42 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations { 43 | return UIInterfaceOrientationMaskPortrait; 44 | } 45 | 46 | - (UIStatusBarStyle)preferredStatusBarStyle { 47 | return UIStatusBarStyleLightContent; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerMaskView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerMaskView.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/3. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /// 扫描遮罩视图 12 | @interface HMScannerMaskView : UIView 13 | 14 | /// 使用裁切区域实例化遮罩视图 15 | /// 16 | /// @param frame 视图区域 17 | /// @param cropRect 裁切区域 18 | /// 19 | /// @return 遮罩视图 20 | + (instancetype)maskViewWithFrame:(CGRect)frame cropRect:(CGRect)cropRect; 21 | 22 | /// 裁切区域 23 | @property (nonatomic) CGRect cropRect; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerMaskView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerMaskView.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/3. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "HMScannerMaskView.h" 10 | 11 | @implementation HMScannerMaskView 12 | 13 | + (instancetype)maskViewWithFrame:(CGRect)frame cropRect:(CGRect)cropRect { 14 | 15 | HMScannerMaskView *maskView = [[self alloc] initWithFrame:frame]; 16 | 17 | maskView.backgroundColor = [UIColor clearColor]; 18 | maskView.cropRect = cropRect; 19 | 20 | return maskView; 21 | } 22 | 23 | - (void)setCropRect:(CGRect)cropRect { 24 | _cropRect = cropRect; 25 | 26 | [self setNeedsDisplay]; 27 | } 28 | 29 | - (void)drawRect:(CGRect)rect { 30 | 31 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 32 | 33 | [[UIColor colorWithWhite:0.0 alpha:0.4] setFill]; 34 | CGContextFillRect(ctx, rect); 35 | 36 | CGContextClearRect(ctx, self.cropRect); 37 | 38 | [[UIColor colorWithWhite:0.95 alpha:1.0] setStroke]; 39 | CGContextStrokeRectWithWidth(ctx, CGRectInset(_cropRect, 1, 1), 1); 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerViewController.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /// 扫描控制器 12 | @interface HMScannerViewController : UIViewController 13 | 14 | /// 实例化扫描控制器 15 | /// 16 | /// @param cardName 名片字符串 17 | /// @param avatar 头像图片 18 | /// @param completion 完成回调 19 | /// 20 | /// @return 扫描控制器 21 | - (instancetype)initWithCardName:(NSString *)cardName avatar:(UIImage *)avatar completion:(void (^)(NSString *stringValue))completion; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /HMQRCodeScanner/QRCode/HMScannerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HMScannerViewController.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "HMScannerViewController.h" 10 | #import "HMScanerCardViewController.h" 11 | #import "HMScannerBorder.h" 12 | #import "HMScannerMaskView.h" 13 | #import "HMScanner.h" 14 | 15 | /// 控件间距 16 | #define kControlMargin 32.0 17 | /// 相册图片最大尺寸 18 | #define kImageMaxSize CGSizeMake(1000, 1000) 19 | 20 | @interface HMScannerViewController () 21 | /// 名片字符串 22 | @property (nonatomic) NSString *cardName; 23 | /// 头像图片 24 | @property (nonatomic) UIImage *avatar; 25 | /// 完成回调 26 | @property (nonatomic, copy) void (^completionCallBack)(NSString *); 27 | @end 28 | 29 | @implementation HMScannerViewController { 30 | /// 扫描框 31 | HMScannerBorder *scannerBorder; 32 | /// 扫描器 33 | HMScanner *scanner; 34 | /// 提示标签 35 | UILabel *tipLabel; 36 | } 37 | 38 | - (instancetype)initWithCardName:(NSString *)cardName avatar:(UIImage *)avatar completion:(void (^)(NSString *))completion { 39 | self = [super init]; 40 | if (self) { 41 | self.cardName = cardName; 42 | self.avatar = avatar; 43 | self.completionCallBack = completion; 44 | } 45 | return self; 46 | } 47 | 48 | - (void)viewDidLoad { 49 | [super viewDidLoad]; 50 | 51 | [self prepareUI]; 52 | 53 | // 实例化扫描器 54 | __weak typeof(self) weakSelf = self; 55 | scanner = [HMScanner scanerWithView:self.view scanFrame:scannerBorder.frame completion:^(NSString *stringValue) { 56 | // 完成回调 57 | weakSelf.completionCallBack(stringValue); 58 | 59 | // 关闭 60 | [weakSelf clickCloseButton]; 61 | }]; 62 | } 63 | 64 | - (void)viewWillAppear:(BOOL)animated { 65 | [super viewWillAppear:animated]; 66 | 67 | [scannerBorder startScannerAnimating]; 68 | [scanner startScan]; 69 | } 70 | 71 | - (void)viewWillDisappear:(BOOL)animated { 72 | [super viewWillDisappear:animated]; 73 | 74 | [scannerBorder stopScannerAnimating]; 75 | [scanner stopScan]; 76 | } 77 | 78 | #pragma mark - 监听方法 79 | /// 点击关闭按钮 80 | - (void)clickCloseButton { 81 | [self dismissViewControllerAnimated:YES completion:nil]; 82 | } 83 | 84 | /// 点击相册按钮 85 | - (void)clickAlbumButton { 86 | 87 | if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { 88 | tipLabel.text = @"无法访问相册"; 89 | 90 | return; 91 | } 92 | 93 | UIImagePickerController *picker = [[UIImagePickerController alloc] init]; 94 | 95 | picker.view.backgroundColor = [UIColor whiteColor]; 96 | picker.delegate = self; 97 | 98 | [self showDetailViewController:picker sender:nil]; 99 | } 100 | 101 | /// 点击名片按钮 102 | - (void)clickCardButton { 103 | HMScanerCardViewController *vc = [[HMScanerCardViewController alloc] initWithCardName:self.cardName avatar:self.avatar]; 104 | 105 | [self showViewController:vc sender:nil]; 106 | } 107 | 108 | #pragma mark - UIImagePickerControllerDelegate 109 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { 110 | 111 | UIImage *image = [self resizeImage:info[UIImagePickerControllerOriginalImage]]; 112 | 113 | // 扫描图像 114 | [HMScanner scaneImage:image completion:^(NSArray *values) { 115 | 116 | if (values.count > 0) { 117 | self.completionCallBack(values.firstObject); 118 | [self dismissViewControllerAnimated:NO completion:^{ 119 | [self clickCloseButton]; 120 | }]; 121 | } else { 122 | tipLabel.text = @"没有识别到二维码,请选择其他照片"; 123 | 124 | [self dismissViewControllerAnimated:YES completion:nil]; 125 | } 126 | }]; 127 | } 128 | 129 | - (UIImage *)resizeImage:(UIImage *)image { 130 | 131 | if (image.size.width < kImageMaxSize.width && image.size.height < kImageMaxSize.height) { 132 | return image; 133 | } 134 | 135 | CGFloat xScale = kImageMaxSize.width / image.size.width; 136 | CGFloat yScale = kImageMaxSize.height / image.size.height; 137 | CGFloat scale = MIN(xScale, yScale); 138 | CGSize size = CGSizeMake(image.size.width * scale, image.size.height * scale); 139 | 140 | UIGraphicsBeginImageContext(size); 141 | 142 | [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; 143 | 144 | UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); 145 | 146 | UIGraphicsEndImageContext(); 147 | 148 | return result; 149 | } 150 | 151 | #pragma mark - 设置界面 152 | - (void)prepareUI { 153 | self.view.backgroundColor = [UIColor darkGrayColor]; 154 | 155 | [self prepareNavigationBar]; 156 | [self prepareScanerBorder]; 157 | [self prepareOtherControls]; 158 | } 159 | 160 | /// 准备提示标签和名片按钮 161 | - (void)prepareOtherControls { 162 | 163 | // 1> 提示标签 164 | tipLabel = [[UILabel alloc] init]; 165 | 166 | tipLabel.text = @"将二维码/条码放入框中,即可自动扫描"; 167 | tipLabel.font = [UIFont systemFontOfSize:12]; 168 | tipLabel.textColor = [UIColor whiteColor]; 169 | tipLabel.textAlignment = NSTextAlignmentCenter; 170 | 171 | [tipLabel sizeToFit]; 172 | tipLabel.center = CGPointMake(scannerBorder.center.x, CGRectGetMaxY(scannerBorder.frame) + kControlMargin); 173 | 174 | [self.view addSubview:tipLabel]; 175 | 176 | // 2> 名片按钮 177 | UIButton *cardButton = [[UIButton alloc] init]; 178 | 179 | [cardButton setTitle:@"我的名片" forState:UIControlStateNormal]; 180 | cardButton.titleLabel.font = [UIFont systemFontOfSize:15]; 181 | [cardButton setTitleColor:self.navigationController.navigationBar.tintColor forState:UIControlStateNormal]; 182 | 183 | [cardButton sizeToFit]; 184 | cardButton.center = CGPointMake(tipLabel.center.x, CGRectGetMaxY(tipLabel.frame) + kControlMargin); 185 | 186 | [self.view addSubview:cardButton]; 187 | 188 | [cardButton addTarget:self action:@selector(clickCardButton) forControlEvents:UIControlEventTouchUpInside]; 189 | } 190 | 191 | /// 准备扫描框 192 | - (void)prepareScanerBorder { 193 | 194 | CGFloat width = self.view.bounds.size.width - 80; 195 | scannerBorder = [[HMScannerBorder alloc] initWithFrame:CGRectMake(0, 0, width, width)]; 196 | 197 | scannerBorder.center = self.view.center; 198 | scannerBorder.tintColor = self.navigationController.navigationBar.tintColor; 199 | 200 | [self.view addSubview:scannerBorder]; 201 | 202 | HMScannerMaskView *maskView = [HMScannerMaskView maskViewWithFrame:self.view.bounds cropRect:scannerBorder.frame]; 203 | [self.view insertSubview:maskView atIndex:0]; 204 | } 205 | 206 | /// 准备导航栏 207 | - (void)prepareNavigationBar { 208 | // 1> 背景颜色 209 | [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithWhite:0.1 alpha:1.0]]; 210 | self.navigationController.navigationBar.translucent = YES; 211 | self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init]; 212 | 213 | // 2> 标题 214 | self.title = @"扫一扫"; 215 | 216 | // 3> 左右按钮 217 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(clickCloseButton)]; 218 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"相册" style:UIBarButtonItemStylePlain target:self action:@selector(clickAlbumButton)]; 219 | } 220 | 221 | @end 222 | -------------------------------------------------------------------------------- /HMQRCodeScanner/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /HMQRCodeScanner/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "HMScannerController.h" 11 | 12 | @interface ViewController () 13 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 14 | @property (weak, nonatomic) IBOutlet UILabel *scanResultLabel; 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (IBAction)clickScanButton:(id)sender { 20 | 21 | NSString *cardName = @"天涯刀哥 - 傅红雪"; 22 | UIImage *avatar = [UIImage imageNamed:@"avatar"]; 23 | 24 | HMScannerController *scanner = [HMScannerController scannerWithCardName:cardName avatar:avatar completion:^(NSString *stringValue) { 25 | 26 | self.scanResultLabel.text = stringValue; 27 | }]; 28 | 29 | [scanner setTitleColor:[UIColor whiteColor] tintColor:[UIColor greenColor]]; 30 | 31 | [self showDetailViewController:scanner sender:nil]; 32 | } 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | 37 | NSString *cardName = @"天涯刀哥 - 傅红雪"; 38 | UIImage *avatar = [UIImage imageNamed:@"avatar"]; 39 | 40 | [HMScannerController cardImageWithCardName:cardName avatar:avatar scale:0.2 completion:^(UIImage *image) { 41 | self.imageView.image = image; 42 | }]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /HMQRCodeScanner/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HMQRCodeScanner 4 | // 5 | // Created by 刘凡 on 16/1/2. 6 | // Copyright © 2016年 itheima. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Fan Liu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HMQRCodeScanner 2 | 3 | 包含 UI 界面的轻量级二维码扫描及生成框架 4 | 5 | ## 功能 6 | 7 | * 提供一个导航控制器,扫描 `二维码 / 条形码` 8 | * 能够生成指定 `字符串` + `avatar(可选)` 的二维码名片 9 | * 能够识别相册图片中的二维码(iOS 64 位设备) 10 | 11 | ## 系统支持 12 | 13 | * iOS 8.0+ 14 | * Xcode 7.0 15 | 16 | ## 安装 17 | 18 | ### CocoaPods 19 | 20 | * 进入终端,`cd` 到项目目录,输入以下命令,建立 `Podfile` 21 | 22 | ```bash 23 | $ pod init 24 | ``` 25 | 26 | * 在 `Podfile` 中输入以下内容: 27 | 28 | ``` 29 | platform :ios, '8.0' 30 | use_frameworks! 31 | 32 | pod 'HMQRCodeScanner' 33 | ``` 34 | 35 | * 在终端中输入以下命令,安装 Pod 36 | 37 | ```bash 38 | $ pod install 39 | ``` 40 | 41 | ## 使用 42 | 43 | * 在 `Info.plist` 中添加两个 `值对` 以授权访问 `相机` 和 `相册` 44 | 45 | * `NSCameraUsageDescription` 46 | * `NSPhotoLibraryUsageDescription` 47 | 48 | 49 | ### Objective-C 50 | 51 | * 导入框架 52 | 53 | ```objc 54 | @import HMQRCodeScanner; 55 | ``` 56 | 57 | * 打开扫描控制器,扫描及完成回调 58 | 59 | ```objc 60 | NSString *cardName = @"天涯刀哥 - 傅红雪"; 61 | UIImage *avatar = [UIImage imageNamed:@"avatar"]; 62 | 63 | // 实例化扫描控制器 64 | HMScannerController *scanner = [HMScannerController scannerWithCardName:cardName avatar:avatar completion:^(NSString *stringValue) { 65 | 66 | self.scanResultLabel.text = stringValue; 67 | }]; 68 | 69 | // 设置导航栏样式 70 | [scanner setTitleColor:[UIColor whiteColor] tintColor:[UIColor greenColor]]; 71 | 72 | // 展现扫描控制器 73 | [self showDetailViewController:scanner sender:nil]; 74 | ``` 75 | 76 | * 生成二维码名片 77 | 78 | ```objc 79 | NSString *cardName = @"天涯刀哥 - 傅红雪"; 80 | UIImage *avatar = [UIImage imageNamed:@"avatar"]; 81 | 82 | [HMScannerController cardImageWithCardName:cardName avatar:avatar scale:0.2 completion:^(UIImage *image) { 83 | self.imageView.image = image; 84 | }]; 85 | ``` 86 | 87 | ### Swift 88 | 89 | * 导入框架 90 | 91 | ```swift 92 | import HMQRCodeScanner 93 | ``` 94 | 95 | * 打开扫描控制器,扫描及完成回调 96 | 97 | ```swift 98 | let cardName = "天涯刀哥 - 傅红雪" 99 | let avatar = UIImage(named: "avatar") 100 | 101 | let scanner = HMScannerController.scannerWithCardName(cardName, avatar: avatar) { (stringValue) -> Void in 102 | self.scanResultLabel.text = stringValue 103 | } 104 | 105 | self.showDetailViewController(scanner, sender: nil) 106 | ``` 107 | 108 | * 生成二维码名片 109 | 110 | ```swift 111 | let cardName = "天涯刀哥 - 傅红雪" 112 | let avatar = UIImage(named: "avatar") 113 | 114 | HMScannerController.cardImageWithCardName(cardName, avatar: avatar, scale: 0.2) { (image) -> Void in 115 | self.imageView.image = image 116 | } 117 | ``` 118 | 119 | --------------------------------------------------------------------------------