├── FZBluetooth.podspec ├── FZBluetoothExample ├── A62BABB1B55584D69200FCDA02F4B02F.png ├── FzBluetoothDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── fzh.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── fzh.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── FzBluetoothDemo.xcscheme │ │ └── xcschememanagement.plist ├── FzBluetoothDemo.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── fzh.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── FzBluetoothDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── FZBlueModel │ │ ├── FZBlueModel.h │ │ ├── FZBlueModel.m │ │ ├── FZSingletonManager.h │ │ └── FZSingletonManager.m │ ├── FZHProgressHUD │ │ ├── FZHProgressHUD.h │ │ └── FZHProgressHUD.m │ ├── Info.plist │ ├── SendViewController.h │ ├── SendViewController.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── Podfile ├── Podfile.lock ├── Pods │ ├── FZBluetooth │ │ ├── FZBluetoothExample │ │ │ └── FzBluetoothDemo │ │ │ │ └── FZHBluetooth │ │ │ │ ├── FzhBluetooth.h │ │ │ │ ├── FzhBluetooth.m │ │ │ │ ├── FzhCallBlock.h │ │ │ │ ├── FzhString.h │ │ │ │ └── FzhString.m │ │ ├── LICENSE │ │ └── README.md │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── xcuserdata │ │ │ └── fzh.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── FZBluetooth.xcscheme │ │ │ ├── Pods-FzBluetoothDemo.xcscheme │ │ │ └── xcschememanagement.plist │ └── Target Support Files │ │ ├── FZBluetooth │ │ ├── FZBluetooth-dummy.m │ │ ├── FZBluetooth-prefix.pch │ │ ├── FZBluetooth-umbrella.h │ │ ├── FZBluetooth.modulemap │ │ ├── FZBluetooth.xcconfig │ │ └── Info.plist │ │ └── Pods-FzBluetoothDemo │ │ ├── Info.plist │ │ ├── Pods-FzBluetoothDemo-acknowledgements.markdown │ │ ├── Pods-FzBluetoothDemo-acknowledgements.plist │ │ ├── Pods-FzBluetoothDemo-dummy.m │ │ ├── Pods-FzBluetoothDemo-frameworks.sh │ │ ├── Pods-FzBluetoothDemo-resources.sh │ │ ├── Pods-FzBluetoothDemo-umbrella.h │ │ ├── Pods-FzBluetoothDemo.debug.xcconfig │ │ ├── Pods-FzBluetoothDemo.modulemap │ │ └── Pods-FzBluetoothDemo.release.xcconfig └── 封装类 │ ├── FzhBluetooth.h │ ├── FzhBluetooth.m │ ├── FzhCallBlock.h │ ├── FzhString.h │ └── FzhString.m ├── LICENSE └── README.md /FZBluetooth.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | 4 | s.name = "FZBluetooth" 5 | s.version = "1.0.2" 6 | s.summary = "FZBluetooth is An extension based on system Bluetooth method development." 7 | s.description = <<-DESC 8 | An extension based on the system Bluetooth library, including Bluetooth basic functions such as lookup, connection, writing, and response. It is more convenient and lightweight to use after encapsulation. 9 | DESC 10 | s.homepage = "https://github.com/fuzheng0301/FZBluetooth" 11 | s.license = "MIT" 12 | s.author = { "fuzheng" => "13683568645@163.com" } 13 | s.platform = :ios, "8.0" 14 | s.source = { :git => "https://github.com/fuzheng0301/FZBluetooth.git", :tag => "#{s.version}" } 15 | s.frameworks = "UIKit", "CoreBluetooth", "Foundation" 16 | s.source_files = "FZHBluetooth", "FZBluetoothExample/FzBluetoothDemo/FZHBluetooth/**/*.{h,m}" 17 | 18 | 19 | end 20 | -------------------------------------------------------------------------------- /FZBluetoothExample/A62BABB1B55584D69200FCDA02F4B02F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzheng0301/FZBluetooth/5ff4bc786a2815f82e2294e46a427f842667f50a/FZBluetoothExample/A62BABB1B55584D69200FCDA02F4B02F.png -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3A2FC3281F6F7056003CFD82 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FC3271F6F7056003CFD82 /* main.m */; }; 11 | 3A2FC32B1F6F7056003CFD82 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FC32A1F6F7056003CFD82 /* AppDelegate.m */; }; 12 | 3A2FC32E1F6F7056003CFD82 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FC32D1F6F7056003CFD82 /* ViewController.m */; }; 13 | 3A2FC3311F6F7056003CFD82 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A2FC32F1F6F7056003CFD82 /* Main.storyboard */; }; 14 | 3A2FC3331F6F7056003CFD82 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3A2FC3321F6F7056003CFD82 /* Assets.xcassets */; }; 15 | 3A2FC3361F6F7056003CFD82 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A2FC3341F6F7056003CFD82 /* LaunchScreen.storyboard */; }; 16 | 3A2FC3491F6F745B003CFD82 /* SendViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FC3481F6F745B003CFD82 /* SendViewController.m */; }; 17 | 3A2FC34D1F6F7B52003CFD82 /* FZHProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FC34C1F6F7B52003CFD82 /* FZHProgressHUD.m */; }; 18 | 3A2FC3531F6F7B69003CFD82 /* FZBlueModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FC3501F6F7B69003CFD82 /* FZBlueModel.m */; }; 19 | 3A2FC3541F6F7B69003CFD82 /* FZSingletonManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FC3521F6F7B69003CFD82 /* FZSingletonManager.m */; }; 20 | F68F2032975246901EE21790 /* Pods_FzBluetoothDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F43885670B3A9C2486750C8 /* Pods_FzBluetoothDemo.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 01ED38D5F09A97134BAB225D /* Pods-FzBluetoothDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FzBluetoothDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo.release.xcconfig"; sourceTree = ""; }; 25 | 2F43885670B3A9C2486750C8 /* Pods_FzBluetoothDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FzBluetoothDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 3A2FC3231F6F7056003CFD82 /* FzBluetoothDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FzBluetoothDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 3A2FC3271F6F7056003CFD82 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 28 | 3A2FC3291F6F7056003CFD82 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 29 | 3A2FC32A1F6F7056003CFD82 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 30 | 3A2FC32C1F6F7056003CFD82 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 31 | 3A2FC32D1F6F7056003CFD82 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 32 | 3A2FC3301F6F7056003CFD82 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | 3A2FC3321F6F7056003CFD82 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | 3A2FC3351F6F7056003CFD82 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | 3A2FC3371F6F7056003CFD82 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 3A2FC3471F6F745B003CFD82 /* SendViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendViewController.h; sourceTree = ""; }; 37 | 3A2FC3481F6F745B003CFD82 /* SendViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SendViewController.m; sourceTree = ""; }; 38 | 3A2FC34B1F6F7B52003CFD82 /* FZHProgressHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FZHProgressHUD.h; sourceTree = ""; }; 39 | 3A2FC34C1F6F7B52003CFD82 /* FZHProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FZHProgressHUD.m; sourceTree = ""; }; 40 | 3A2FC34F1F6F7B69003CFD82 /* FZBlueModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FZBlueModel.h; sourceTree = ""; }; 41 | 3A2FC3501F6F7B69003CFD82 /* FZBlueModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FZBlueModel.m; sourceTree = ""; }; 42 | 3A2FC3511F6F7B69003CFD82 /* FZSingletonManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FZSingletonManager.h; sourceTree = ""; }; 43 | 3A2FC3521F6F7B69003CFD82 /* FZSingletonManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FZSingletonManager.m; sourceTree = ""; }; 44 | 83016D86BB9BFF397D061E1B /* Pods-FzBluetoothDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FzBluetoothDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo.debug.xcconfig"; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 3A2FC3201F6F7056003CFD82 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | F68F2032975246901EE21790 /* Pods_FzBluetoothDemo.framework in Frameworks */, 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 3A2FC31A1F6F7056003CFD82 = { 60 | isa = PBXGroup; 61 | children = ( 62 | 3A2FC3251F6F7056003CFD82 /* FzBluetoothDemo */, 63 | 3A2FC3241F6F7056003CFD82 /* Products */, 64 | D006CB779FD7FDA9C32D9656 /* Pods */, 65 | A4198C2027CC2ADE17EE6495 /* Frameworks */, 66 | ); 67 | sourceTree = ""; 68 | }; 69 | 3A2FC3241F6F7056003CFD82 /* Products */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 3A2FC3231F6F7056003CFD82 /* FzBluetoothDemo.app */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | 3A2FC3251F6F7056003CFD82 /* FzBluetoothDemo */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 3A2FC3291F6F7056003CFD82 /* AppDelegate.h */, 81 | 3A2FC32A1F6F7056003CFD82 /* AppDelegate.m */, 82 | 3A2FC34A1F6F7B52003CFD82 /* FZHProgressHUD */, 83 | 3A2FC34E1F6F7B69003CFD82 /* FZBlueModel */, 84 | 3A2FC32C1F6F7056003CFD82 /* ViewController.h */, 85 | 3A2FC32D1F6F7056003CFD82 /* ViewController.m */, 86 | 3A2FC3471F6F745B003CFD82 /* SendViewController.h */, 87 | 3A2FC3481F6F745B003CFD82 /* SendViewController.m */, 88 | 3A2FC32F1F6F7056003CFD82 /* Main.storyboard */, 89 | 3A2FC3321F6F7056003CFD82 /* Assets.xcassets */, 90 | 3A2FC3341F6F7056003CFD82 /* LaunchScreen.storyboard */, 91 | 3A2FC3371F6F7056003CFD82 /* Info.plist */, 92 | 3A2FC3261F6F7056003CFD82 /* Supporting Files */, 93 | ); 94 | path = FzBluetoothDemo; 95 | sourceTree = ""; 96 | }; 97 | 3A2FC3261F6F7056003CFD82 /* Supporting Files */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 3A2FC3271F6F7056003CFD82 /* main.m */, 101 | ); 102 | name = "Supporting Files"; 103 | sourceTree = ""; 104 | }; 105 | 3A2FC34A1F6F7B52003CFD82 /* FZHProgressHUD */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 3A2FC34B1F6F7B52003CFD82 /* FZHProgressHUD.h */, 109 | 3A2FC34C1F6F7B52003CFD82 /* FZHProgressHUD.m */, 110 | ); 111 | path = FZHProgressHUD; 112 | sourceTree = ""; 113 | }; 114 | 3A2FC34E1F6F7B69003CFD82 /* FZBlueModel */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 3A2FC34F1F6F7B69003CFD82 /* FZBlueModel.h */, 118 | 3A2FC3501F6F7B69003CFD82 /* FZBlueModel.m */, 119 | 3A2FC3511F6F7B69003CFD82 /* FZSingletonManager.h */, 120 | 3A2FC3521F6F7B69003CFD82 /* FZSingletonManager.m */, 121 | ); 122 | path = FZBlueModel; 123 | sourceTree = ""; 124 | }; 125 | A4198C2027CC2ADE17EE6495 /* Frameworks */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 2F43885670B3A9C2486750C8 /* Pods_FzBluetoothDemo.framework */, 129 | ); 130 | name = Frameworks; 131 | sourceTree = ""; 132 | }; 133 | D006CB779FD7FDA9C32D9656 /* Pods */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 83016D86BB9BFF397D061E1B /* Pods-FzBluetoothDemo.debug.xcconfig */, 137 | 01ED38D5F09A97134BAB225D /* Pods-FzBluetoothDemo.release.xcconfig */, 138 | ); 139 | name = Pods; 140 | sourceTree = ""; 141 | }; 142 | /* End PBXGroup section */ 143 | 144 | /* Begin PBXNativeTarget section */ 145 | 3A2FC3221F6F7056003CFD82 /* FzBluetoothDemo */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = 3A2FC33A1F6F7056003CFD82 /* Build configuration list for PBXNativeTarget "FzBluetoothDemo" */; 148 | buildPhases = ( 149 | 2379D0AB376BCAE7520D2CAB /* [CP] Check Pods Manifest.lock */, 150 | 3A2FC31F1F6F7056003CFD82 /* Sources */, 151 | 3A2FC3201F6F7056003CFD82 /* Frameworks */, 152 | 3A2FC3211F6F7056003CFD82 /* Resources */, 153 | C5B38E68CEC2B99D311B12D0 /* [CP] Embed Pods Frameworks */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | ); 159 | name = FzBluetoothDemo; 160 | productName = FzBluetoothDemo; 161 | productReference = 3A2FC3231F6F7056003CFD82 /* FzBluetoothDemo.app */; 162 | productType = "com.apple.product-type.application"; 163 | }; 164 | /* End PBXNativeTarget section */ 165 | 166 | /* Begin PBXProject section */ 167 | 3A2FC31B1F6F7056003CFD82 /* Project object */ = { 168 | isa = PBXProject; 169 | attributes = { 170 | LastUpgradeCheck = 0940; 171 | ORGANIZATIONNAME = "付正"; 172 | TargetAttributes = { 173 | 3A2FC3221F6F7056003CFD82 = { 174 | CreatedOnToolsVersion = 8.3.3; 175 | DevelopmentTeam = U99K52B2HT; 176 | ProvisioningStyle = Automatic; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 3A2FC31E1F6F7056003CFD82 /* Build configuration list for PBXProject "FzBluetoothDemo" */; 181 | compatibilityVersion = "Xcode 3.2"; 182 | developmentRegion = English; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | English, 186 | en, 187 | Base, 188 | ); 189 | mainGroup = 3A2FC31A1F6F7056003CFD82; 190 | productRefGroup = 3A2FC3241F6F7056003CFD82 /* Products */; 191 | projectDirPath = ""; 192 | projectRoot = ""; 193 | targets = ( 194 | 3A2FC3221F6F7056003CFD82 /* FzBluetoothDemo */, 195 | ); 196 | }; 197 | /* End PBXProject section */ 198 | 199 | /* Begin PBXResourcesBuildPhase section */ 200 | 3A2FC3211F6F7056003CFD82 /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 3A2FC3361F6F7056003CFD82 /* LaunchScreen.storyboard in Resources */, 205 | 3A2FC3331F6F7056003CFD82 /* Assets.xcassets in Resources */, 206 | 3A2FC3311F6F7056003CFD82 /* Main.storyboard in Resources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXResourcesBuildPhase section */ 211 | 212 | /* Begin PBXShellScriptBuildPhase section */ 213 | 2379D0AB376BCAE7520D2CAB /* [CP] Check Pods Manifest.lock */ = { 214 | isa = PBXShellScriptBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | ); 218 | inputPaths = ( 219 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 220 | "${PODS_ROOT}/Manifest.lock", 221 | ); 222 | name = "[CP] Check Pods Manifest.lock"; 223 | outputPaths = ( 224 | "$(DERIVED_FILE_DIR)/Pods-FzBluetoothDemo-checkManifestLockResult.txt", 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | shellPath = /bin/sh; 228 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 229 | showEnvVarsInLog = 0; 230 | }; 231 | C5B38E68CEC2B99D311B12D0 /* [CP] Embed Pods Frameworks */ = { 232 | isa = PBXShellScriptBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | ); 236 | inputPaths = ( 237 | "${SRCROOT}/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-frameworks.sh", 238 | "${BUILT_PRODUCTS_DIR}/FZBluetooth/FZBluetooth.framework", 239 | ); 240 | name = "[CP] Embed Pods Frameworks"; 241 | outputPaths = ( 242 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FZBluetooth.framework", 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | shellPath = /bin/sh; 246 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-frameworks.sh\"\n"; 247 | showEnvVarsInLog = 0; 248 | }; 249 | /* End PBXShellScriptBuildPhase section */ 250 | 251 | /* Begin PBXSourcesBuildPhase section */ 252 | 3A2FC31F1F6F7056003CFD82 /* Sources */ = { 253 | isa = PBXSourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 3A2FC32E1F6F7056003CFD82 /* ViewController.m in Sources */, 257 | 3A2FC3531F6F7B69003CFD82 /* FZBlueModel.m in Sources */, 258 | 3A2FC34D1F6F7B52003CFD82 /* FZHProgressHUD.m in Sources */, 259 | 3A2FC32B1F6F7056003CFD82 /* AppDelegate.m in Sources */, 260 | 3A2FC3541F6F7B69003CFD82 /* FZSingletonManager.m in Sources */, 261 | 3A2FC3281F6F7056003CFD82 /* main.m in Sources */, 262 | 3A2FC3491F6F745B003CFD82 /* SendViewController.m in Sources */, 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXSourcesBuildPhase section */ 267 | 268 | /* Begin PBXVariantGroup section */ 269 | 3A2FC32F1F6F7056003CFD82 /* Main.storyboard */ = { 270 | isa = PBXVariantGroup; 271 | children = ( 272 | 3A2FC3301F6F7056003CFD82 /* Base */, 273 | ); 274 | name = Main.storyboard; 275 | sourceTree = ""; 276 | }; 277 | 3A2FC3341F6F7056003CFD82 /* LaunchScreen.storyboard */ = { 278 | isa = PBXVariantGroup; 279 | children = ( 280 | 3A2FC3351F6F7056003CFD82 /* Base */, 281 | ); 282 | name = LaunchScreen.storyboard; 283 | sourceTree = ""; 284 | }; 285 | /* End PBXVariantGroup section */ 286 | 287 | /* Begin XCBuildConfiguration section */ 288 | 3A2FC3381F6F7056003CFD82 /* Debug */ = { 289 | isa = XCBuildConfiguration; 290 | buildSettings = { 291 | ALWAYS_SEARCH_USER_PATHS = NO; 292 | CLANG_ANALYZER_NONNULL = YES; 293 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 294 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 295 | CLANG_CXX_LIBRARY = "libc++"; 296 | CLANG_ENABLE_MODULES = YES; 297 | CLANG_ENABLE_OBJC_ARC = YES; 298 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 299 | CLANG_WARN_BOOL_CONVERSION = YES; 300 | CLANG_WARN_COMMA = YES; 301 | CLANG_WARN_CONSTANT_CONVERSION = YES; 302 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 304 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 305 | CLANG_WARN_EMPTY_BODY = YES; 306 | CLANG_WARN_ENUM_CONVERSION = YES; 307 | CLANG_WARN_INFINITE_RECURSION = YES; 308 | CLANG_WARN_INT_CONVERSION = YES; 309 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 311 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 313 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 314 | CLANG_WARN_STRICT_PROTOTYPES = YES; 315 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 319 | COPY_PHASE_STRIP = NO; 320 | DEBUG_INFORMATION_FORMAT = dwarf; 321 | ENABLE_STRICT_OBJC_MSGSEND = YES; 322 | ENABLE_TESTABILITY = YES; 323 | GCC_C_LANGUAGE_STANDARD = gnu99; 324 | GCC_DYNAMIC_NO_PIC = NO; 325 | GCC_NO_COMMON_BLOCKS = YES; 326 | GCC_OPTIMIZATION_LEVEL = 0; 327 | GCC_PREPROCESSOR_DEFINITIONS = ( 328 | "DEBUG=1", 329 | "$(inherited)", 330 | ); 331 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 332 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 333 | GCC_WARN_UNDECLARED_SELECTOR = YES; 334 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 335 | GCC_WARN_UNUSED_FUNCTION = YES; 336 | GCC_WARN_UNUSED_VARIABLE = YES; 337 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 338 | MTL_ENABLE_DEBUG_INFO = YES; 339 | ONLY_ACTIVE_ARCH = YES; 340 | SDKROOT = iphoneos; 341 | TARGETED_DEVICE_FAMILY = "1,2"; 342 | }; 343 | name = Debug; 344 | }; 345 | 3A2FC3391F6F7056003CFD82 /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | ALWAYS_SEARCH_USER_PATHS = NO; 349 | CLANG_ANALYZER_NONNULL = YES; 350 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 352 | CLANG_CXX_LIBRARY = "libc++"; 353 | CLANG_ENABLE_MODULES = YES; 354 | CLANG_ENABLE_OBJC_ARC = YES; 355 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 356 | CLANG_WARN_BOOL_CONVERSION = YES; 357 | CLANG_WARN_COMMA = YES; 358 | CLANG_WARN_CONSTANT_CONVERSION = YES; 359 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 360 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 361 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 362 | CLANG_WARN_EMPTY_BODY = YES; 363 | CLANG_WARN_ENUM_CONVERSION = YES; 364 | CLANG_WARN_INFINITE_RECURSION = YES; 365 | CLANG_WARN_INT_CONVERSION = YES; 366 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 367 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 368 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 369 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 370 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 371 | CLANG_WARN_STRICT_PROTOTYPES = YES; 372 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 373 | CLANG_WARN_UNREACHABLE_CODE = YES; 374 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 375 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 376 | COPY_PHASE_STRIP = NO; 377 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 378 | ENABLE_NS_ASSERTIONS = NO; 379 | ENABLE_STRICT_OBJC_MSGSEND = YES; 380 | GCC_C_LANGUAGE_STANDARD = gnu99; 381 | GCC_NO_COMMON_BLOCKS = YES; 382 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 383 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 384 | GCC_WARN_UNDECLARED_SELECTOR = YES; 385 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 386 | GCC_WARN_UNUSED_FUNCTION = YES; 387 | GCC_WARN_UNUSED_VARIABLE = YES; 388 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 389 | MTL_ENABLE_DEBUG_INFO = NO; 390 | SDKROOT = iphoneos; 391 | TARGETED_DEVICE_FAMILY = "1,2"; 392 | VALIDATE_PRODUCT = YES; 393 | }; 394 | name = Release; 395 | }; 396 | 3A2FC33B1F6F7056003CFD82 /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | baseConfigurationReference = 83016D86BB9BFF397D061E1B /* Pods-FzBluetoothDemo.debug.xcconfig */; 399 | buildSettings = { 400 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 401 | DEBUG_INFORMATION_FORMAT = dwarf; 402 | DEVELOPMENT_TEAM = U99K52B2HT; 403 | INFOPLIST_FILE = FzBluetoothDemo/Info.plist; 404 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 406 | PRODUCT_BUNDLE_IDENTIFIER = fuzheng.FzBluetoothDemo; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | }; 409 | name = Debug; 410 | }; 411 | 3A2FC33C1F6F7056003CFD82 /* Release */ = { 412 | isa = XCBuildConfiguration; 413 | baseConfigurationReference = 01ED38D5F09A97134BAB225D /* Pods-FzBluetoothDemo.release.xcconfig */; 414 | buildSettings = { 415 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 416 | DEVELOPMENT_TEAM = U99K52B2HT; 417 | INFOPLIST_FILE = FzBluetoothDemo/Info.plist; 418 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 419 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 420 | PRODUCT_BUNDLE_IDENTIFIER = fuzheng.FzBluetoothDemo; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | }; 423 | name = Release; 424 | }; 425 | /* End XCBuildConfiguration section */ 426 | 427 | /* Begin XCConfigurationList section */ 428 | 3A2FC31E1F6F7056003CFD82 /* Build configuration list for PBXProject "FzBluetoothDemo" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | 3A2FC3381F6F7056003CFD82 /* Debug */, 432 | 3A2FC3391F6F7056003CFD82 /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Release; 436 | }; 437 | 3A2FC33A1F6F7056003CFD82 /* Build configuration list for PBXNativeTarget "FzBluetoothDemo" */ = { 438 | isa = XCConfigurationList; 439 | buildConfigurations = ( 440 | 3A2FC33B1F6F7056003CFD82 /* Debug */, 441 | 3A2FC33C1F6F7056003CFD82 /* Release */, 442 | ); 443 | defaultConfigurationIsVisible = 0; 444 | defaultConfigurationName = Release; 445 | }; 446 | /* End XCConfigurationList section */ 447 | }; 448 | rootObject = 3A2FC31B1F6F7056003CFD82 /* Project object */; 449 | } 450 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcodeproj/project.xcworkspace/xcuserdata/fzh.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzheng0301/FZBluetooth/5ff4bc786a2815f82e2294e46a427f842667f50a/FZBluetoothExample/FzBluetoothDemo.xcodeproj/project.xcworkspace/xcuserdata/fzh.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcodeproj/xcuserdata/fzh.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcodeproj/xcuserdata/fzh.xcuserdatad/xcschemes/FzBluetoothDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcodeproj/xcuserdata/fzh.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | FzBluetoothDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3A2FC3221F6F7056003CFD82 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcworkspace/xcuserdata/fzh.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuzheng0301/FZBluetooth/5ff4bc786a2815f82e2294e46a427f842667f50a/FZBluetoothExample/FzBluetoothDemo.xcworkspace/xcuserdata/fzh.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo.xcworkspace/xcuserdata/fzh.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 22 | 23 | 24 | 26 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/18. 6 | // Copyright © 2017年 付正. 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 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/18. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | 22 | if([UINavigationBar conformsToProtocol:@protocol(UIAppearanceContainer)]) { 23 | [UINavigationBar appearance].tintColor = [UIColor whiteColor]; 24 | [[UINavigationBar appearance] setTitleTextAttributes:@{NSFontAttributeName : [UIFont boldSystemFontOfSize:18], NSForegroundColorAttributeName : [UIColor whiteColor]}]; 25 | [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:(51)/255.f green:(171)/255.f blue:(160)/255.f alpha:1.f]]; 26 | [[UINavigationBar appearance] setTranslucent:NO]; 27 | } 28 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 29 | ViewController *vc = [[ViewController alloc] init]; 30 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:vc]; 31 | self.window.rootViewController = navigationController; 32 | self.window.backgroundColor = [UIColor whiteColor]; 33 | [self.window makeKeyAndVisible]; 34 | 35 | return YES; 36 | } 37 | 38 | 39 | - (void)applicationWillResignActive:(UIApplication *)application { 40 | // 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. 41 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 42 | } 43 | 44 | 45 | - (void)applicationDidEnterBackground:(UIApplication *)application { 46 | // 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. 47 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 48 | } 49 | 50 | 51 | - (void)applicationWillEnterForeground:(UIApplication *)application { 52 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 53 | } 54 | 55 | 56 | - (void)applicationDidBecomeActive:(UIApplication *)application { 57 | // 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. 58 | } 59 | 60 | 61 | - (void)applicationWillTerminate:(UIApplication *)application { 62 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 63 | } 64 | 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/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 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/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 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/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 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/FZBlueModel/FZBlueModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // FZBlueModel.h 3 | // FZBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/6/19. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface FZBlueModel : NSObject 13 | 14 | @property (nonatomic, strong) NSString *blueName; //设备名称 15 | @property (nonatomic, strong) CBPeripheral *peripheral; 16 | @property (nonatomic, strong) CBCharacteristic * GPrint_Chatacter; 17 | @property (nonatomic, strong) NSString * UUIDString; //UUID 18 | @property (nonatomic, strong) NSString * distance; //中心到外设的距离 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/FZBlueModel/FZBlueModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // FZBlueModel.m 3 | // FZBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/6/19. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import "FZBlueModel.h" 10 | 11 | @implementation FZBlueModel 12 | 13 | -(void)setValue:(id)value forUndefinedKey:(NSString *)key 14 | { 15 | 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/FZBlueModel/FZSingletonManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // FZSingletonManager.h 3 | // FZeyBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/5/22. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface FZSingletonManager : NSObject 13 | 14 | @property (strong, nonatomic) CBPeripheral * GPrint_Peripheral;//当前连接的Peripheral 15 | @property (strong, nonatomic) CBCharacteristic * GPrint_Chatacter;//当前连接的Chatacter 16 | 17 | +(FZSingletonManager *)shareInstance; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/FZBlueModel/FZSingletonManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // FZSingletonManager.m 3 | // FZeyBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/5/22. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import "FZSingletonManager.h" 10 | 11 | @implementation FZSingletonManager 12 | 13 | +(FZSingletonManager *)shareInstance 14 | { 15 | static FZSingletonManager * singletonManager = nil; 16 | @synchronized(self){ 17 | if (!singletonManager) { 18 | singletonManager = [[FZSingletonManager alloc]init]; 19 | } 20 | } 21 | return singletonManager; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/FZHProgressHUD/FZHProgressHUD.h: -------------------------------------------------------------------------------- 1 | // 2 | // FZHProgressHUD.h 3 | // Version 0.4 4 | // Created by Matej Bukovinski on 2.4.09. 5 | // 6 | 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | 9 | // Copyright (c) 2011 Matej Bukovinski 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import 30 | 31 | @protocol FZHProgressHUDDelegate; 32 | 33 | ///////////////////////////////////////////////////////////////////////////////////////////// 34 | 35 | typedef enum { 36 | /** Progress is shown using an UIActivityIndicatorView. This is the default. */ 37 | FZHProgressHUDModeIndeterminate, 38 | /** Progress is shown using a FZHRoundProgressView. */ 39 | FZHProgressHUDModeDeterminate, 40 | /** Shows a custom view */ 41 | FZHProgressHUDModeCustomView 42 | } FZHProgressHUDMode; 43 | 44 | typedef enum { 45 | /** Opacity animation */ 46 | FZHProgressHUDAnimationFade, 47 | /** Opacity + scale animation */ 48 | FZHProgressHUDAnimationZoom 49 | } FZHProgressHUDAnimation; 50 | 51 | ///////////////////////////////////////////////////////////////////////////////////////////// 52 | 53 | /** 54 | * Displays a simple HUD window containing a progress indicator and two optional labels for short messages. 55 | * 56 | * This is a simple drop-in class for displaying a progress HUD view similar to Apples private UIProgressHUD class. 57 | * The FZHProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all 58 | * user input on this region, thereby preventing the user operations on components below the view. The HUD itself is 59 | * drawn centered as a rounded semi-transparent view witch resizes depending on the user specified content. 60 | * 61 | * This view supports three modes of operation: 62 | * - FZHProgressHUDModeIndeterminate - shows a UIActivityIndicatorView 63 | * - FZHProgressHUDModeDeterminate - shows a custom round progress indicator (FZHRoundProgressView) 64 | * - FZHProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView) 65 | * 66 | * All three modes can have optional labels assigned: 67 | * - If the labelText property is set and non-empty then a label containing the provided content is placed below the 68 | * indicator view. 69 | * - If also the detailsLabelText property is set then another label is placed below the first label. 70 | */ 71 | @interface FZHProgressHUD : UIView { 72 | 73 | FZHProgressHUDMode mode; 74 | FZHProgressHUDAnimation animationType; 75 | 76 | SEL methodForExecution; 77 | id targetForExecution; 78 | id objectForExecution; 79 | BOOL useAnimation; 80 | 81 | float yOffset; 82 | float xOffset; 83 | 84 | float width; 85 | float height; 86 | 87 | CGSize minSize; 88 | BOOL square; 89 | 90 | float margin; 91 | 92 | BOOL diFZHackground; 93 | 94 | BOOL taskInProgress; 95 | float graceTime; 96 | float minShowTime; 97 | NSTimer *graceTimer; 98 | NSTimer *minShowTimer; 99 | NSDate *showStarted; 100 | 101 | UIView *indicator; 102 | UILabel *label; 103 | UILabel *detailsLabel; 104 | 105 | float progress; 106 | 107 | #if __has_feature(objc_arc_weak) 108 | id __weak delegate; 109 | #elif __has_feature(objc_arc) 110 | id __unsafe_unretained delegate; 111 | #else 112 | id delegate; 113 | #endif 114 | NSString *labelText; 115 | NSString *detailsLabelText; 116 | float opacity; 117 | UIFont *labelFont; 118 | UIFont *detailsLabelFont; 119 | 120 | BOOL isFinished; 121 | BOOL removeFromSuperViewOnHide; 122 | 123 | UIView *customView; 124 | 125 | CGAffineTransform rotationTransform; 126 | } 127 | 128 | /** 129 | * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:. 130 | * 131 | * @param view The view that the HUD will be added to 132 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 133 | * animations while disappearing. 134 | * @return A reference to the created HUD. 135 | * 136 | * @see hideHUDForView:animated: 137 | */ 138 | + (FZHProgressHUD *)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; 139 | 140 | /** 141 | * Finds a HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:. 142 | * 143 | * @param view The view that is going to be searched for a HUD subview. 144 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 145 | * animations while disappearing. 146 | * @return YES if a HUD was found and removed, NO otherwise. 147 | * 148 | * @see hideHUDForView:animated: 149 | */ 150 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated; 151 | 152 | /** 153 | * A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with 154 | * window.bounds as the parameter. 155 | * 156 | * @param window The window instance that will provide the bounds for the HUD. Should probably be the same instance as 157 | * the HUD's superview (i.e., the window that the HUD will be added to). 158 | */ 159 | - (id)initWithWindow:(UIWindow *)window; 160 | 161 | /** 162 | * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with 163 | * view.bounds as the parameter 164 | * 165 | * @param view The view instance that will provide the bounds for the HUD. Should probably be the same instance as 166 | * the HUD's superview (i.e., the view that the HUD will be added to). 167 | */ 168 | - (id)initWithView:(UIView *)view; 169 | 170 | /** 171 | * The UIView (i.g., a UIIMageView) to be shown when the HUD is in FZHProgressHUDModeCustomView. 172 | * For best results use a 37 by 37 pixel view (so the bounds match the build in indicator bounds). 173 | */ 174 | #if __has_feature(objc_arc) 175 | @property (strong) UIView *customView; 176 | #else 177 | @property (retain) UIView *customView; 178 | #endif 179 | 180 | /** 181 | * FZHProgressHUD operation mode. Switches between indeterminate (FZHProgressHUDModeIndeterminate) and determinate 182 | * progress (FZHProgressHUDModeDeterminate). The default is FZHProgressHUDModeIndeterminate. 183 | * 184 | * @see FZHProgressHUDMode 185 | */ 186 | @property (assign) FZHProgressHUDMode mode; 187 | 188 | /** 189 | * The animation type that should be used when the HUD is shown and hidden. 190 | * 191 | * @see FZHProgressHUDAnimation 192 | */ 193 | @property (assign) FZHProgressHUDAnimation animationType; 194 | 195 | /** 196 | * The HUD delegate object. If set the delegate will receive hudWasHidden callbacks when the HUD was hidden. The 197 | * delegate should conform to the FZHProgressHUDDelegate protocol and implement the hudWasHidden method. The delegate 198 | * object will not be retained. 199 | */ 200 | #if __has_feature(objc_arc_weak) 201 | @property (weak) id delegate; 202 | #elif __has_feature(objc_arc) 203 | @property (unsafe_unretained) id delegate; 204 | #else 205 | @property (assign) id delegate; 206 | #endif 207 | 208 | /** 209 | * An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit 210 | * the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or 211 | * set to @"", then no message is displayed. 212 | */ 213 | @property (copy) NSString *labelText; 214 | 215 | /** 216 | * An optional details message displayed below the labelText message. This message is displayed only if the labelText 217 | * property is also set and is different from an empty string (@""). 218 | */ 219 | @property (copy) NSString *detailsLabelText; 220 | 221 | /** 222 | * The opacity of the HUD window. Defaults to 0.9 (90% opacity). 223 | */ 224 | @property (assign) float opacity; 225 | 226 | /** 227 | * The x-axis offset of the HUD relative to the centre of the superview. 228 | */ 229 | @property (assign) float xOffset; 230 | 231 | /** 232 | * The y-ayis offset of the HUD relative to the centre of the superview. 233 | */ 234 | @property (assign) float yOffset; 235 | 236 | /** 237 | * The amounth of space between the HUD edge and the HUD elements (labels, indicators or custom views). 238 | * 239 | * Defaults to 20.0 240 | */ 241 | @property (assign) float margin; 242 | 243 | /** 244 | * Cover the HUD background view with a radial gradient. 245 | */ 246 | @property (assign) BOOL diFZHackground; 247 | 248 | /* 249 | * Grace period is the time (in seconds) that the invoked method may be run without 250 | * showing the HUD. If the task finishes befor the grace time runs out, the HUD will 251 | * not be shown at all. 252 | * This may be used to prevent HUD display for very short tasks. 253 | * Defaults to 0 (no grace time). 254 | * Grace time functionality is only supported when the task status is known! 255 | * @see taskInProgress 256 | */ 257 | @property (assign) float graceTime; 258 | 259 | 260 | /** 261 | * The minimum time (in seconds) that the HUD is shown. 262 | * This avoids the problem of the HUD being shown and than instantly hidden. 263 | * Defaults to 0 (no minimum show time). 264 | */ 265 | @property (assign) float minShowTime; 266 | 267 | /** 268 | * Indicates that the executed operation is in progress. Needed for correct graceTime operation. 269 | * If you don't set a graceTime (different than 0.0) this does nothing. 270 | * This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:. 271 | * When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly), 272 | * you need to set this property when your task starts and completes in order to have normal graceTime 273 | * functunality. 274 | */ 275 | @property (assign) BOOL taskInProgress; 276 | 277 | /** 278 | * Removes the HUD from it's parent view when hidden. 279 | * Defaults to NO. 280 | */ 281 | @property (assign) BOOL removeFromSuperViewOnHide; 282 | 283 | /** 284 | * Font to be used for the main label. Set this property if the default is not adequate. 285 | */ 286 | #if __has_feature(objc_arc) 287 | @property (strong) UIFont* labelFont; 288 | #else 289 | @property (retain) UIFont* labelFont; 290 | #endif 291 | 292 | /** 293 | * Font to be used for the details label. Set this property if the default is not adequate. 294 | */ 295 | #if __has_feature(objc_arc) 296 | @property (strong) UIFont* detailsLabelFont; 297 | #else 298 | @property (retain) UIFont* detailsLabelFont; 299 | #endif 300 | 301 | /** 302 | * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0. 303 | */ 304 | @property (assign) float progress; 305 | 306 | /** 307 | * The minimum size of the HUD bezel. Defaults to CGSizeZero. 308 | */ 309 | @property (assign) CGSize minSize; 310 | 311 | /** 312 | * Force the HUD dimensions to be equal if possible. 313 | */ 314 | @property (assign, getter = isSquare) BOOL square; 315 | 316 | /** 317 | * Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so 318 | * the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread 319 | * (e.g., when using something like NSOperation or calling an asynchronous call like NSUrlRequest). 320 | * 321 | * If you need to perform a blocking thask on the main thread, you can try spining the run loop imeidiately after calling this 322 | * method by using: 323 | * 324 | * [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantPast]]; 325 | * 326 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 327 | * animations while disappearing. 328 | */ 329 | - (void)show:(BOOL)animated; 330 | 331 | /** 332 | * Hide the HUD. This still calls the hudWasHidden delegate. This is the counterpart of the hide: method. Use it to 333 | * hide the HUD when your task completes. 334 | * 335 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 336 | * animations while disappearing. 337 | */ 338 | - (void)hide:(BOOL)animated; 339 | 340 | /** 341 | * Hide the HUD after a delay. This still calls the hudWasHidden delegate. This is the counterpart of the hide: method. Use it to 342 | * hide the HUD when your task completes. 343 | * 344 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 345 | * animations while disappearing. 346 | * @param delay Delay in secons until the HUD is hidden. 347 | */ 348 | - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay; 349 | 350 | /** 351 | * Shows the HUD while a background task is executing in a new thread, then hides the HUD. 352 | * 353 | * This method also takes care of NSAutoreleasePools so your method does not have to be concerned with setting up a 354 | * pool. 355 | * 356 | * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread. 357 | * @param target The object that the target method belongs to. 358 | * @param object An optional object to be passed to the method. 359 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 360 | * animations while disappearing. 361 | */ 362 | - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated; 363 | 364 | @end 365 | 366 | ///////////////////////////////////////////////////////////////////////////////////////////// 367 | 368 | @protocol FZHProgressHUDDelegate 369 | 370 | @optional 371 | 372 | /** 373 | * Called after the HUD was fully hidden from the screen. 374 | */ 375 | - (void)hudWasHidden:(FZHProgressHUD *)hud; 376 | 377 | /** 378 | * @deprecated use hudWasHidden: instead 379 | * @see hudWasHidden: 380 | */ 381 | - (void)hudWasHidden __attribute__ ((deprecated)); 382 | 383 | @end 384 | 385 | ///////////////////////////////////////////////////////////////////////////////////////////// 386 | 387 | /** 388 | * A progress view for showing definite progress by filling up a circle (pie chart). 389 | */ 390 | @interface FZHRoundProgressView : UIView { 391 | @private 392 | float _progress; 393 | } 394 | 395 | /** 396 | * Progress (0.0 to 1.0) 397 | */ 398 | @property (nonatomic, assign) float progress; 399 | 400 | @end 401 | 402 | ///////////////////////////////////////////////////////////////////////////////////////////// 403 | 404 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/FZHProgressHUD/FZHProgressHUD.m: -------------------------------------------------------------------------------- 1 | // 2 | // FZHProgressHUD.m 3 | // Version 0.4 4 | // Created by Matej Bukovinski on 2.4.09. 5 | // 6 | 7 | #import "FZHProgressHUD.h" 8 | 9 | @interface FZHProgressHUD () 10 | 11 | - (void)hideUsingAnimation:(BOOL)animated; 12 | - (void)showUsingAnimation:(BOOL)animated; 13 | - (void)done; 14 | - (void)updateLabelText:(NSString *)newText; 15 | - (void)updateDetailsLabelText:(NSString *)newText; 16 | - (void)updateProgress; 17 | - (void)updateIndicators; 18 | - (void)handleGraceTimer:(NSTimer *)theTimer; 19 | - (void)handleMinShowTimer:(NSTimer *)theTimer; 20 | - (void)setTransformForCurrentOrientation:(BOOL)animated; 21 | - (void)cleanUp; 22 | - (void)launchExecution; 23 | - (void)deviceOrientationDidChange:(NSNotification *)notification; 24 | - (void)hideDelayed:(NSNumber *)animated; 25 | 26 | #if __has_feature(objc_arc) 27 | @property (strong) UIView *indicator; 28 | @property (strong) NSTimer *graceTimer; 29 | @property (strong) NSTimer *minShowTimer; 30 | @property (strong) NSDate *showStarted; 31 | #else 32 | @property (retain) UIView *indicator; 33 | @property (retain) NSTimer *graceTimer; 34 | @property (retain) NSTimer *minShowTimer; 35 | @property (retain) NSDate *showStarted; 36 | #endif 37 | 38 | @property (assign) float width; 39 | @property (assign) float height; 40 | 41 | @end 42 | 43 | 44 | @implementation FZHProgressHUD 45 | 46 | #pragma mark - 47 | #pragma mark Accessors 48 | 49 | @synthesize animationType; 50 | 51 | @synthesize delegate; 52 | @synthesize opacity; 53 | @synthesize labelFont; 54 | @synthesize detailsLabelFont; 55 | 56 | @synthesize indicator; 57 | 58 | @synthesize width; 59 | @synthesize height; 60 | @synthesize xOffset; 61 | @synthesize yOffset; 62 | @synthesize minSize; 63 | @synthesize square; 64 | @synthesize margin; 65 | @synthesize diFZHackground; 66 | 67 | @synthesize graceTime; 68 | @synthesize minShowTime; 69 | @synthesize graceTimer; 70 | @synthesize minShowTimer; 71 | @synthesize taskInProgress; 72 | @synthesize removeFromSuperViewOnHide; 73 | 74 | @synthesize customView; 75 | 76 | @synthesize showStarted; 77 | 78 | - (void)setMode:(FZHProgressHUDMode)newMode { 79 | // Dont change mode if it wasn't actually changed to prevent flickering 80 | if (mode && (mode == newMode)) { 81 | return; 82 | } 83 | 84 | mode = newMode; 85 | 86 | if ([NSThread isMainThread]) { 87 | [self updateIndicators]; 88 | [self setNeedsLayout]; 89 | [self setNeedsDisplay]; 90 | } else { 91 | [self performSelectorOnMainThread:@selector(updateIndicators) withObject:nil waitUntilDone:NO]; 92 | [self performSelectorOnMainThread:@selector(setNeedsLayout) withObject:nil waitUntilDone:NO]; 93 | [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO]; 94 | } 95 | } 96 | 97 | - (FZHProgressHUDMode)mode { 98 | return mode; 99 | } 100 | 101 | - (void)setLabelText:(NSString *)newText { 102 | if ([NSThread isMainThread]) { 103 | [self updateLabelText:newText]; 104 | [self setNeedsLayout]; 105 | [self setNeedsDisplay]; 106 | } else { 107 | [self performSelectorOnMainThread:@selector(updateLabelText:) withObject:newText waitUntilDone:NO]; 108 | [self performSelectorOnMainThread:@selector(setNeedsLayout) withObject:nil waitUntilDone:NO]; 109 | [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO]; 110 | } 111 | } 112 | 113 | - (NSString *)labelText { 114 | return labelText; 115 | } 116 | 117 | - (void)setDetailsLabelText:(NSString *)newText { 118 | if ([NSThread isMainThread]) { 119 | [self updateDetailsLabelText:newText]; 120 | [self setNeedsLayout]; 121 | [self setNeedsDisplay]; 122 | } else { 123 | [self performSelectorOnMainThread:@selector(updateDetailsLabelText:) withObject:newText waitUntilDone:NO]; 124 | [self performSelectorOnMainThread:@selector(setNeedsLayout) withObject:nil waitUntilDone:NO]; 125 | [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO]; 126 | } 127 | } 128 | 129 | - (NSString *)detailsLabelText { 130 | return detailsLabelText; 131 | } 132 | 133 | - (void)setProgress:(float)newProgress { 134 | progress = newProgress; 135 | 136 | // Update display ony if showing the determinate progress view 137 | if (mode == FZHProgressHUDModeDeterminate) { 138 | if ([NSThread isMainThread]) { 139 | [self updateProgress]; 140 | [self setNeedsDisplay]; 141 | } else { 142 | [self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO]; 143 | [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO]; 144 | } 145 | } 146 | } 147 | 148 | - (float)progress { 149 | return progress; 150 | } 151 | 152 | #pragma mark - 153 | #pragma mark Accessor helpers 154 | 155 | - (void)updateLabelText:(NSString *)newText { 156 | if (labelText != newText) { 157 | #if !__has_feature(objc_arc) 158 | [labelText release]; 159 | #endif 160 | labelText = [newText copy]; 161 | } 162 | } 163 | 164 | - (void)updateDetailsLabelText:(NSString *)newText { 165 | if (detailsLabelText != newText) { 166 | #if !__has_feature(objc_arc) 167 | [detailsLabelText release]; 168 | #endif 169 | detailsLabelText = [newText copy]; 170 | } 171 | } 172 | 173 | - (void)updateProgress { 174 | [(FZHRoundProgressView *)indicator setProgress:progress]; 175 | } 176 | 177 | - (void)updateIndicators { 178 | if (indicator) { 179 | [indicator removeFromSuperview]; 180 | } 181 | 182 | if (mode == FZHProgressHUDModeDeterminate) { 183 | #if __has_feature(objc_arc) 184 | self.indicator = [[FZHRoundProgressView alloc] init]; 185 | #else 186 | self.indicator = [[[FZHRoundProgressView alloc] init] autorelease]; 187 | #endif 188 | } 189 | else if (mode == FZHProgressHUDModeCustomView && self.customView != nil){ 190 | self.indicator = self.customView; 191 | } else { 192 | #if __has_feature(objc_arc) 193 | self.indicator = [[UIActivityIndicatorView alloc] 194 | initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 195 | #else 196 | self.indicator = [[[UIActivityIndicatorView alloc] 197 | initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease]; 198 | #endif 199 | [(UIActivityIndicatorView *)indicator startAnimating]; 200 | } 201 | 202 | 203 | [self addSubview:indicator]; 204 | } 205 | 206 | #pragma mark - 207 | #pragma mark Constants 208 | 209 | #define PADDING 4.0f 210 | 211 | #define LABELFONTSIZE 16.0f 212 | #define LABELDETAILSFONTSIZE 12.0f 213 | 214 | #pragma mark - 215 | #pragma mark Class methods 216 | 217 | + (FZHProgressHUD *)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { 218 | FZHProgressHUD *hud = [[FZHProgressHUD alloc] initWithView:view]; 219 | [view addSubview:hud]; 220 | [hud show:animated]; 221 | #if __has_feature(objc_arc) 222 | return hud; 223 | #else 224 | return [hud autorelease]; 225 | #endif 226 | } 227 | 228 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated { 229 | UIView *viewToRemove = nil; 230 | for (UIView *v in [view subviews]) { 231 | if ([v isKindOfClass:[FZHProgressHUD class]]) { 232 | viewToRemove = v; 233 | } 234 | } 235 | if (viewToRemove != nil) { 236 | FZHProgressHUD *HUD = (FZHProgressHUD *)viewToRemove; 237 | HUD.removeFromSuperViewOnHide = YES; 238 | [HUD hide:animated]; 239 | return YES; 240 | } else { 241 | return NO; 242 | } 243 | } 244 | 245 | 246 | #pragma mark - 247 | #pragma mark Lifecycle methods 248 | 249 | - (id)initWithWindow:(UIWindow *)window { 250 | return [self initWithView:window]; 251 | } 252 | 253 | - (id)initWithView:(UIView *)view { 254 | // Let's check if the view is nil (this is a common error when using the windw initializer above) 255 | if (!view) { 256 | [NSException raise:@"FZHProgressHUDViewIsNillException" 257 | format:@"The view used in the FZHProgressHUD initializer is nil."]; 258 | } 259 | id me = [self initWithFrame:view.bounds]; 260 | // We need to take care of rotation ourselfs if we're adding the HUD to a window 261 | if ([view isKindOfClass:[UIWindow class]]) { 262 | [self setTransformForCurrentOrientation:NO]; 263 | } 264 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) 265 | name:UIDeviceOrientationDidChangeNotification object:nil]; 266 | 267 | return me; 268 | } 269 | 270 | - (void)removeFromSuperview { 271 | [[NSNotificationCenter defaultCenter] removeObserver:self 272 | name:UIDeviceOrientationDidChangeNotification 273 | object:nil]; 274 | 275 | [super removeFromSuperview]; 276 | } 277 | 278 | 279 | - (id)initWithFrame:(CGRect)frame { 280 | self = [super initWithFrame:frame]; 281 | if (self) { 282 | // Set default values for properties 283 | self.animationType = FZHProgressHUDAnimationFade; 284 | self.mode = FZHProgressHUDModeIndeterminate; 285 | self.labelText = nil; 286 | self.detailsLabelText = nil; 287 | self.opacity = 0.8f; 288 | self.labelFont = [UIFont boldSystemFontOfSize:LABELFONTSIZE]; 289 | self.detailsLabelFont = [UIFont boldSystemFontOfSize:LABELDETAILSFONTSIZE]; 290 | self.xOffset = 0.0f; 291 | self.yOffset = 0.0f; 292 | self.diFZHackground = NO; 293 | self.margin = 20.0f; 294 | self.graceTime = 0.0f; 295 | self.minShowTime = 0.0f; 296 | self.removeFromSuperViewOnHide = NO; 297 | self.minSize = CGSizeZero; 298 | self.square = NO; 299 | 300 | self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 301 | 302 | // Transparent background 303 | self.opaque = NO; 304 | self.backgroundColor = [UIColor clearColor]; 305 | 306 | // Make invisible for now 307 | self.alpha = 0.0f; 308 | 309 | // Add label 310 | label = [[UILabel alloc] initWithFrame:self.bounds]; 311 | 312 | // Add details label 313 | detailsLabel = [[UILabel alloc] initWithFrame:self.bounds]; 314 | 315 | taskInProgress = NO; 316 | rotationTransform = CGAffineTransformIdentity; 317 | } 318 | return self; 319 | } 320 | 321 | #if !__has_feature(objc_arc) 322 | - (void)dealloc { 323 | [indicator release]; 324 | [label release]; 325 | [detailsLabel release]; 326 | [labelText release]; 327 | [detailsLabelText release]; 328 | [graceTimer release]; 329 | [minShowTimer release]; 330 | [showStarted release]; 331 | [customView release]; 332 | [super dealloc]; 333 | } 334 | #endif 335 | 336 | #pragma mark - 337 | #pragma mark Layout 338 | 339 | - (void)layoutSubviews { 340 | CGRect frame = self.bounds; 341 | 342 | // Compute HUD dimensions based on indicator size (add margin to HUD border) 343 | CGRect indFrame = indicator.bounds; 344 | self.width = indFrame.size.width + 2 * margin; 345 | self.height = indFrame.size.height + 2 * margin; 346 | 347 | // Position the indicator 348 | indFrame.origin.x = floorf((frame.size.width - indFrame.size.width) / 2) + self.xOffset; 349 | indFrame.origin.y = floorf((frame.size.height - indFrame.size.height) / 2) + self.yOffset; 350 | indicator.frame = indFrame; 351 | 352 | // Add label if label text was set 353 | if (nil != self.labelText) { 354 | // Get size of label text 355 | CGSize dims = [self.labelText sizeWithFont:self.labelFont]; 356 | 357 | // Compute label dimensions based on font metrics if size is larger than max then clip the label width 358 | float lHeight = dims.height; 359 | float lWidth; 360 | if (dims.width <= (frame.size.width - 4 * margin)) { 361 | lWidth = dims.width; 362 | } 363 | else { 364 | lWidth = frame.size.width - 4 * margin; 365 | } 366 | 367 | // Set label properties 368 | label.font = self.labelFont; 369 | label.adjustsFontSizeToFitWidth = NO; 370 | label.textAlignment = UITextAlignmentCenter; 371 | label.opaque = NO; 372 | label.backgroundColor = [UIColor clearColor]; 373 | label.textColor = [UIColor whiteColor]; 374 | label.text = self.labelText; 375 | 376 | // Update HUD size 377 | if (self.width < (lWidth + 2 * margin)) { 378 | self.width = lWidth + 2 * margin; 379 | } 380 | self.height = self.height + lHeight + PADDING; 381 | 382 | // Move indicator to make room for the label 383 | indFrame.origin.y -= (floorf(lHeight / 2 + PADDING / 2)); 384 | indicator.frame = indFrame; 385 | 386 | // Set the label position and dimensions 387 | CGRect lFrame = CGRectMake(floorf((frame.size.width - lWidth) / 2) + xOffset, 388 | floorf(indFrame.origin.y + indFrame.size.height + PADDING), 389 | lWidth, lHeight); 390 | label.frame = lFrame; 391 | 392 | [self addSubview:label]; 393 | 394 | // Add details label delatils text was set 395 | if (nil != self.detailsLabelText) { 396 | 397 | // Set label properties 398 | detailsLabel.font = self.detailsLabelFont; 399 | detailsLabel.adjustsFontSizeToFitWidth = NO; 400 | detailsLabel.textAlignment = UITextAlignmentCenter; 401 | detailsLabel.opaque = NO; 402 | detailsLabel.backgroundColor = [UIColor clearColor]; 403 | detailsLabel.textColor = [UIColor whiteColor]; 404 | detailsLabel.text = self.detailsLabelText; 405 | detailsLabel.numberOfLines = 0; 406 | 407 | CGFloat maxHeight = frame.size.height - self.height - 2*margin; 408 | CGSize labelSize = [detailsLabel.text sizeWithFont:detailsLabel.font constrainedToSize:CGSizeMake(frame.size.width - 4*margin, maxHeight) lineBreakMode:detailsLabel.lineBreakMode]; 409 | lHeight = labelSize.height; 410 | lWidth = labelSize.width; 411 | 412 | // Update HUD size 413 | if (self.width < lWidth) { 414 | self.width = lWidth + 2 * margin; 415 | } 416 | self.height = self.height + lHeight + PADDING; 417 | 418 | // Move indicator to make room for the new label 419 | indFrame.origin.y -= (floorf(lHeight / 2 + PADDING / 2)); 420 | indicator.frame = indFrame; 421 | 422 | // Move first label to make room for the new label 423 | lFrame.origin.y -= (floorf(lHeight / 2 + PADDING / 2)); 424 | label.frame = lFrame; 425 | 426 | // Set label position and dimensions 427 | CGRect lFrameD = CGRectMake(floorf((frame.size.width - lWidth) / 2) + xOffset, 428 | lFrame.origin.y + lFrame.size.height + PADDING, lWidth, lHeight); 429 | detailsLabel.frame = lFrameD; 430 | 431 | [self addSubview:detailsLabel]; 432 | } 433 | } 434 | 435 | if (square) { 436 | CGFloat max = MAX(self.width, self.height); 437 | if (max <= frame.size.width - 2*margin) { 438 | self.width = max; 439 | } 440 | if (max <= frame.size.height - 2*margin) { 441 | self.height = max; 442 | } 443 | } 444 | 445 | if (self.width < minSize.width) { 446 | self.width = minSize.width; 447 | } 448 | if (self.height < minSize.height) { 449 | self.height = minSize.height; 450 | } 451 | } 452 | 453 | #pragma mark - 454 | #pragma mark Showing and execution 455 | 456 | - (void)show:(BOOL)animated { 457 | useAnimation = animated; 458 | 459 | // If the grace time is set postpone the HUD display 460 | if (self.graceTime > 0.0) { 461 | self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime 462 | target:self 463 | selector:@selector(handleGraceTimer:) 464 | userInfo:nil 465 | repeats:NO]; 466 | } 467 | // ... otherwise show the HUD imediately 468 | else { 469 | [self setNeedsDisplay]; 470 | [self showUsingAnimation:useAnimation]; 471 | } 472 | } 473 | 474 | - (void)hide:(BOOL)animated { 475 | useAnimation = animated; 476 | 477 | // If the minShow time is set, calculate how long the hud was shown, 478 | // and pospone the hiding operation if necessary 479 | if (self.minShowTime > 0.0 && showStarted) { 480 | NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted]; 481 | if (interv < self.minShowTime) { 482 | self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) 483 | target:self 484 | selector:@selector(handleMinShowTimer:) 485 | userInfo:nil 486 | repeats:NO]; 487 | return; 488 | } 489 | } 490 | 491 | // ... otherwise hide the HUD immediately 492 | [self hideUsingAnimation:useAnimation]; 493 | } 494 | 495 | - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay { 496 | [self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay]; 497 | } 498 | 499 | - (void)hideDelayed:(NSNumber *)animated { 500 | [self hide:[animated boolValue]]; 501 | } 502 | 503 | - (void)handleGraceTimer:(NSTimer *)theTimer { 504 | // Show the HUD only if the task is still running 505 | if (taskInProgress) { 506 | [self setNeedsDisplay]; 507 | [self showUsingAnimation:useAnimation]; 508 | } 509 | } 510 | 511 | - (void)handleMinShowTimer:(NSTimer *)theTimer { 512 | [self hideUsingAnimation:useAnimation]; 513 | } 514 | 515 | - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated { 516 | 517 | methodForExecution = method; 518 | #if __has_feature(objc_arc) 519 | targetForExecution = target; 520 | objectForExecution = object; 521 | #else 522 | targetForExecution = [target retain]; 523 | objectForExecution = [object retain]; 524 | #endif 525 | 526 | // Launch execution in new thread 527 | taskInProgress = YES; 528 | [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil]; 529 | 530 | // Show HUD view 531 | [self show:animated]; 532 | } 533 | 534 | - (void)launchExecution { 535 | #if !__has_feature(objc_arc) 536 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 537 | #endif 538 | #pragma clang diagnostic push 539 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 540 | // Start executing the requested task 541 | [targetForExecution performSelector:methodForExecution withObject:objectForExecution]; 542 | #pragma clang diagnostic pop 543 | // Task completed, update view in main thread (note: view operations should 544 | // be done only in the main thread) 545 | [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO]; 546 | 547 | #if !__has_feature(objc_arc) 548 | [pool release]; 549 | #endif 550 | } 551 | 552 | - (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context { 553 | [self done]; 554 | } 555 | 556 | - (void)done { 557 | isFinished = YES; 558 | 559 | // If delegate was set make the callback 560 | self.alpha = 0.0f; 561 | 562 | if(delegate != nil) { 563 | if ([delegate respondsToSelector:@selector(hudWasHidden:)]) { 564 | [delegate performSelector:@selector(hudWasHidden:) withObject:self]; 565 | } else if ([delegate respondsToSelector:@selector(hudWasHidden)]) { 566 | [delegate performSelector:@selector(hudWasHidden)]; 567 | } 568 | } 569 | 570 | if (removeFromSuperViewOnHide) { 571 | [self removeFromSuperview]; 572 | } 573 | } 574 | 575 | - (void)cleanUp { 576 | taskInProgress = NO; 577 | 578 | self.indicator = nil; 579 | 580 | #if !__has_feature(objc_arc) 581 | [targetForExecution release]; 582 | [objectForExecution release]; 583 | #endif 584 | 585 | [self hide:useAnimation]; 586 | } 587 | 588 | #pragma mark - 589 | #pragma mark Fade in and Fade out 590 | 591 | - (void)showUsingAnimation:(BOOL)animated { 592 | self.alpha = 0.0f; 593 | if (animated && animationType == FZHProgressHUDAnimationZoom) { 594 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f)); 595 | } 596 | 597 | self.showStarted = [NSDate date]; 598 | // Fade in 599 | if (animated) { 600 | [UIView beginAnimations:nil context:NULL]; 601 | [UIView setAnimationDuration:0.30]; 602 | self.alpha = 1.0f; 603 | if (animationType == FZHProgressHUDAnimationZoom) { 604 | self.transform = rotationTransform; 605 | } 606 | [UIView commitAnimations]; 607 | } 608 | else { 609 | self.alpha = 1.0f; 610 | } 611 | } 612 | 613 | - (void)hideUsingAnimation:(BOOL)animated { 614 | // Fade out 615 | if (animated) { 616 | [UIView beginAnimations:nil context:NULL]; 617 | [UIView setAnimationDuration:0.30]; 618 | [UIView setAnimationDelegate:self]; 619 | [UIView setAnimationDidStopSelector:@selector(animationFinished: finished: context:)]; 620 | // 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden 621 | // in the done method 622 | if (animationType == FZHProgressHUDAnimationZoom) { 623 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f)); 624 | } 625 | self.alpha = 0.02f; 626 | [UIView commitAnimations]; 627 | } 628 | else { 629 | self.alpha = 0.0f; 630 | [self done]; 631 | } 632 | } 633 | 634 | #pragma mark BG Drawing 635 | 636 | - (void)drawRect:(CGRect)rect { 637 | 638 | CGContextRef context = UIGraphicsGetCurrentContext(); 639 | 640 | if (diFZHackground) { 641 | //Gradient colours 642 | size_t gradLocationsNum = 2; 643 | CGFloat gradLocations[2] = {0.0f, 1.0f}; 644 | CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f}; 645 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 646 | CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum); 647 | CGColorSpaceRelease(colorSpace); 648 | 649 | //Gradient center 650 | CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); 651 | //Gradient radius 652 | float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ; 653 | //Gradient draw 654 | CGContextDrawRadialGradient (context, gradient, gradCenter, 655 | 0, gradCenter, gradRadius, 656 | kCGGradientDrawsAfterEndLocation); 657 | CGGradientRelease(gradient); 658 | } 659 | 660 | // Center HUD 661 | CGRect allRect = self.bounds; 662 | // Draw rounded HUD bacgroud rect 663 | CGRect boxRect = CGRectMake(roundf((allRect.size.width - self.width) / 2) + self.xOffset, 664 | roundf((allRect.size.height - self.height) / 2) + self.yOffset, self.width, self.height); 665 | // Corner radius 666 | float radius = 10.0f; 667 | 668 | CGContextBeginPath(context); 669 | CGContextSetGrayFillColor(context, 0.0f, self.opacity); 670 | CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect)); 671 | CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0); 672 | CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0); 673 | CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0); 674 | CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0); 675 | CGContextClosePath(context); 676 | CGContextFillPath(context); 677 | } 678 | 679 | #pragma mark - 680 | #pragma mark Manual oritentation change 681 | 682 | #define RADIANS(degrees) ((degrees * (float)M_PI) / 180.0f) 683 | 684 | - (void)deviceOrientationDidChange:(NSNotification *)notification { 685 | if (!self.superview) { 686 | return; 687 | } 688 | 689 | if ([self.superview isKindOfClass:[UIWindow class]]) { 690 | [self setTransformForCurrentOrientation:YES]; 691 | } else { 692 | self.bounds = self.superview.bounds; 693 | [self setNeedsDisplay]; 694 | } 695 | } 696 | 697 | - (void)setTransformForCurrentOrientation:(BOOL)animated { 698 | UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; 699 | NSInteger degrees = 0; 700 | 701 | // Stay in sync with the superview 702 | if (self.superview) { 703 | self.bounds = self.superview.bounds; 704 | [self setNeedsDisplay]; 705 | } 706 | 707 | if (UIInterfaceOrientationIsLandscape(orientation)) { 708 | if (orientation == UIInterfaceOrientationLandscapeLeft) { degrees = -90; } 709 | else { degrees = 90; } 710 | // Window coordinates differ! 711 | self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width); 712 | } else { 713 | if (orientation == UIInterfaceOrientationPortraitUpsideDown) { degrees = 180; } 714 | else { degrees = 0; } 715 | } 716 | 717 | rotationTransform = CGAffineTransformMakeRotation(RADIANS(degrees)); 718 | 719 | if (animated) { 720 | [UIView beginAnimations:nil context:nil]; 721 | } 722 | [self setTransform:rotationTransform]; 723 | if (animated) { 724 | [UIView commitAnimations]; 725 | } 726 | } 727 | 728 | @end 729 | 730 | ///////////////////////////////////////////////////////////////////////////////////////////// 731 | 732 | @implementation FZHRoundProgressView 733 | 734 | #pragma mark - 735 | #pragma mark Accessors 736 | 737 | - (float)progress { 738 | return _progress; 739 | } 740 | 741 | - (void)setProgress:(float)progress { 742 | _progress = progress; 743 | [self setNeedsDisplay]; 744 | } 745 | 746 | #pragma mark - 747 | #pragma mark Lifecycle 748 | 749 | - (id)init { 750 | return [self initWithFrame:CGRectMake(0.0f, 0.0f, 37.0f, 37.0f)]; 751 | } 752 | 753 | - (id)initWithFrame:(CGRect)frame { 754 | self = [super initWithFrame:frame]; 755 | if (self) { 756 | self.backgroundColor = [UIColor clearColor]; 757 | self.opaque = NO; 758 | } 759 | return self; 760 | } 761 | 762 | #pragma mark - 763 | #pragma mark Drawing 764 | 765 | - (void)drawRect:(CGRect)rect { 766 | 767 | CGRect allRect = self.bounds; 768 | CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f); 769 | 770 | CGContextRef context = UIGraphicsGetCurrentContext(); 771 | 772 | // Draw background 773 | CGContextSetRGBStrokeColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white 774 | CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 0.1f); // translucent white 775 | CGContextSetLineWidth(context, 2.0f); 776 | CGContextFillEllipseInRect(context, circleRect); 777 | CGContextStrokeEllipseInRect(context, circleRect); 778 | 779 | // Draw progress 780 | CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2); 781 | CGFloat radius = (allRect.size.width - 4) / 2; 782 | CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees 783 | CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle; 784 | CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white 785 | CGContextMoveToPoint(context, center.x, center.y); 786 | CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0); 787 | CGContextClosePath(context); 788 | CGContextFillPath(context); 789 | } 790 | 791 | @end 792 | 793 | ///////////////////////////////////////////////////////////////////////////////////////////// 794 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | NSBluetoothAlwaysUsageDescription 26 | 访问蓝牙 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/SendViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SendViewController.h 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/18. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define ScreenWidth [UIScreen mainScreen].bounds.size.width 12 | #define ScreenHeight [UIScreen mainScreen].bounds.size.height 13 | 14 | @interface SendViewController : UIViewController 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/SendViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SendViewController.m 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/18. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import "SendViewController.h" 10 | #import "FzhBluetooth.h" 11 | #import "FZBlueModel.h" 12 | #import "FZSingletonManager.h" 13 | 14 | @interface SendViewController () 15 | { 16 | UILabel *messageLabel; //展示 17 | UITextField *textF; 18 | } 19 | @end 20 | 21 | @implementation SendViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | // Do any additional setup after loading the view. 26 | 27 | self.title = @"发送接收数据"; 28 | 29 | self.view.backgroundColor = [UIColor whiteColor]; 30 | 31 | [self createUI]; 32 | } 33 | 34 | -(void)viewDidDisappear:(BOOL)animated 35 | { 36 | //断开连接 37 | [[FzhBluetooth shareInstance]cancelPeripheralConnection]; 38 | } 39 | 40 | -(void)createUI 41 | { 42 | textF = [[UITextField alloc]initWithFrame:CGRectMake(ScreenWidth/2-110, 80, 220, 40)]; 43 | textF.placeholder = @"输入发送给设备的指令"; 44 | textF.text = @"5ac2000000c2a5"; 45 | textF.layer.borderWidth = 1.0; 46 | [self.view addSubview:textF]; 47 | 48 | [self buttonWithTitle:@"发 送" frame:CGRectMake(ScreenWidth/2-50, 140, 100, 40) action:@selector(didClickSend) AddView:self.view]; 49 | 50 | messageLabel = [[UILabel alloc]initWithFrame:CGRectMake(30, 200, ScreenWidth-60, 200)]; 51 | messageLabel.numberOfLines = 0; 52 | messageLabel.layer.borderWidth = 0.5; 53 | [self.view addSubview:messageLabel]; 54 | } 55 | 56 | -(void)didClickSend 57 | { 58 | if (textF.text.length <= 0) { 59 | UIAlertView *alt = [[UIAlertView alloc]initWithTitle:@"提示" message:@"请输入内容" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; 60 | [alt show]; 61 | return; 62 | } 63 | [self.view endEditing:YES]; 64 | 65 | if (!messageLabel.text) { 66 | messageLabel.text = @""; 67 | } 68 | messageLabel.text = [NSString stringWithFormat:@"%@\nsend:%@",messageLabel.text,textF.text]; 69 | NSDictionary *dic = [[FzhBluetooth shareInstance]writeValue:textF.text forCharacteristic:[FZSingletonManager shareInstance].GPrint_Chatacter]; 70 | NSError *error = [dic objectForKey:@"error"]; 71 | if (error == nil) { 72 | NSString *returnStr = [dic objectForKey:@"returnStr"]; 73 | NSLog(@"returnStr = %@",returnStr); 74 | messageLabel.text = [NSString stringWithFormat:@"%@\nrep:%@\n",messageLabel.text,returnStr]; 75 | } else { 76 | messageLabel.text = [NSString stringWithFormat:@"%@\nrep:%@\n",messageLabel.text,[error localizedDescription]]; 77 | } 78 | 79 | // [[FzhBluetooth shareInstance]writeValue:textF.text forCharacteristic:[FZSingletonManager shareInstance].GPrint_Chatacter completionBlock:^(CBCharacteristic *characteristic, NSError *error) { 80 | // NSLog(@"发送成功"); 81 | // if (!messageLabel.text) { 82 | // messageLabel.text = @""; 83 | // } 84 | // messageLabel.text = [NSString stringWithFormat:@"%@\nsend:%@",messageLabel.text,textF.text]; 85 | // } returnBlock:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSString *returnStr, NSError *error) { 86 | // NSLog(@"应答数据,%@",returnStr); 87 | // messageLabel.text = [NSString stringWithFormat:@"%@\nrep:%@",messageLabel.text,returnStr]; 88 | // }]; 89 | } 90 | 91 | #pragma mark --- 点击屏幕空白处收起键盘 92 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 93 | { 94 | [self.view endEditing:YES]; 95 | } 96 | 97 | #pragma mark --- 创建button公共方法 98 | /**使用示例:[self buttonWithTitle:@"点 击" frame:CGRectMake((self.view.frame.size.width - 150)/2, (self.view.frame.size.height - 40)/3, 150, 40) action:@selector(didClickButton) AddView:self.view];*/ 99 | -(UIButton *)buttonWithTitle:(NSString *)title frame:(CGRect)frame action:(SEL)action AddView:(id)view 100 | { 101 | UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; 102 | button.frame = frame; 103 | button.backgroundColor = [UIColor lightGrayColor]; 104 | [button setTitle:title forState:UIControlStateNormal]; 105 | [button addTarget:self action:action forControlEvents:UIControlEventTouchDown]; 106 | [view addSubview:button]; 107 | return button; 108 | } 109 | 110 | - (void)didReceiveMemoryWarning { 111 | [super didReceiveMemoryWarning]; 112 | // Dispose of any resources that can be recreated. 113 | } 114 | 115 | /* 116 | #pragma mark - Navigation 117 | 118 | // In a storyboard-based application, you will often want to do a little preparation before navigation 119 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 120 | // Get the new view controller using [segue destinationViewController]. 121 | // Pass the selected object to the new view controller. 122 | } 123 | */ 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/18. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/18. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "FzhBluetooth.h" 11 | #import "FZBlueModel.h" 12 | #import "FZHProgressHUD.h" 13 | #import "FZSingletonManager.h" 14 | #import "SendViewController.h" 15 | 16 | #define UUID_String @"0000FEE9-0000-1000-8000-00805F9B34FB" 17 | 18 | @interface ViewController () 19 | { 20 | UITableView *mytableView; 21 | UILabel * nameLabel; //蓝牙名字 22 | UILabel * uuidStrLabel; //UUID 23 | UILabel * distanceLabel; //距离 24 | 25 | FZHProgressHUD * HUD; 26 | } 27 | @property (nonatomic,strong) NSMutableArray *blueListArr; 28 | @end 29 | 30 | @implementation ViewController 31 | 32 | - (void)viewDidLoad { 33 | [super viewDidLoad]; 34 | // Do any additional setup after loading the view, typically from a nib. 35 | 36 | self.title = @"搜索设备"; 37 | self.blueListArr = [[NSMutableArray alloc]init]; 38 | 39 | //加载圈 40 | HUD = [[FZHProgressHUD alloc]initWithView:self.navigationController.view]; 41 | [self.navigationController.view addSubview:HUD]; 42 | HUD.delegate = self; 43 | HUD.animationType = FZHProgressHUDAnimationZoom; 44 | HUD.labelText = @"连接中..."; 45 | 46 | //创建展示列表 47 | [self createTableView]; 48 | 49 | [FzhBluetooth shareInstance].delegate = self; 50 | //删除自动重连 51 | [[FzhBluetooth shareInstance] createAutomaticConnectionEquipmenWithSetOrDelate:DelateAutomaticConnectionEquipmen Peripheral:nil]; 52 | [[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"DeviceUUID"]; 53 | [[NSUserDefaults standardUserDefaults] synchronize]; 54 | 55 | [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(didHaveAutoConnection) name:PostAutoConnectionNotificaiton object:nil]; 56 | 57 | UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)]; 58 | label.text = @"请打开系统蓝牙"; 59 | label.textColor = [UIColor whiteColor]; 60 | label.textAlignment = NSTextAlignmentCenter; 61 | label.backgroundColor = [UIColor blackColor]; 62 | label.alpha = 0.3; 63 | label.userInteractionEnabled = NO; 64 | //监听蓝牙状态 65 | [[FzhBluetooth shareInstance] returnBluetoothStateWithBlock:^(NSInteger state) { 66 | if (state != 5) { 67 | [self.view addSubview:label]; 68 | } else { 69 | [label removeFromSuperview]; 70 | //搜索蓝牙设备 71 | [self scanBluetooths]; 72 | 73 | //是否有UUID,如果有自动重连 74 | NSString * uuid = [[NSUserDefaults standardUserDefaults] objectForKey:@"DeviceUUID"]; 75 | if (uuid.length > 0) { 76 | CBPeripheral * p = [[FzhBluetooth shareInstance] retrievePeripheralWithUUIDString:uuid]; 77 | 78 | [FzhBluetooth shareInstance] .UUIDString = UUID_String; 79 | [self autoCollectBluetoothWithPeripheral:p]; 80 | } 81 | } 82 | }]; 83 | } 84 | 85 | -(void)viewDidDisappear:(BOOL)animated 86 | { 87 | [HUD hide:YES]; 88 | } 89 | 90 | #pragma mark --- 创建蓝牙设备列表 91 | -(void)createTableView 92 | { 93 | mytableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight-64) style:UITableViewStylePlain]; 94 | mytableView.delegate = self; 95 | mytableView.dataSource = self; 96 | [self.view addSubview:mytableView]; 97 | } 98 | 99 | #pragma mark --- 有自动重连设备 100 | -(void)autoCollectBluetoothWithPeripheral:(CBPeripheral *)p 101 | { 102 | [HUD show:YES]; 103 | 104 | //停止扫描 105 | [[FzhBluetooth shareInstance] stopScan]; 106 | 107 | [[FzhBluetooth shareInstance] connectPeripheral:p completeBlock:^(CBPeripheral *peripheral, CBService *service, CBCharacteristic *character) { 108 | NSLog(@"链接成功"); 109 | [self->HUD hide:YES]; 110 | 111 | //当前蓝牙model 112 | FZBlueModel * blueModel = [[FZBlueModel alloc]init]; 113 | blueModel.blueName = peripheral.name; 114 | blueModel.peripheral = peripheral; 115 | blueModel.UUIDString = peripheral.identifier.UUIDString; 116 | 117 | [FZSingletonManager shareInstance].GPrint_Chatacter = [FzhBluetooth shareInstance] .writeCharacteristic; 118 | [FZSingletonManager shareInstance].GPrint_Peripheral = peripheral; 119 | 120 | //本地保存 121 | [[NSUserDefaults standardUserDefaults] setObject:blueModel.UUIDString forKey:@"DeviceUUID"]; 122 | [[NSUserDefaults standardUserDefaults] synchronize]; 123 | 124 | SendViewController *sendVC = [[SendViewController alloc]init]; 125 | [self.navigationController pushViewController:sendVC animated:YES]; 126 | } failBlock:^(CBPeripheral *peripheral, NSError *error) { 127 | NSLog(@"链接失败"); 128 | }]; 129 | } 130 | 131 | #pragma mark --- 搜索蓝牙设备 132 | -(void)scanBluetooths 133 | { 134 | [[FzhBluetooth shareInstance] scanForPeripheralsWithPrefixName:@"MI" discoverPeripheral:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) { 135 | NSLog(@"搜索到了设备:%@",peripheral.name); 136 | 137 | NSInteger perpheralIndex = -1 ; 138 | for (int i = 0; i < self.blueListArr.count; i++) { 139 | FZBlueModel *model = [[FZBlueModel alloc]init]; 140 | model = self.blueListArr[i]; 141 | if ([model.peripheral.identifier isEqual:peripheral.identifier]) { 142 | perpheralIndex = i ; 143 | break ; 144 | } 145 | } 146 | FZBlueModel *model = [[FZBlueModel alloc]init]; 147 | model.blueName = peripheral.name; 148 | model.peripheral = peripheral; 149 | model.UUIDString = peripheral.identifier.UUIDString; 150 | double min = [[FzhBluetooth shareInstance] fzRssiToNumber:RSSI]; 151 | model.distance = [NSString stringWithFormat:@"%.2f",min]; 152 | if (perpheralIndex != -1) { 153 | [self.blueListArr replaceObjectAtIndex:perpheralIndex withObject:model]; 154 | } 155 | else{ 156 | [self.blueListArr addObject:model]; 157 | } 158 | [self->mytableView reloadData]; 159 | }]; 160 | } 161 | 162 | -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 163 | { 164 | return self.blueListArr.count; 165 | } 166 | 167 | - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 168 | { 169 | static NSString *CellIdentifier = @"Cell"; 170 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 171 | if (cell == nil) { 172 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 173 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 174 | 175 | nameLabel = [[UILabel alloc]initWithFrame:CGRectMake(10, 5, ScreenWidth-20, 20)]; 176 | [cell addSubview:nameLabel]; 177 | 178 | uuidStrLabel = [[UILabel alloc]initWithFrame:CGRectMake(10, 25, ScreenWidth*3/4, 20)]; 179 | uuidStrLabel.font = [UIFont systemFontOfSize:11]; 180 | [cell addSubview:uuidStrLabel]; 181 | 182 | distanceLabel = [[UILabel alloc]initWithFrame:CGRectMake(ScreenWidth*3/4+10, 25, ScreenWidth/4-20, 20)]; 183 | distanceLabel.font = [UIFont systemFontOfSize:11]; 184 | distanceLabel.textAlignment = NSTextAlignmentRight; 185 | [cell addSubview:distanceLabel]; 186 | } 187 | while ([cell.contentView.subviews lastObject]) 188 | { 189 | [(UIView *)[cell.contentView.subviews lastObject] removeFromSuperview]; 190 | } 191 | 192 | FZBlueModel *blueModel = self.blueListArr[indexPath.row]; 193 | nameLabel.text = blueModel.blueName; 194 | uuidStrLabel.text = blueModel.UUIDString; 195 | distanceLabel.text = [NSString stringWithFormat:@"距离:%@米",blueModel.distance]; 196 | 197 | return cell; 198 | } 199 | 200 | -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 201 | { 202 | return 0.1; 203 | } 204 | 205 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 206 | { 207 | return 50; 208 | } 209 | 210 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 211 | { 212 | [HUD show:YES]; 213 | 214 | //停止扫描 215 | [[FzhBluetooth shareInstance] stopScan]; 216 | 217 | FZBlueModel *blueModel = self.blueListArr[indexPath.row]; 218 | //设置目标设备特征UUID 219 | [FzhBluetooth shareInstance] .UUIDString = UUID_String; 220 | //连接设备 221 | [[FzhBluetooth shareInstance] connectPeripheral:blueModel.peripheral completeBlock:^(CBPeripheral *peripheral, CBService *service, CBCharacteristic *character) { 222 | NSLog(@"链接成功"); 223 | [self->HUD hide:YES]; 224 | 225 | [FZSingletonManager shareInstance].GPrint_Chatacter = [FzhBluetooth shareInstance] .writeCharacteristic; 226 | [FZSingletonManager shareInstance].GPrint_Peripheral = peripheral; 227 | 228 | //本地保存 229 | [[NSUserDefaults standardUserDefaults] setObject:blueModel.UUIDString forKey:@"DeviceUUID"]; 230 | [[NSUserDefaults standardUserDefaults] synchronize]; 231 | 232 | //设置自动重连 233 | // [[FzhBluetooth shareInstance] createAutomaticConnectionEquipmenWithSetOrDelate:SetAutomaticConnectionEquipmen Peripheral:peripheral]; 234 | 235 | SendViewController *sendVC = [[SendViewController alloc]init]; 236 | [self.navigationController pushViewController:sendVC animated:YES]; 237 | 238 | } failBlock:^(CBPeripheral *peripheral, NSError *error) { 239 | NSLog(@"链接失败"); 240 | }]; 241 | } 242 | 243 | -(void)didHaveAutoConnection 244 | { 245 | [HUD show:YES]; 246 | } 247 | 248 | #pragma mark --- 有自动连接设备走到这里 249 | -(void)connectionWithPerpheral:(CBPeripheral *)peripheral 250 | { 251 | [FzhBluetooth shareInstance].UUIDString = UUID_String; 252 | [[FzhBluetooth shareInstance]createCharacticWithPeripheral:peripheral UUIDString:UUID_String]; 253 | 254 | [FZSingletonManager shareInstance].GPrint_Chatacter = [FzhBluetooth shareInstance] .writeCharacteristic; 255 | [FZSingletonManager shareInstance].GPrint_Peripheral = peripheral; 256 | 257 | [HUD hide:YES]; 258 | SendViewController *sendVC = [[SendViewController alloc]init]; 259 | [self.navigationController pushViewController:sendVC animated:YES]; 260 | } 261 | 262 | - (void)didReceiveMemoryWarning { 263 | [super didReceiveMemoryWarning]; 264 | // Dispose of any resources that can be recreated. 265 | } 266 | 267 | 268 | @end 269 | -------------------------------------------------------------------------------- /FZBluetoothExample/FzBluetoothDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/18. 6 | // Copyright © 2017年 付正. 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 | -------------------------------------------------------------------------------- /FZBluetoothExample/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | workspace 'FzBluetoothDemo' 3 | 4 | use_frameworks! 5 | 6 | target 'FzBluetoothDemo' do 7 | pod "FZBluetooth","~>1.0.2" 8 | end 9 | -------------------------------------------------------------------------------- /FZBluetoothExample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FZBluetooth (1.0.2) 3 | 4 | DEPENDENCIES: 5 | - FZBluetooth (~> 1.0.2) 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - FZBluetooth 10 | 11 | SPEC CHECKSUMS: 12 | FZBluetooth: 11ca2e44d24c27533402b30eba58b440afbe82cb 13 | 14 | PODFILE CHECKSUM: aaed65e0c86c6a73c0d4778f6eec5013684282b1 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/FZBluetooth/FZBluetoothExample/FzBluetoothDemo/FZHBluetooth/FzhBluetooth.h: -------------------------------------------------------------------------------- 1 | // 2 | // FzhBluetooth.h 3 | // BlueTooth 4 | // 5 | // Created by 付正 on 2017/9/14. 6 | // Copyright © 2017年 BFMobile. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "FzhCallBlock.h" 12 | 13 | typedef enum : NSUInteger { 14 | SetAutomaticConnectionEquipmen = 0, //设置自动连接的设备 15 | DelateAutomaticConnectionEquipmen, //删除自动连接的设备 16 | } AutomaticConnectionEquipmenEnum; 17 | 18 | @protocol FZAutomaticConnectionDelegate 19 | 20 | /** 21 | 自动连接的设备代理方法 22 | */ 23 | @optional - (void)connectionWithPerpheral:(CBPeripheral *)peripheral; 24 | 25 | @end 26 | 27 | @interface FzhBluetooth : NSObject 28 | 29 | @property (assign,nonatomic) AutomaticConnectionEquipmenEnum connectionEquipment; 30 | @property (strong, nonatomic) id delegate; 31 | @property (copy, nonatomic) FZStateUpdateBlock stateUpdateBlock; 32 | /** 发现一个蓝牙外设的回调 */ 33 | @property (copy, nonatomic) FZDiscoverPeripheralBlock discoverPeripheralBlcok; 34 | /** 连接外设完成的回调 */ 35 | @property (copy, nonatomic) FZConnectSuccessBlock connectSuccessBlock; 36 | /** 连接外设失败的回调 */ 37 | @property (copy, nonatomic) FZConnectFailBlock connectFailBlock; 38 | /** 获取蓝牙外设信号强度的回调 */ 39 | @property (copy, nonatomic) FZGetRSSIBlock getRSSIBlock; 40 | /** 将数据写入特性中的回调 */ 41 | @property (copy, nonatomic) FZWriteToCharacteristicBlock writeToCharacteristicBlock; 42 | @property (copy, nonatomic) FZEquipmentReturnBlock equipmentReturnBlock; 43 | 44 | @property (nonatomic, strong) CBCharacteristic * writeCharacteristic;//可写入参数,蓝牙连接成功,2秒后获得参数值 45 | @property (nonatomic, strong) CBCharacteristic * notifyCharacteristic;//可通知参数,蓝牙连接成功,2秒后获得参数值 46 | 47 | +(instancetype)shareInstance; 48 | 49 | /** 50 | 设置设备特性UUID,如果不设置默认为空 51 | */ 52 | @property (nonatomic, strong) NSString *UUIDString; 53 | 54 | /** 55 | * 连接设备后获取到的所有Service 56 | */ 57 | @property (nonatomic, strong) NSMutableArray *serviceArr; 58 | 59 | /** 60 | 系统当前蓝牙的状态 61 | 62 | @param stateBlock 实时返回当前蓝牙状态 63 | */ 64 | - (void)returnBluetoothStateWithBlock:(FZStateUpdateBlock)stateBlock; 65 | 66 | /** 67 | * 开始搜索蓝牙外设,每次在block中返回一个蓝牙外设信息 68 | * 69 | * @param nameStr 模糊搜索设备名称,目标设备名称包含字段 70 | * 返回的block参数可参考CBCentralManager 的 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 71 | * 72 | * @param discoverBlock 搜索到蓝牙外设后的回调 73 | */ 74 | - (void)scanForPeripheralsWithPrefixName:(NSString *)nameStr discoverPeripheral:(FZDiscoverPeripheralBlock)discoverBlock; 75 | 76 | /** 77 | * 连接某个蓝牙外设,并查询服务,特性,特性描述 78 | * 79 | * @param peripheral 要连接的蓝牙外设 80 | * @param completionBlock 操作执行完的回调 81 | */ 82 | - (void)connectPeripheral:(CBPeripheral *)peripheral 83 | completeBlock:(FZConnectSuccessBlock)completionBlock 84 | failBlock:(FZConnectFailBlock)failBlock; 85 | 86 | /** 87 | * 获取某外设的距离 88 | * 89 | * @param getRSSIBlock 获取信号完成后的回调 90 | */ 91 | - (void)readRSSICompletionBlock:(FZGetRSSIBlock)getRSSIBlock; 92 | 93 | /** 94 | RSSI转距离number 95 | 96 | @param RSSI RSSI 97 | @return 距离 98 | */ 99 | - (double)fzRssiToNumber:(NSNumber *)RSSI; 100 | 101 | /** 102 | * 往某个特性中写入数据,自动识别数据长度超过限制分段传输 103 | * 104 | * @param dataStr 写入的数据 105 | * @param characteristic 特性对象 106 | * @param completionBlock 写入完成后的回调,只有type为CBCharacteristicWriteWithResponse时,才会回调 107 | */ 108 | - (void)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic completionBlock:(FZWriteToCharacteristicBlock)completionBlock returnBlock:(FZEquipmentReturnBlock)equipmentBlock; 109 | 110 | /** 111 | * 往某个特性中写入数据,同步返回结果 112 | * 113 | * @param dataStr 写入的数据 114 | * @param characteristic 特性对象 115 | * @return 设备应答结果 returnStr 应答内容 error 错误信息 116 | */ 117 | -(NSDictionary *)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic; 118 | 119 | /** 120 | * 停止扫描 121 | */ 122 | - (void)stopScan; 123 | 124 | /** 125 | * 断开蓝牙连接 126 | */ 127 | - (void)cancelPeripheralConnection; 128 | 129 | /** 130 | 设置或删除自动连接设备 131 | 设置后,在代理方法connectionWithPerpheral:里会返回设备的peripheral 132 | 133 | @param setOrDel 自动连接和删除自动连接 134 | @param peripheral 设备peripheral 135 | */ 136 | -(void)createAutomaticConnectionEquipmenWithSetOrDelate:(AutomaticConnectionEquipmenEnum)setOrDel Peripheral:(CBPeripheral *)peripheral; 137 | 138 | /** 139 | 通过UUID获取peripheral 140 | 用户自主记录想要自动连接的UUID,获取peripheral后调用连接方法 141 | 142 | @param UUIDString UUID 143 | @return peripheral 144 | */ 145 | - (CBPeripheral *)retrievePeripheralWithUUIDString:(NSString *)UUIDString; 146 | 147 | /** 148 | 重新设置特征值 149 | 150 | @param peripheral peripheral 151 | @param uuidString uuidString 152 | */ 153 | -(void)createCharacticWithPeripheral:(CBPeripheral *)peripheral UUIDString:(NSString *)uuidString; 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/FZBluetooth/FZBluetoothExample/FzBluetoothDemo/FZHBluetooth/FzhBluetooth.m: -------------------------------------------------------------------------------- 1 | // 2 | // FzhBluetooth.m 3 | // BlueTooth 4 | // 5 | // Created by 付正 on 2017/9/14. 6 | // Copyright © 2017年 BFMobile. All rights reserved. 7 | // 8 | 9 | #import "FzhBluetooth.h" 10 | #import "FzhString.h" 11 | 12 | @implementation FzhBluetooth 13 | { 14 | CBCentralManager *fzhCentralManager; 15 | CBPeripheral *fzhPeripheral; 16 | CBCharacteristic *fzgCharacteristic; 17 | 18 | NSString *prefixString; //记录设备名称包含字符串 19 | NSString *resultStr; //记录应答数据 20 | 21 | NSInteger writeCount; /**< 写入次数 */ 22 | NSInteger responseCount; /**< 返回次数 */ 23 | } 24 | 25 | +(instancetype)shareInstance 26 | { 27 | static FzhBluetooth *share = nil; 28 | static dispatch_once_t oneToken; 29 | dispatch_once(&oneToken, ^{ 30 | share = [[FzhBluetooth alloc]init]; 31 | }); 32 | return share; 33 | } 34 | 35 | - (instancetype)init 36 | { 37 | self = [super init]; 38 | if (self) { 39 | //CBCentralManagerOptionShowPowerAlertKey对应的BOOL值,当设为YES时,表示CentralManager初始化时,如果蓝牙没有打开,将弹出Alert提示框 40 | //CBCentralManagerOptionRestoreIdentifierKey对应的是一个唯一标识的字符串,用于蓝牙进程被杀掉恢复连接时用的。 41 | // dispatch_queue_t centralQueue = dispatch_queue_create("centralQueue",DISPATCH_QUEUE_SERIAL); 42 | // NSDictionary *dic = @{CBCentralManagerOptionShowPowerAlertKey : [NSNumber numberWithBool:YES], 43 | // CBCentralManagerOptionRestoreIdentifierKey : @"unique identifier" 44 | // }; 45 | // fzhCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:dic]; 46 | fzhCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; 47 | resultStr = @""; 48 | self.serviceArr = [[NSMutableArray alloc]init]; 49 | } 50 | return self; 51 | } 52 | 53 | - (void)dealloc 54 | { 55 | [self stopScan]; 56 | fzhCentralManager.delegate = nil; 57 | fzhPeripheral.delegate = nil; 58 | } 59 | 60 | #pragma mark --- 系统当前蓝牙的状态 61 | - (void)returnBluetoothStateWithBlock:(FZStateUpdateBlock)stateBlock 62 | { 63 | self.stateUpdateBlock = stateBlock; 64 | } 65 | 66 | //检查App的设备BLE是否可用 (ensure that Bluetooth low energy is supported and available to use on the central device) 67 | - (void)centralManagerDidUpdateState:(CBCentralManager *)central 68 | { 69 | if (_stateUpdateBlock) { 70 | _stateUpdateBlock(central.state); 71 | } 72 | switch (central.state) 73 | { 74 | case CBCentralManagerStatePoweredOn: 75 | //discover what peripheral devices are available for your app to connect to 76 | //第一个参数为CBUUID的数组,需要搜索特点服务的蓝牙设备,只要每搜索到一个符合条件的蓝牙设备都会调用didDiscoverPeripheral代理方法 77 | [self startScan]; 78 | break; 79 | default: 80 | NSLog(@"Central Manager did change state"); 81 | break; 82 | } 83 | } 84 | 85 | #pragma mark --- 搜索蓝牙设备 86 | - (void)scanForPeripheralsWithPrefixName:(NSString *)nameStr discoverPeripheral:(FZDiscoverPeripheralBlock)discoverBlock; 87 | { 88 | prefixString = nameStr; 89 | [self startScan]; 90 | self.discoverPeripheralBlcok = discoverBlock; 91 | } 92 | 93 | - (void)startScan 94 | { 95 | //不重复扫描已发现设备 96 | //CBCentralManagerScanOptionAllowDuplicatesKey设置为NO表示不重复扫瞄已发现设备,为YES就是允许。 97 | //CBCentralManagerOptionShowPowerAlertKey设置为YES就是在蓝牙未打开的时候显示弹框 98 | // NSDictionary *option = @{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO],CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:YES]}; 99 | // [fzhCentralManager scanForPeripheralsWithServices:nil options:option]; 100 | [fzhCentralManager scanForPeripheralsWithServices:nil options:nil]; 101 | } 102 | 103 | #pragma mark --- 连接蓝牙 104 | - (void)connectPeripheral:(CBPeripheral *)peripheral 105 | completeBlock:(FZConnectSuccessBlock)completionBlock 106 | failBlock:(FZConnectFailBlock)failBlock 107 | { 108 | fzhPeripheral = peripheral; 109 | 110 | //连接设备 111 | [fzhCentralManager connectPeripheral:peripheral options:nil]; 112 | //4.设置代理 113 | // fzhPeripheralManager.delegate = self; 114 | 115 | self.connectSuccessBlock = completionBlock; 116 | self.connectFailBlock = failBlock; 117 | 118 | } 119 | 120 | //搜索蓝牙代理方法 121 | //在蓝牙于后台被杀掉时,重连之后会首先调用此方法,可以获取蓝牙恢复时的各种状态 122 | - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI 123 | { 124 | // 125 | NSString *perName = [[NSUserDefaults standardUserDefaults] objectForKey:@"conPeripheral"]; 126 | NSString *setOrDelNum = [[NSUserDefaults standardUserDefaults] objectForKey:@"setOrDel"]; 127 | 128 | if (_discoverPeripheralBlcok) { 129 | if (prefixString.length > 0) { 130 | if ([peripheral.name hasPrefix:prefixString]) { 131 | _discoverPeripheralBlcok(central,peripheral,advertisementData,RSSI); 132 | } 133 | } else { 134 | _discoverPeripheralBlcok(central,peripheral,advertisementData,RSSI); 135 | } 136 | [self startScan]; 137 | } 138 | if ([setOrDelNum isEqualToString:@"0"]) { 139 | //有自动重连的设备 140 | [[NSNotificationCenter defaultCenter]postNotificationName:PostAutoConnectionNotificaiton object:nil]; 141 | if ([peripheral.name isEqualToString:perName]) { 142 | fzhPeripheral = peripheral; 143 | [fzhCentralManager connectPeripheral:peripheral options:nil]; 144 | } 145 | } 146 | } 147 | 148 | // 蓝牙设备连接成功 149 | - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral 150 | { 151 | [fzhCentralManager stopScan]; 152 | //Before you begin interacting with the peripheral, you should set the peripheral’s delegate to ensure that it receives the appropriate callbacks(设置代理) 153 | [fzhPeripheral setDelegate:self]; 154 | //discover all of the services that a peripheral offers,搜索服务,回调didDiscoverServices 155 | [fzhPeripheral discoverServices:nil]; 156 | 157 | //延时操作 158 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 159 | if (self.connectSuccessBlock) { 160 | self.connectSuccessBlock(peripheral,nil,self.writeCharacteristic); 161 | } else { 162 | //返回连接成功 163 | if ([self.delegate respondsToSelector:@selector(connectionWithPerpheral:)]) { 164 | [self.delegate connectionWithPerpheral:peripheral]; 165 | } 166 | } 167 | }); 168 | } 169 | 170 | // 连接失败,就会得到回调: 171 | - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error 172 | { 173 | if (_connectFailBlock) { 174 | _connectFailBlock(peripheral,error); 175 | } 176 | } 177 | 178 | #pragma mark --- 断开蓝牙连接走这里 179 | - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error 180 | { 181 | [self cancelPeripheralConnection]; 182 | if (error) 183 | { 184 | NSLog(@">>> didDisconnectPeripheral for %@ with error: %@", peripheral.name, [error localizedDescription]); 185 | } 186 | } 187 | 188 | #pragma mark --- 停止扫描 189 | - (void)stopScan 190 | { 191 | [fzhCentralManager stopScan]; 192 | } 193 | 194 | #pragma mark --- 断开蓝牙连接 195 | - (void)cancelPeripheralConnection 196 | { 197 | if (fzhPeripheral) { 198 | [fzhCentralManager cancelPeripheralConnection:fzhPeripheral]; 199 | } 200 | } 201 | 202 | #pragma mark --- 设置或删除自动连接设备 203 | -(void)createAutomaticConnectionEquipmenWithSetOrDelate:(AutomaticConnectionEquipmenEnum)setOrDel Peripheral:(CBPeripheral *)peripheral 204 | { 205 | self.connectionEquipment = setOrDel; 206 | if (setOrDel == SetAutomaticConnectionEquipmen) { 207 | //设置自动连接设备 208 | [[NSUserDefaults standardUserDefaults] setObject:peripheral.name forKey:@"conPeripheral"]; 209 | [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%lu",(long)setOrDel] forKey:@"setOrDel"]; 210 | [[NSUserDefaults standardUserDefaults] synchronize]; 211 | } else if (setOrDel == DelateAutomaticConnectionEquipmen) { 212 | //删除自动连接设备 213 | [[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"conPeripheral"]; 214 | [[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"setOrDel"]; 215 | [[NSUserDefaults standardUserDefaults] synchronize]; 216 | } 217 | } 218 | 219 | #pragma mark --- 通过UUID获取peripheral 220 | - (CBPeripheral *)retrievePeripheralWithUUIDString:(NSString *)UUIDString 221 | { 222 | CBPeripheral *p = nil; 223 | @try { 224 | NSUUID *uuid = [[NSUUID alloc]initWithUUIDString:UUIDString]; 225 | p = [fzhCentralManager retrievePeripheralsWithIdentifiers:@[uuid]][0]; 226 | } @catch (NSException *exception) { 227 | NSLog(@">>> retrievePeripheralWithUUIDString error:%@",exception); 228 | } @finally { 229 | } 230 | return p; 231 | } 232 | 233 | //获取服务后的回调 234 | - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error 235 | { 236 | if (error) { 237 | NSLog(@"didDiscoverServices : %@", [error localizedDescription]); 238 | return; 239 | } 240 | 241 | //提取出所有特征 242 | self.serviceArr = [NSMutableArray arrayWithArray:peripheral.services]; 243 | 244 | for (CBService *s in peripheral.services) { 245 | [s.peripheral discoverCharacteristics:nil forService:s]; 246 | } 247 | } 248 | 249 | //获取特征后的回调 250 | - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error 251 | { 252 | if (error) { 253 | NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]); 254 | return; 255 | } 256 | 257 | #pragma mark ---此处需要筛选自己需要的特征 258 | [self createCharacticWithPeripheral:peripheral Service:service]; 259 | } 260 | 261 | #pragma mark --- 重新设置特征值 262 | -(void)createCharacticWithPeripheral:(CBPeripheral *)peripheral UUIDString:(NSString *)uuidString 263 | { 264 | for (CBService *s in peripheral.services) { 265 | [s.peripheral discoverCharacteristics:nil forService:s]; 266 | 267 | if ([s.UUID isEqual: [CBUUID UUIDWithString:uuidString]]) { 268 | [self createCharacticWithPeripheral:peripheral Service:s]; 269 | } 270 | } 271 | } 272 | 273 | // 筛选特征 274 | -(void)createCharacticWithPeripheral:(CBPeripheral *)peripheral Service:(CBService *)service 275 | { 276 | if (self.UUIDString) { 277 | if (![service.UUID isEqual:[CBUUID UUIDWithString:self.UUIDString]]) { 278 | return; 279 | } 280 | } 281 | 282 | for (CBCharacteristic *c in service.characteristics) 283 | { 284 | CBCharacteristicProperties properties = c.properties; 285 | if (properties & CBCharacteristicPropertyWrite) { 286 | fzgCharacteristic = c; 287 | self.writeCharacteristic = c; 288 | }else if (properties & CBCharacteristicPropertyNotify) { 289 | self.notifyCharacteristic = c; 290 | [peripheral setNotifyValue:YES forCharacteristic:c]; 291 | }else if (properties & CBCharacteristicPropertyWriteWithoutResponse) { 292 | fzgCharacteristic = c; 293 | self.writeCharacteristic = c; 294 | }else if (properties & CBCharacteristicPropertyIndicate) { 295 | self.notifyCharacteristic = c; 296 | } 297 | } 298 | } 299 | 300 | //订阅的特征值有新的数据时回调 301 | - (void)peripheral:(CBPeripheral *)peripheral 302 | didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic 303 | error:(NSError *)error 304 | { 305 | if (error) { 306 | NSLog(@"Error changing notification state: %@", 307 | [error localizedDescription]); 308 | } 309 | [peripheral readValueForCharacteristic:characteristic]; 310 | } 311 | 312 | // 读数据时回调 313 | - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error 314 | { 315 | if (error) { 316 | //报错了 317 | NSLog(@"didUpdateValueForCharacteristic error : %@", error.localizedDescription); 318 | return; 319 | } 320 | if (!characteristic.value) { 321 | return; 322 | } 323 | if (resultStr.length <= 0 || ![resultStr isEqualToString:[[FzhString sharedInstance] fzHexStringFromData:characteristic.value]]) { 324 | resultStr = [[FzhString sharedInstance]fzHexStringFromData:characteristic.value]; 325 | if (_equipmentReturnBlock) { 326 | _equipmentReturnBlock(peripheral,characteristic,resultStr,error); 327 | } 328 | } 329 | } 330 | 331 | //写数据后回调 332 | - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic 333 | error:(NSError *)error 334 | { 335 | resultStr = @""; 336 | if (!_writeToCharacteristicBlock) { 337 | return; 338 | } 339 | responseCount ++; 340 | if (writeCount != responseCount) { 341 | return; 342 | } 343 | _writeToCharacteristicBlock(characteristic,error); 344 | } 345 | 346 | #pragma mark --- 写入数据 347 | - (void)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic completionBlock:(FZWriteToCharacteristicBlock)completionBlock returnBlock:(FZEquipmentReturnBlock)equipmentBlock 348 | { 349 | writeCount = 0; 350 | responseCount = 0; 351 | 352 | NSMutableArray *sendArr = [[NSMutableArray alloc]init]; 353 | if (dataStr.length > 40) { 354 | int count = (int)dataStr.length / 40 + 1; 355 | for (int i = 0; i < count; i ++) { 356 | if (i < count-1) { 357 | [sendArr addObject:[dataStr substringWithRange:NSMakeRange(0+i*40, 40)]]; 358 | } else { 359 | [sendArr addObject:[dataStr substringFromIndex:i*40]]; 360 | } 361 | } 362 | for (NSString *sendStr in sendArr) { 363 | NSData *data = [[FzhString sharedInstance] convertHexStrToData:sendStr]; 364 | _writeToCharacteristicBlock = completionBlock; 365 | _equipmentReturnBlock = equipmentBlock; 366 | if (fzhPeripheral == nil) { 367 | NSString *desc = NSLocalizedString(@"Not connected devices", @""); 368 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; 369 | NSError *error = [NSError errorWithDomain:@"com.okey.wearkit.ErrorDomain" 370 | code:-101 371 | userInfo:userInfo]; 372 | _writeToCharacteristicBlock(nil,error); 373 | return; 374 | } 375 | [fzhPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; 376 | writeCount ++; 377 | } 378 | return; 379 | } 380 | NSData *data = [[FzhString sharedInstance] convertHexStrToData:dataStr]; 381 | _writeToCharacteristicBlock = completionBlock; 382 | _equipmentReturnBlock = equipmentBlock; 383 | if (fzhPeripheral == nil) { 384 | NSString *desc = NSLocalizedString(@"Not connected devices", @""); 385 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; 386 | NSError *error = [NSError errorWithDomain:@"com.okey.wearkit.ErrorDomain" 387 | code:-101 388 | userInfo:userInfo]; 389 | _writeToCharacteristicBlock(nil,error); 390 | return; 391 | } 392 | [fzhPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; 393 | writeCount ++; 394 | } 395 | 396 | #pragma mark --- 写入数据 同步返回数据 397 | -(NSDictionary *)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic 398 | { 399 | __block NSString * str = nil; 400 | __block NSError *reError = nil; 401 | [self writeValue:dataStr forCharacteristic:characteristic completionBlock:^(CBCharacteristic *characteristic, NSError *error) { 402 | NSLog(@"发送成功"); 403 | reError = error; 404 | } returnBlock:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSString *returnStr, NSError *error) { 405 | str = returnStr; 406 | reError = error; 407 | }]; 408 | 409 | while (!str) { 410 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 411 | } 412 | 413 | NSMutableDictionary *dict = [[NSMutableDictionary alloc]init]; 414 | [dict setValue:reError forKey:@"error"]; 415 | [dict setValue:str forKey:@"returnStr"]; 416 | return dict; 417 | } 418 | 419 | #pragma mark --- 获取外设的信号强度 420 | - (void)readRSSICompletionBlock:(FZGetRSSIBlock)getRSSIBlock 421 | { 422 | self.getRSSIBlock = getRSSIBlock; 423 | [fzhPeripheral readRSSI]; 424 | } 425 | 426 | #pragma mark ---------------- 获取信号之后的回调 ------------------ 427 | # if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 428 | - (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error { 429 | if (_getRSSIBlock) { 430 | double number = [self fzRssiToNumber:peripheral.RSSI]; 431 | _getRSSIBlock(number,error); 432 | } 433 | } 434 | #else 435 | - (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error { 436 | if (_getRSSIBlock) { 437 | double number = [self fzRssiToNumber:RSSI]; 438 | _getRSSIBlock(number,error); 439 | } 440 | } 441 | #endif 442 | 443 | #pragma mark - RSSI转距离number 444 | - (double)fzRssiToNumber:(NSNumber *)RSSI 445 | { 446 | int tempRssi = [RSSI intValue]; 447 | int absRssi = abs(tempRssi); 448 | float power = (absRssi-75)/(10*2.0); 449 | double number = pow(10, power);//除0外,任何数的0次方等于1 450 | return number; 451 | } 452 | 453 | @end 454 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/FZBluetooth/FZBluetoothExample/FzBluetoothDemo/FZHBluetooth/FzhCallBlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // FzhCallBlock.h 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/21. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #ifndef FzhCallBlock_h 10 | #define FzhCallBlock_h 11 | 12 | #define PostAutoConnectionNotificaiton @"AutoConnectionNotificaiton" 13 | 14 | /** 蓝牙状态改变的block */ 15 | typedef void(^FZStateUpdateBlock)(NSInteger state); 16 | /** 发现一个蓝牙外设的block */ 17 | typedef void(^FZDiscoverPeripheralBlock)(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI); 18 | /** 连接完成的block*/ 19 | typedef void(^FZConnectSuccessBlock)(CBPeripheral *peripheral,CBService *service, CBCharacteristic *character); 20 | /** 连接失败的block*/ 21 | typedef void(^FZConnectFailBlock)(CBPeripheral *peripheral, NSError *error); 22 | /** 获取蓝牙外设信号的回调 */ 23 | typedef void(^FZGetRSSIBlock)(double number, NSError *error); 24 | /** 往特性中写入数据的回调 */ 25 | typedef void(^FZWriteToCharacteristicBlock)(CBCharacteristic *characteristic, NSError *error); 26 | /** 设备返回数据的回调 */ 27 | typedef void(^FZEquipmentReturnBlock)(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSString *returnStr, NSError *error); 28 | 29 | #endif /* FzhCallBlock_h */ 30 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/FZBluetooth/FZBluetoothExample/FzBluetoothDemo/FZHBluetooth/FzhString.h: -------------------------------------------------------------------------------- 1 | // 2 | // FzhString.h 3 | // OkeyBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/20. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FzhString : NSObject 12 | 13 | +(FzhString *)sharedInstance; 14 | 15 | /** 16 | 异或运算 17 | 18 | @param pan 字符串1 19 | @param pinv 字符串2 20 | @return 返回异或结果 21 | */ 22 | - (NSString *)pinxCreator:(NSString *)pan withPinv:(NSString *)pinv; 23 | 24 | /** 25 | 16进制转10进制 26 | 27 | @param sixteenStr 16进制字符串 28 | @return 10进制字符串 29 | */ 30 | -(NSString *)sixteenChangeTenString:(NSString *)sixteenStr; 31 | 32 | /** 33 | 10进制转16进制 34 | 35 | @param decimal 10进制数字 36 | @return 16进制字符串 37 | */ 38 | - (NSString *)hexStringFromString:(NSInteger)decimal; 39 | 40 | /** 41 | @see 16进制字符串转2进制数据 42 | @param hex 16进制字符串 43 | @return 2进制数据 44 | */ 45 | -(NSData *)hex2data:(NSString *)hex; 46 | 47 | /** 48 | @see 16进制字符串转2进制字符串 49 | @param hex 16进制字符串 50 | @return 2进制字符串 51 | */ 52 | -(NSString *)getBinaryByhex:(NSString *)hex; 53 | 54 | /** 55 | @see 2进制字符串转16进制字符串 56 | @param binary 2进制字符串 57 | @return 16进制字符串 58 | */ 59 | -(NSString *)getHexadecimalWithBinary:(NSString *)binary; 60 | 61 | /** 62 | NSData转16进制NSString 63 | 64 | @param data data数据 65 | @return string数据 66 | */ 67 | - (NSString *)fzHexStringFromData:(NSData *)data; 68 | 69 | /** 70 | NSString转NSData 71 | 72 | @param str string数据 73 | @return data数据 74 | */ 75 | - (NSMutableData *)convertHexStrToData:(NSString *)str; 76 | 77 | /** 78 | 自定义error信息 79 | 80 | @param domain 标志字段(如:com.okey.wearkit.domain) 81 | @param code error编号 82 | @param errorStr error信息 83 | @return error 84 | */ 85 | -(NSError *)returnErrorWithDomain:(NSString *)domain Code:(int)code ErrorStr:(NSString *)errorStr; 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/FZBluetooth/FZBluetoothExample/FzBluetoothDemo/FZHBluetooth/FzhString.m: -------------------------------------------------------------------------------- 1 | // 2 | // FzhString.m 3 | // OkeyBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/20. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import "FzhString.h" 10 | 11 | @implementation FzhString 12 | 13 | //创建一个单例 14 | static FzhString *dbManger=nil; 15 | +(FzhString *)sharedInstance 16 | { 17 | @synchronized(self){ 18 | if (nil==dbManger) { 19 | dbManger=[[FzhString alloc]init]; 20 | } 21 | return dbManger; 22 | } 23 | } 24 | 25 | #pragma mark --- 异或运算 26 | - (NSString *)pinxCreator:(NSString *)pan withPinv:(NSString *)pinv 27 | { 28 | if (pan.length != pinv.length) 29 | { 30 | return nil; 31 | } 32 | const char *panchar = [pan UTF8String]; 33 | const char *pinvchar = [pinv UTF8String]; 34 | NSString *temp = [[NSString alloc] init]; 35 | for (int i = 0; i < pan.length; i++) 36 | { 37 | int panValue = [self charToint:panchar[i]]; 38 | int pinvValue = [self charToint:pinvchar[i]]; 39 | 40 | temp = [temp stringByAppendingString:[NSString stringWithFormat:@"%X",panValue^pinvValue]]; 41 | } 42 | return temp; 43 | } 44 | - (int)charToint:(char)tempChar 45 | { 46 | if (tempChar >= '0' && tempChar <='9') { 47 | return tempChar - '0'; 48 | } else if (tempChar >= 'A' && tempChar <= 'F') { 49 | return tempChar - 'A' + 10; 50 | } 51 | return 0; 52 | } 53 | 54 | #pragma mark --- 2进制字符串转16进制字符串 55 | -(NSString *)getHexadecimalWithBinary:(NSString *)binary 56 | { 57 | NSMutableDictionary *hexDic = [[NSMutableDictionary alloc] init]; 58 | [hexDic setObject:@"0" forKey:@"0000"]; 59 | [hexDic setObject:@"1" forKey:@"0001"]; 60 | [hexDic setObject:@"2" forKey:@"0010"]; 61 | [hexDic setObject:@"3" forKey:@"0011"]; 62 | [hexDic setObject:@"4" forKey:@"0100"]; 63 | [hexDic setObject:@"5" forKey:@"0101"]; 64 | [hexDic setObject:@"6" forKey:@"0110"]; 65 | [hexDic setObject:@"7" forKey:@"0111"]; 66 | [hexDic setObject:@"8" forKey:@"1000"]; 67 | [hexDic setObject:@"9" forKey:@"1001"]; 68 | [hexDic setObject:@"a" forKey:@"1010"]; 69 | [hexDic setObject:@"b" forKey:@"1011"]; 70 | [hexDic setObject:@"c" forKey:@"1100"]; 71 | [hexDic setObject:@"d" forKey:@"1101"]; 72 | [hexDic setObject:@"e" forKey:@"1110"]; 73 | [hexDic setObject:@"f" forKey:@"1111"]; 74 | NSString *binaryString=@""; 75 | for (int i = 0; i < binary.length; i ++) { 76 | NSString *key = [binary substringWithRange:NSMakeRange(i, 4)]; 77 | binaryString = [binaryString stringByAppendingString:[hexDic objectForKey:key]]; 78 | i += 3; 79 | } 80 | 81 | return binaryString; 82 | } 83 | 84 | #pragma mark --- 16进制字符串转2进制字符串 85 | -(NSString *)getBinaryByhex:(NSString *)hex 86 | { 87 | NSMutableDictionary *hexDic = [[NSMutableDictionary alloc] init]; 88 | [hexDic setObject:@"0000" forKey:@"0"]; 89 | [hexDic setObject:@"0001" forKey:@"1"]; 90 | [hexDic setObject:@"0010" forKey:@"2"]; 91 | [hexDic setObject:@"0011" forKey:@"3"]; 92 | [hexDic setObject:@"0100" forKey:@"4"]; 93 | [hexDic setObject:@"0101" forKey:@"5"]; 94 | [hexDic setObject:@"0110" forKey:@"6"]; 95 | [hexDic setObject:@"0111" forKey:@"7"]; 96 | [hexDic setObject:@"1000" forKey:@"8"]; 97 | [hexDic setObject:@"1001" forKey:@"9"]; 98 | [hexDic setObject:@"1010" forKey:@"A"]; 99 | [hexDic setObject:@"1011" forKey:@"B"]; 100 | [hexDic setObject:@"1100" forKey:@"C"]; 101 | [hexDic setObject:@"1101" forKey:@"D"]; 102 | [hexDic setObject:@"1110" forKey:@"E"]; 103 | [hexDic setObject:@"1111" forKey:@"F"]; 104 | [hexDic setObject:@"1010" forKey:@"a"]; 105 | [hexDic setObject:@"1011" forKey:@"b"]; 106 | [hexDic setObject:@"1100" forKey:@"c"]; 107 | [hexDic setObject:@"1101" forKey:@"d"]; 108 | [hexDic setObject:@"1110" forKey:@"e"]; 109 | [hexDic setObject:@"1111" forKey:@"f"]; 110 | NSMutableString *binaryString=[[NSMutableString alloc] init]; 111 | for (int i=0; i<[hex length]; i++) { 112 | NSRange rage; 113 | rage.length = 1; 114 | rage.location = i; 115 | NSString *key = [hex substringWithRange:rage]; 116 | binaryString = [NSMutableString stringWithFormat:@"%@%@",binaryString,[NSString stringWithFormat:@"%@",[hexDic objectForKey:key]]]; 117 | } 118 | return binaryString; 119 | } 120 | 121 | #pragma mark --- 十进制准换为十六进制字符串 122 | - (NSString *)hexStringFromString:(NSInteger)decimal 123 | { 124 | NSString *hex =@""; 125 | NSString *letter; 126 | NSInteger number; 127 | for (int i = 0; i<9; i++) { 128 | number = decimal % 16; 129 | decimal = decimal / 16; 130 | switch (number) { 131 | case 10: 132 | letter =@"a"; break; 133 | case 11: 134 | letter =@"b"; break; 135 | case 12: 136 | letter =@"c"; break; 137 | case 13: 138 | letter =@"d"; break; 139 | case 14: 140 | letter =@"e"; break; 141 | case 15: 142 | letter =@"f"; break; 143 | default: 144 | letter = [NSString stringWithFormat:@"%ld", (long)number]; 145 | } 146 | hex = [letter stringByAppendingString:hex]; 147 | if (decimal == 0) { 148 | 149 | break; 150 | } 151 | } 152 | if (hex.length == 1) { 153 | hex = [NSString stringWithFormat:@"0%@",hex]; 154 | } 155 | return hex; 156 | } 157 | 158 | #pragma mark --- 16进制转10进制 159 | -(NSString *)sixteenChangeTenString:(NSString *)sixteenStr 160 | { 161 | NSString * temp10 = [NSString stringWithFormat:@"%lu",strtoul([sixteenStr UTF8String],0,16)]; 162 | return temp10; 163 | } 164 | 165 | #pragma mark --- 16进制转2进制数据 166 | -(NSData *)hex2data:(NSString *)hex 167 | { 168 | NSMutableData *data = [NSMutableData dataWithCapacity:hex.length / 2]; 169 | unsigned char whole_byte; 170 | char byte_chars[3] = {'\0','\0','\0'}; 171 | int i; 172 | for (i=0; i < hex.length / 2; i++) { 173 | byte_chars[0] = [hex characterAtIndex:i*2]; 174 | byte_chars[1] = [hex characterAtIndex:i*2+1]; 175 | whole_byte = strtol(byte_chars, NULL, 16); 176 | [data appendBytes:&whole_byte length:1]; 177 | } 178 | return data; 179 | } 180 | 181 | #pragma mark - NSData转16进制NSString 182 | - (NSString *)fzHexStringFromData:(NSData *)data 183 | { 184 | Byte *bytes = (Byte *)[data bytes]; 185 | //下面是Byte 转换为16进制 186 | NSString *hexStr=@""; 187 | for(int i=0;i<[data length];i++){ 188 | NSString *newHexStr = [NSString stringWithFormat:@"%x",bytes[i]&0xff];///16进制数 189 | if([newHexStr length]==1){ 190 | hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr]; 191 | }else{ 192 | hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr]; 193 | } 194 | } 195 | return hexStr; 196 | } 197 | 198 | #pragma mark - NSString转NSData 199 | - (NSMutableData *)convertHexStrToData:(NSString *)str 200 | { 201 | if (!str || [str length] == 0) { 202 | return nil; 203 | } 204 | 205 | NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:8]; 206 | NSRange range; 207 | if ([str length] %2 == 0) { 208 | range = NSMakeRange(0,2); 209 | } else { 210 | range = NSMakeRange(0,1); 211 | } 212 | for (NSInteger i = range.location; i < [str length]; i += 2) { 213 | unsigned int anInt; 214 | NSString *hexCharStr = [str substringWithRange:range]; 215 | NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr]; 216 | 217 | [scanner scanHexInt:&anInt]; 218 | NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1]; 219 | [hexData appendData:entity]; 220 | 221 | range.location += range.length; 222 | range.length = 2; 223 | } 224 | 225 | return hexData; 226 | } 227 | 228 | #pragma mark --- 自定义error 229 | -(NSError *)returnErrorWithDomain:(NSString *)domain Code:(int)code ErrorStr:(NSString *)errorStr 230 | { 231 | NSString *desc = NSLocalizedString(errorStr, @""); 232 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; 233 | NSError *error = [NSError errorWithDomain:domain 234 | code:code 235 | userInfo:userInfo]; 236 | return error; 237 | } 238 | 239 | @end 240 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/FZBluetooth/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 FuZheng 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 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/FZBluetooth/README.md: -------------------------------------------------------------------------------- 1 | # FZBluetooth 2 | iOS蓝牙原生封装,助力智能硬件开发 3 | 4 | ![](https://github.com/fuzheng0301/FZBluetooth/blob/master/FZBluetooth%E5%B0%81%E8%A3%85%E7%B1%BB%E5%92%8CDemo/A62BABB1B55584D69200FCDA02F4B02F.png) 5 | 6 | 最近公司在做硬件设备,作为一名iOS开发人员,主要负责手机软件、硬件的连接方面,开发连接硬件使用的SDK,其中主要模块是蓝牙连接,通过蓝牙与硬件设备连接,发送指令使硬件工作。 7 | 功能说起来很简单,但是寻找了好几天的蓝牙方面的Demo,看到了很多前人大神们封装的Bluetooth方法,感觉对于我等小白实在是有点深奥,方法繁多,不知从何处下手。所以最后考虑再三,还是从底层基础入手,自己重新整理、封装了一份蓝牙的查找、连接、写入、断开的类,本着程序员的开源精神,分享出来,欢迎大家指正。 8 | 9 | # 使用 Cocoapods 导入 10 | FZBluetooth is available on [CocoaPods](http://cocoapods.org). Add the following to your Podfile: 11 | 12 | ```ruby 13 | pod "FZBluetooth","~>1.0.0" 14 | ``` 15 | 16 | # 目录 17 | 1. [系统蓝牙状态监听](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#系统蓝牙状态监听) 18 | 2. [蓝牙搜索](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#蓝牙搜索) 19 | 3. [蓝牙设备的连接](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#蓝牙设备的连接) 20 | 4. [设备的自动连接设置](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#设备的自动连接设置) 21 | 5. [写入数据](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#写入数据) 22 | 6. [蓝牙的断开](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#蓝牙的断开) 23 | 7. [其他](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#其他) 24 | 25 | # 方法说明简介 26 | 27 | ## 系统蓝牙状态监听 28 | 这个很方便,在系统蓝牙方法centralManagerDidUpdateState中就可以实时获取到蓝牙状态的改变,所以用一个Block回调就可以得到状态,根据状态变化做对应操作即可。 29 | 30 | 方法代码如下: 31 | ``` 32 | /** 33 | 34 | 系统当前蓝牙的状态 35 | 36 | @paramstateBlock 实时返回当前蓝牙状态 37 | 38 | */ 39 | 40 | - (void)returnBluetoothStateWithBlock:(FZStateUpdateBlock)stateBlock; 41 | ``` 42 | 43 | ## 蓝牙搜索 44 | 蓝牙搜索的功能方法中,用系统原生的方法scanForPeripheralsWithServices:options:,在蓝牙搜索的代理方法centralManager:didDiscoverPeripheral:advertisementData:RSSI:里获取搜索结果,用Block返回搜索结果。另外添加了方法通过设置参数nameStr来筛选返回的设备名称,nameStr为设备模糊搜索参数,设备中包含nameStr字段即可返回搜索结果。 45 | 46 | 封装后的代码调用方法如下: 47 | ``` 48 | /* 49 | 50 | * 开始搜索蓝牙外设,每次在block中返回一个蓝牙外设信息 51 | 52 | *@paramnameStr  模糊搜索设备名称,目标设备名称包含字段 53 | 54 | *  返回的block参数可参考CBCentralManager 的centralManager:didDiscoverPeripheral:advertisementData:RSSI: 55 | 56 | *@paramdiscoverBlock 搜索到蓝牙外设后的回调 57 | 58 | */ 59 | 60 | - (void)scanForPeripheralsWithPrefixName:(NSString *)nameStr discoverPeripheral:(FZDiscoverPeripheralBlock)discoverBlock; 61 | ``` 62 | 63 | ## 蓝牙设备的连接 64 | 蓝牙的连接为系统方法connectPeripheral:options:,连接设备的结果分别通过代理方法centralManager:didConnectPeripheral:返回成功、centralManager:didFailToConnectPeripheral:error:返回失败,通过两个Block分别返回成功和失败。其中成功后要停止搜索stopScan,失败要返回失败原因。 65 | 66 | 代码方法如下: 67 | ``` 68 | /* 69 | 70 | *  连接某个蓝牙外设,并查询服务,特性,特性描述 71 | 72 | *@paramperipheral          要连接的蓝牙外设 73 | 74 | *@paramcompletionBlock    操作执行完的回调 75 | 76 | */ 77 | 78 | - (void)connectPeripheral:(CBPeripheral *)peripheral completeBlock:(FZConnectSuccessBlock)completionBlock failBlock:(FZConnectFailBlock)failBlock; 79 | ``` 80 | 81 | ## 设备的自动连接设置 82 | 设备的自动连接,这里我写了两种方法,大家可以根据自己喜好自由选择。 83 | ### 1.根据设备peripheral自动连接 84 | 方法代码如下: 85 | ``` 86 | /** 87 | 88 | 设置或删除自动连接设备 89 | 90 | 设置后,在代理方法connectionWithPerpheral:里会返回设备的peripheral 91 | 92 | @param setOrDel 自动连接和删除自动连接 93 | 94 | @param peripheral 设备peripheral 95 | 96 | */ 97 | 98 | -(void)createAutomaticConnectionEquipmenWithSetOrDelate:(AutomaticConnectionEquipmenEnum)setOrDel Peripheral:(CBPeripheral *)peripheral; 99 | ``` 100 | 这个方法中setOrDel有两个枚举值,分别为:SetAutomaticConnectionEquipmen(设置自动重连)和DelateAutomaticConnectionEquipmen(删除自动重连)。 101 | 102 | **使用方法:** 103 | 104 | 设置自动重连设备后,这里传入的peripheral会自动保存,服从代理:FZAutomaticConnectionDelegate,在代理方法 105 | ``` 106 | /** 107 | 108 | 自动连接的设备代理方法 109 | 110 | */ 111 | 112 | - (void)connectionWithPerpheral:(CBPeripheral *)peripheral; 113 | ``` 114 | 中可以获取到重连的设备peripheral,随后进行连接操作即可。 115 | 116 | ### 2.通过设备UUID自动连接 117 | 代码方法如下: 118 | ``` 119 | /** 120 | 121 | 通过UUID获取peripheral 122 | 123 | 用户自主记录想要自动连接的UUID,获取peripheral后调用连接方法 124 | 125 | @param UUIDString UUID 126 | 127 | @return peripheral 128 | 129 | */ 130 | 131 | - (CBPeripheral *)retrievePeripheralWithUUIDString:(NSString *)UUIDString; 132 | ``` 133 | **使用方法:** 134 | 获取到设备的UUID后,通过此方法得到设备的peripheral,然后调用连接设备的方法即可自动重新连接。 135 | 136 | ## 写入数据 137 | 写入数据,在大多数的第三方方法里会有UUID、characteristic、peripheral等很多参数,混乱不易理解。这里我封装后只留了一个characteristic特性参数,而且已经帮大家筛选出来了,可以在封装方法头文件里,连接设备成功后直接获取到。另一方面,写入内容直接用NSString类型就可以,内部会自动转成NSData格式写入设备。 138 | 139 | 写入数据原生方法为writeValue:forCharacteristic:type:,写入数据后会在代理方法peripheral:didWriteValueForCharacteristic:error:方法里得到是否写入成功,成功与否用Block返回了结果。另外,如果蓝牙设备有应答的时候,会在peripheral:didUpdateValueForCharacteristic:error:方法里返回,处理起来比较麻烦,我下面封装了两种方法,一种通过Block异步返回结果,一种为同步返回应答结果,大家可以根据需要自由选择。 140 | 141 | ### 1.异步Block方式返回结果 142 | 代码封装后的接口为: 143 | ``` 144 | /* 145 | 146 | *  往某个特性中写入数据 147 | 148 | *@paramcharacteristic 特性对象 149 | 150 | *@paramcompletionBlock 写入完成后的回调,只有type为CBCharacteristicWriteWithResponse时,才会回调 151 | 152 | */ 153 | 154 | - (void)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic completionBlock:(FZWriteToCharacteristicBlock)completionBlock returnBlock:(FZEquipmentReturnBlock)equipmentBlock; 155 | ``` 156 | ### 2.同步返回结果 157 | 接口方法为: 158 | ``` 159 | /** 160 | 161 | * 往某个特性中写入数据,同步返回结果  162 | 163 | * @param dataStr 写入的数据 164 | 165 | * @param characteristic 特性对象 166 | 167 | * @return 设备应答结果 returnStr 应答内容 error 错误信息 168 | 169 | */ 170 | 171 | -(NSDictionary *)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic; 172 | ``` 173 | 同步返回的字典中有两个参数:returnStr和error,returnStr为设备应答数据,error为错误信息,判断error为nil时为成功,取returnStr进行下一步操作即可。 174 | 175 | **这里需要注意的是:实际开发中,可以用一个叫lightBlue的蓝牙开发辅助APP,看一下设备有多少特征值,我们实际用的时候需要用哪个,这个可以直接询问硬件厂商或硬件开发人员,然后在调用写入方法前,设置封装类中的属性UUIDString的对应值,可以保证连接过程中稳定不出问题** 176 | 177 | ## 蓝牙的断开 178 | 蓝牙的断开,只留了一个方法,断开当前连接的设备,使用系统原生方法cancelPeripheralConnection:,设备的信息在连接时已自动记录,所以不需要传入参数 179 | 180 | 代码封装后的方法如下: 181 | ``` 182 | /* 183 | 184 | * 断开蓝牙连接 185 | 186 | */ 187 | 188 | - (void)readRSSICompletionBlock:(FZGetRSSIBlock)getRSSIBlock; 189 | ``` 190 | 191 | ## 其他 192 | 其他的方法,头文件里开放了”RSSI转距离Double类型数据”、”NSData转16进制字符串”、”NSString类型转NSData类型数据”三个方法。 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FZBluetooth (1.0.2) 3 | 4 | DEPENDENCIES: 5 | - FZBluetooth (~> 1.0.2) 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - FZBluetooth 10 | 11 | SPEC CHECKSUMS: 12 | FZBluetooth: 11ca2e44d24c27533402b30eba58b440afbe82cb 13 | 14 | PODFILE CHECKSUM: aaed65e0c86c6a73c0d4778f6eec5013684282b1 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Pods.xcodeproj/xcuserdata/fzh.xcuserdatad/xcschemes/FZBluetooth.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Pods.xcodeproj/xcuserdata/fzh.xcuserdatad/xcschemes/Pods-FzBluetoothDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Pods.xcodeproj/xcuserdata/fzh.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | FZBluetooth.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-FzBluetoothDemo.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/FZBluetooth/FZBluetooth-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_FZBluetooth : NSObject 3 | @end 4 | @implementation PodsDummy_FZBluetooth 5 | @end 6 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/FZBluetooth/FZBluetooth-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/FZBluetooth/FZBluetooth-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "FzhBluetooth.h" 14 | #import "FzhCallBlock.h" 15 | #import "FzhString.h" 16 | 17 | FOUNDATION_EXPORT double FZBluetoothVersionNumber; 18 | FOUNDATION_EXPORT const unsigned char FZBluetoothVersionString[]; 19 | 20 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/FZBluetooth/FZBluetooth.modulemap: -------------------------------------------------------------------------------- 1 | framework module FZBluetooth { 2 | umbrella header "FZBluetooth-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/FZBluetooth/FZBluetooth.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FZBluetooth 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = -framework "CoreBluetooth" -framework "Foundation" -framework "UIKit" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/FZBluetooth 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/FZBluetooth/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## FZBluetooth 5 | 6 | MIT License 7 | 8 | Copyright (c) 2018 FuZheng 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2018 FuZheng 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | FZBluetooth 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_FzBluetoothDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_FzBluetoothDemo 5 | @end 6 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | 145 | if [[ "$CONFIGURATION" == "Debug" ]]; then 146 | install_framework "${BUILT_PRODUCTS_DIR}/FZBluetooth/FZBluetooth.framework" 147 | fi 148 | if [[ "$CONFIGURATION" == "Release" ]]; then 149 | install_framework "${BUILT_PRODUCTS_DIR}/FZBluetooth/FZBluetooth.framework" 150 | fi 151 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 152 | wait 153 | fi 154 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_FzBluetoothDemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_FzBluetoothDemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FZBluetooth" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FZBluetooth/FZBluetooth.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "FZBluetooth" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_FzBluetoothDemo { 2 | umbrella header "Pods-FzBluetoothDemo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /FZBluetoothExample/Pods/Target Support Files/Pods-FzBluetoothDemo/Pods-FzBluetoothDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FZBluetooth" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FZBluetooth/FZBluetooth.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "FZBluetooth" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /FZBluetoothExample/封装类/FzhBluetooth.h: -------------------------------------------------------------------------------- 1 | // 2 | // FzhBluetooth.h 3 | // BlueTooth 4 | // 5 | // Created by 付正 on 2017/9/14. 6 | // Copyright © 2017年 BFMobile. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "FzhCallBlock.h" 12 | 13 | typedef enum : NSUInteger { 14 | SetAutomaticConnectionEquipmen = 0, //设置自动连接的设备 15 | DelateAutomaticConnectionEquipmen, //删除自动连接的设备 16 | } AutomaticConnectionEquipmenEnum; 17 | 18 | @protocol FZAutomaticConnectionDelegate 19 | 20 | /** 21 | 自动连接的设备代理方法 22 | */ 23 | @optional - (void)connectionWithPerpheral:(CBPeripheral *)peripheral; 24 | 25 | @end 26 | 27 | @interface FzhBluetooth : NSObject 28 | 29 | @property (assign,nonatomic) AutomaticConnectionEquipmenEnum connectionEquipment; 30 | @property (strong, nonatomic) id delegate; 31 | @property (copy, nonatomic) FZStateUpdateBlock stateUpdateBlock; 32 | /** 发现一个蓝牙外设的回调 */ 33 | @property (copy, nonatomic) FZDiscoverPeripheralBlock discoverPeripheralBlcok; 34 | /** 连接外设完成的回调 */ 35 | @property (copy, nonatomic) FZConnectSuccessBlock connectSuccessBlock; 36 | /** 连接外设失败的回调 */ 37 | @property (copy, nonatomic) FZConnectFailBlock connectFailBlock; 38 | /** 获取蓝牙外设信号强度的回调 */ 39 | @property (copy, nonatomic) FZGetRSSIBlock getRSSIBlock; 40 | /** 将数据写入特性中的回调 */ 41 | @property (copy, nonatomic) FZWriteToCharacteristicBlock writeToCharacteristicBlock; 42 | @property (copy, nonatomic) FZEquipmentReturnBlock equipmentReturnBlock; 43 | 44 | @property (nonatomic, strong) CBCharacteristic * writeCharacteristic;//可写入参数,蓝牙连接成功,2秒后获得参数值 45 | @property (nonatomic, strong) CBCharacteristic * notifyCharacteristic;//可通知参数,蓝牙连接成功,2秒后获得参数值 46 | 47 | +(instancetype)shareInstance; 48 | 49 | /** 50 | 设置设备特性UUID,如果不设置默认为空 51 | */ 52 | @property (nonatomic, strong) NSString *UUIDString; 53 | 54 | /** 55 | * 连接设备后获取到的所有Service 56 | */ 57 | @property (nonatomic, strong) NSMutableArray *serviceArr; 58 | 59 | /** 60 | 系统当前蓝牙的状态 61 | 62 | @param stateBlock 实时返回当前蓝牙状态 63 | */ 64 | - (void)returnBluetoothStateWithBlock:(FZStateUpdateBlock)stateBlock; 65 | 66 | /** 67 | * 开始搜索蓝牙外设,每次在block中返回一个蓝牙外设信息 68 | * 69 | * @param nameStr 模糊搜索设备名称,目标设备名称包含字段 70 | * 返回的block参数可参考CBCentralManager 的 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 71 | * 72 | * @param discoverBlock 搜索到蓝牙外设后的回调 73 | */ 74 | - (void)scanForPeripheralsWithPrefixName:(NSString *)nameStr discoverPeripheral:(FZDiscoverPeripheralBlock)discoverBlock; 75 | 76 | /** 77 | * 连接某个蓝牙外设,并查询服务,特性,特性描述 78 | * 79 | * @param peripheral 要连接的蓝牙外设 80 | * @param completionBlock 操作执行完的回调 81 | */ 82 | - (void)connectPeripheral:(CBPeripheral *)peripheral 83 | completeBlock:(FZConnectSuccessBlock)completionBlock 84 | failBlock:(FZConnectFailBlock)failBlock; 85 | 86 | /** 87 | * 获取某外设的距离 88 | * 89 | * @param getRSSIBlock 获取信号完成后的回调 90 | */ 91 | - (void)readRSSICompletionBlock:(FZGetRSSIBlock)getRSSIBlock; 92 | 93 | /** 94 | RSSI转距离number 95 | 96 | @param RSSI RSSI 97 | @return 距离 98 | */ 99 | - (double)fzRssiToNumber:(NSNumber *)RSSI; 100 | 101 | /** 102 | * 往某个特性中写入数据,自动识别数据长度超过限制分段传输 103 | * 104 | * @param dataStr 写入的数据 105 | * @param characteristic 特性对象 106 | * @param completionBlock 写入完成后的回调,只有type为CBCharacteristicWriteWithResponse时,才会回调 107 | */ 108 | - (void)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic completionBlock:(FZWriteToCharacteristicBlock)completionBlock returnBlock:(FZEquipmentReturnBlock)equipmentBlock; 109 | 110 | /** 111 | * 往某个特性中写入数据,同步返回结果 112 | * 113 | * @param dataStr 写入的数据 114 | * @param characteristic 特性对象 115 | * @return 设备应答结果 returnStr 应答内容 error 错误信息 116 | */ 117 | -(NSDictionary *)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic; 118 | 119 | /** 120 | * 停止扫描 121 | */ 122 | - (void)stopScan; 123 | 124 | /** 125 | * 断开蓝牙连接 126 | */ 127 | - (void)cancelPeripheralConnection; 128 | 129 | /** 130 | 设置或删除自动连接设备 131 | 设置后,在代理方法connectionWithPerpheral:里会返回设备的peripheral 132 | 133 | @param setOrDel 自动连接和删除自动连接 134 | @param peripheral 设备peripheral 135 | */ 136 | -(void)createAutomaticConnectionEquipmenWithSetOrDelate:(AutomaticConnectionEquipmenEnum)setOrDel Peripheral:(CBPeripheral *)peripheral; 137 | 138 | /** 139 | 通过UUID获取peripheral 140 | 用户自主记录想要自动连接的UUID,获取peripheral后调用连接方法 141 | 142 | @param UUIDString UUID 143 | @return peripheral 144 | */ 145 | - (CBPeripheral *)retrievePeripheralWithUUIDString:(NSString *)UUIDString; 146 | 147 | /** 148 | 重新设置特征值 149 | 150 | @param peripheral peripheral 151 | @param uuidString uuidString 152 | */ 153 | -(void)createCharacticWithPeripheral:(CBPeripheral *)peripheral UUIDString:(NSString *)uuidString; 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /FZBluetoothExample/封装类/FzhBluetooth.m: -------------------------------------------------------------------------------- 1 | // 2 | // FzhBluetooth.m 3 | // BlueTooth 4 | // 5 | // Created by 付正 on 2017/9/14. 6 | // Copyright © 2017年 BFMobile. All rights reserved. 7 | // 8 | 9 | #import "FzhBluetooth.h" 10 | #import "FzhString.h" 11 | 12 | @implementation FzhBluetooth 13 | { 14 | CBCentralManager *fzhCentralManager; 15 | CBPeripheral *fzhPeripheral; 16 | CBCharacteristic *fzgCharacteristic; 17 | 18 | NSString *prefixString; //记录设备名称包含字符串 19 | NSString *resultStr; //记录应答数据 20 | 21 | NSInteger writeCount; /**< 写入次数 */ 22 | NSInteger responseCount; /**< 返回次数 */ 23 | } 24 | 25 | +(instancetype)shareInstance 26 | { 27 | static FzhBluetooth *share = nil; 28 | static dispatch_once_t oneToken; 29 | dispatch_once(&oneToken, ^{ 30 | share = [[FzhBluetooth alloc]init]; 31 | }); 32 | return share; 33 | } 34 | 35 | - (instancetype)init 36 | { 37 | self = [super init]; 38 | if (self) { 39 | fzhCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; 40 | resultStr = @""; 41 | self.serviceArr = [[NSMutableArray alloc]init]; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)dealloc 47 | { 48 | [self stopScan]; 49 | fzhCentralManager.delegate = nil; 50 | fzhPeripheral.delegate = nil; 51 | } 52 | 53 | #pragma mark --- 系统当前蓝牙的状态 54 | - (void)returnBluetoothStateWithBlock:(FZStateUpdateBlock)stateBlock 55 | { 56 | self.stateUpdateBlock = stateBlock; 57 | } 58 | 59 | //检查App的设备BLE是否可用 (ensure that Bluetooth low energy is supported and available to use on the central device) 60 | - (void)centralManagerDidUpdateState:(CBCentralManager *)central 61 | { 62 | if (_stateUpdateBlock) { 63 | _stateUpdateBlock(central.state); 64 | } 65 | switch (central.state) 66 | { 67 | case CBCentralManagerStatePoweredOn: 68 | //discover what peripheral devices are available for your app to connect to 69 | //第一个参数为CBUUID的数组,需要搜索特点服务的蓝牙设备,只要每搜索到一个符合条件的蓝牙设备都会调用didDiscoverPeripheral代理方法 70 | [fzhCentralManager scanForPeripheralsWithServices:nil options:nil]; 71 | break; 72 | default: 73 | NSLog(@"Central Manager did change state"); 74 | break; 75 | } 76 | } 77 | 78 | #pragma mark --- 搜索蓝牙设备 79 | - (void)scanForPeripheralsWithPrefixName:(NSString *)nameStr discoverPeripheral:(FZDiscoverPeripheralBlock)discoverBlock; 80 | { 81 | prefixString = nameStr; 82 | [self start]; 83 | self.discoverPeripheralBlcok = discoverBlock; 84 | } 85 | 86 | - (void)start 87 | { 88 | [fzhCentralManager scanForPeripheralsWithServices:nil options:nil]; 89 | } 90 | 91 | #pragma mark --- 连接蓝牙 92 | - (void)connectPeripheral:(CBPeripheral *)peripheral 93 | completeBlock:(FZConnectSuccessBlock)completionBlock 94 | failBlock:(FZConnectFailBlock)failBlock 95 | { 96 | fzhPeripheral = peripheral; 97 | 98 | //连接设备 99 | [fzhCentralManager connectPeripheral:peripheral options:nil]; 100 | //4.设置代理 101 | // fzhPeripheralManager.delegate = self; 102 | 103 | self.connectSuccessBlock = completionBlock; 104 | self.connectFailBlock = failBlock; 105 | 106 | } 107 | 108 | //搜索蓝牙代理方法 109 | - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI 110 | { 111 | // 112 | NSString *perName = [[NSUserDefaults standardUserDefaults] objectForKey:@"conPeripheral"]; 113 | NSString *setOrDelNum = [[NSUserDefaults standardUserDefaults] objectForKey:@"setOrDel"]; 114 | 115 | if (_discoverPeripheralBlcok) { 116 | if (prefixString.length > 0) { 117 | if ([peripheral.name hasPrefix:prefixString]) { 118 | _discoverPeripheralBlcok(central,peripheral,advertisementData,RSSI); 119 | } 120 | } else { 121 | _discoverPeripheralBlcok(central,peripheral,advertisementData,RSSI); 122 | } 123 | [self start]; 124 | } 125 | if ([setOrDelNum isEqualToString:@"0"]) { 126 | //有自动重连的设备 127 | [[NSNotificationCenter defaultCenter]postNotificationName:PostAutoConnectionNotificaiton object:nil]; 128 | if ([peripheral.name isEqualToString:perName]) { 129 | fzhPeripheral = peripheral; 130 | [fzhCentralManager connectPeripheral:peripheral options:nil]; 131 | } 132 | } 133 | } 134 | 135 | // 蓝牙设备连接成功 136 | - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral 137 | { 138 | [fzhCentralManager stopScan]; 139 | //Before you begin interacting with the peripheral, you should set the peripheral’s delegate to ensure that it receives the appropriate callbacks(设置代理) 140 | [fzhPeripheral setDelegate:self]; 141 | //discover all of the services that a peripheral offers,搜索服务,回调didDiscoverServices 142 | [fzhPeripheral discoverServices:nil]; 143 | 144 | //延时操作 145 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 146 | if (_connectSuccessBlock) { 147 | _connectSuccessBlock(peripheral,nil,self.writeCharacteristic); 148 | } else { 149 | //返回连接成功 150 | if ([self.delegate respondsToSelector:@selector(connectionWithPerpheral:)]) { 151 | [self.delegate connectionWithPerpheral:peripheral]; 152 | } 153 | } 154 | }); 155 | } 156 | 157 | // 连接失败,就会得到回调: 158 | - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error 159 | { 160 | if (_connectFailBlock) { 161 | _connectFailBlock(peripheral,error); 162 | } 163 | } 164 | 165 | #pragma mark --- 获取外设的信号强度 166 | - (void)readRSSICompletionBlock:(FZGetRSSIBlock)getRSSIBlock 167 | { 168 | self.getRSSIBlock = getRSSIBlock; 169 | [fzhPeripheral readRSSI]; 170 | } 171 | 172 | #pragma mark --- 写入数据 173 | - (void)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic completionBlock:(FZWriteToCharacteristicBlock)completionBlock returnBlock:(FZEquipmentReturnBlock)equipmentBlock 174 | { 175 | writeCount = 0; 176 | responseCount = 0; 177 | 178 | NSMutableArray *sendArr = [[NSMutableArray alloc]init]; 179 | if (dataStr.length > 40) { 180 | int count = dataStr.length / 40 + 1; 181 | for (int i = 0; i < count; i ++) { 182 | if (i < count-1) { 183 | [sendArr addObject:[dataStr substringWithRange:NSMakeRange(0+i*40, 40)]]; 184 | } else { 185 | [sendArr addObject:[dataStr substringFromIndex:i*40]]; 186 | } 187 | } 188 | for (NSString *sendStr in sendArr) { 189 | NSData *data = [[FzhString sharedInstance] convertHexStrToData:sendStr]; 190 | _writeToCharacteristicBlock = completionBlock; 191 | _equipmentReturnBlock = equipmentBlock; 192 | if (fzhPeripheral == nil) { 193 | NSString *desc = NSLocalizedString(@"Not connected devices", @""); 194 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; 195 | NSError *error = [NSError errorWithDomain:@"com.okey.wearkit.ErrorDomain" 196 | code:-101 197 | userInfo:userInfo]; 198 | _writeToCharacteristicBlock(nil,error); 199 | return; 200 | } 201 | [fzhPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; 202 | writeCount ++; 203 | } 204 | return; 205 | } 206 | NSData *data = [[FzhString sharedInstance] convertHexStrToData:dataStr]; 207 | _writeToCharacteristicBlock = completionBlock; 208 | _equipmentReturnBlock = equipmentBlock; 209 | if (fzhPeripheral == nil) { 210 | NSString *desc = NSLocalizedString(@"Not connected devices", @""); 211 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; 212 | NSError *error = [NSError errorWithDomain:@"com.okey.wearkit.ErrorDomain" 213 | code:-101 214 | userInfo:userInfo]; 215 | _writeToCharacteristicBlock(nil,error); 216 | return; 217 | } 218 | [fzhPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; 219 | writeCount ++; 220 | } 221 | 222 | #pragma mark --- 写入数据 同步返回数据 223 | -(NSDictionary *)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic 224 | { 225 | __block NSString * str = nil; 226 | __block NSError *reError = nil; 227 | [self writeValue:dataStr forCharacteristic:characteristic completionBlock:^(CBCharacteristic *characteristic, NSError *error) { 228 | NSLog(@"发送成功"); 229 | reError = error; 230 | } returnBlock:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSString *returnStr, NSError *error) { 231 | str = returnStr; 232 | reError = error; 233 | }]; 234 | 235 | while (!str) { 236 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 237 | } 238 | 239 | NSMutableDictionary *dict = [[NSMutableDictionary alloc]init]; 240 | [dict setValue:reError forKey:@"error"]; 241 | [dict setValue:str forKey:@"returnStr"]; 242 | return dict; 243 | } 244 | 245 | //写数据后回调 246 | - (void)peripheral:(CBPeripheral *)peripheral 247 | didWriteValueForCharacteristic:(CBCharacteristic *)characteristic 248 | error:(NSError *)error 249 | { 250 | resultStr = @""; 251 | if (!_writeToCharacteristicBlock) { 252 | return; 253 | } 254 | responseCount ++; 255 | if (writeCount != responseCount) { 256 | return; 257 | } 258 | _writeToCharacteristicBlock(characteristic,error); 259 | } 260 | 261 | #pragma mark --- 停止扫描 262 | - (void)stopScan 263 | { 264 | [fzhCentralManager stopScan]; 265 | } 266 | 267 | #pragma mark --- 断开蓝牙连接 268 | - (void)cancelPeripheralConnection 269 | { 270 | if (fzhPeripheral) { 271 | [fzhCentralManager cancelPeripheralConnection:fzhPeripheral]; 272 | } 273 | } 274 | 275 | #pragma mark --- 设置或删除自动连接设备 276 | -(void)createAutomaticConnectionEquipmenWithSetOrDelate:(AutomaticConnectionEquipmenEnum)setOrDel Peripheral:(CBPeripheral *)peripheral 277 | { 278 | self.connectionEquipment = setOrDel; 279 | if (setOrDel == SetAutomaticConnectionEquipmen) { 280 | //设置自动连接设备 281 | [[NSUserDefaults standardUserDefaults] setObject:peripheral.name forKey:@"conPeripheral"]; 282 | [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%lu",(unsigned long)setOrDel] forKey:@"setOrDel"]; 283 | [[NSUserDefaults standardUserDefaults] synchronize]; 284 | } else if (setOrDel == DelateAutomaticConnectionEquipmen) { 285 | //删除自动连接设备 286 | [[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"conPeripheral"]; 287 | [[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"setOrDel"]; 288 | [[NSUserDefaults standardUserDefaults] synchronize]; 289 | } 290 | } 291 | 292 | #pragma mark --- 通过UUID获取peripheral 293 | - (CBPeripheral *)retrievePeripheralWithUUIDString:(NSString *)UUIDString 294 | { 295 | CBPeripheral *p = nil; 296 | @try { 297 | NSUUID *uuid = [[NSUUID alloc]initWithUUIDString:UUIDString]; 298 | p = [fzhCentralManager retrievePeripheralsWithIdentifiers:@[uuid]][0]; 299 | } @catch (NSException *exception) { 300 | NSLog(@">>> retrievePeripheralWithUUIDString error:%@",exception); 301 | } @finally { 302 | } 303 | return p; 304 | } 305 | 306 | #pragma mark --- 断开蓝牙连接走这里 307 | - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error 308 | { 309 | [self cancelPeripheralConnection]; 310 | if (error) 311 | { 312 | NSLog(@">>> didDisconnectPeripheral for %@ with error: %@", peripheral.name, [error localizedDescription]); 313 | } 314 | } 315 | 316 | //获取服务后的回调 317 | - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error 318 | { 319 | if (error) { 320 | NSLog(@"didDiscoverServices : %@", [error localizedDescription]); 321 | return; 322 | } 323 | 324 | //提取出所有特征 325 | self.serviceArr = [NSMutableArray arrayWithArray:peripheral.services]; 326 | 327 | for (CBService *s in peripheral.services) { 328 | [s.peripheral discoverCharacteristics:nil forService:s]; 329 | } 330 | } 331 | 332 | //获取特征后的回调 333 | - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error 334 | { 335 | if (error) { 336 | NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]); 337 | return; 338 | } 339 | 340 | #warning ---此处需要筛选自己需要的特征 341 | [self createCharacticWithPeripheral:peripheral Service:service]; 342 | } 343 | 344 | #pragma mark --- 重新设置特征值 345 | -(void)createCharacticWithPeripheral:(CBPeripheral *)peripheral UUIDString:(NSString *)uuidString 346 | { 347 | for (CBService *s in peripheral.services) { 348 | [s.peripheral discoverCharacteristics:nil forService:s]; 349 | 350 | if ([s.UUID isEqual: [CBUUID UUIDWithString:uuidString]]) { 351 | [self createCharacticWithPeripheral:peripheral Service:s]; 352 | } 353 | } 354 | } 355 | 356 | // 筛选特征 357 | -(void)createCharacticWithPeripheral:(CBPeripheral *)peripheral Service:(CBService *)service 358 | { 359 | if (self.UUIDString) { 360 | if (![service.UUID isEqual:[CBUUID UUIDWithString:self.UUIDString]]) { 361 | return; 362 | } 363 | } 364 | 365 | for (CBCharacteristic *c in service.characteristics) 366 | { 367 | CBCharacteristicProperties properties = c.properties; 368 | if (properties & CBCharacteristicPropertyWrite) { 369 | fzgCharacteristic = c; 370 | self.writeCharacteristic = c; 371 | }else if (properties & CBCharacteristicPropertyNotify) { 372 | self.notifyCharacteristic = c; 373 | [peripheral setNotifyValue:YES forCharacteristic:c]; 374 | }else if (properties & CBCharacteristicPropertyWriteWithoutResponse) { 375 | fzgCharacteristic = c; 376 | self.writeCharacteristic = c; 377 | }else if (properties & CBCharacteristicPropertyIndicate) { 378 | self.notifyCharacteristic = c; 379 | } 380 | } 381 | } 382 | 383 | //订阅的特征值有新的数据时回调 384 | - (void)peripheral:(CBPeripheral *)peripheral 385 | didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic 386 | error:(NSError *)error 387 | { 388 | if (error) { 389 | NSLog(@"Error changing notification state: %@", 390 | [error localizedDescription]); 391 | } 392 | [peripheral readValueForCharacteristic:characteristic]; 393 | } 394 | 395 | // 获取到特征的值时回调 396 | - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error 397 | { 398 | if (error) { 399 | //报错了 400 | NSLog(@"didUpdateValueForCharacteristic error : %@", error.localizedDescription); 401 | return; 402 | } 403 | if (!characteristic.value) { 404 | return; 405 | } 406 | if (resultStr.length <= 0 || ![resultStr isEqualToString:[[FzhString sharedInstance] fzHexStringFromData:characteristic.value]]) { 407 | resultStr = [[FzhString sharedInstance]fzHexStringFromData:characteristic.value]; 408 | if (_equipmentReturnBlock) { 409 | _equipmentReturnBlock(peripheral,characteristic,resultStr,error); 410 | } 411 | } 412 | } 413 | 414 | #pragma mark ---------------- 获取信号之后的回调 ------------------ 415 | # if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 416 | - (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error { 417 | if (_getRSSIBlock) { 418 | double number = [self fzRssiToNumber:peripheral.RSSI]; 419 | _getRSSIBlock(number,error); 420 | } 421 | } 422 | #else 423 | - (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error { 424 | if (_getRSSIBlock) { 425 | double number = [self fzRssiToNumber:RSSI]; 426 | _getRSSIBlock(number,error); 427 | } 428 | } 429 | #endif 430 | 431 | #pragma mark - RSSI转距离number 432 | - (double)fzRssiToNumber:(NSNumber *)RSSI 433 | { 434 | int tempRssi = [RSSI intValue]; 435 | int absRssi = abs(tempRssi); 436 | float power = (absRssi-75)/(10*2.0); 437 | double number = pow(10, power);//除0外,任何数的0次方等于1 438 | return number; 439 | } 440 | 441 | @end 442 | -------------------------------------------------------------------------------- /FZBluetoothExample/封装类/FzhCallBlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // FzhCallBlock.h 3 | // FzBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/21. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #ifndef FzhCallBlock_h 10 | #define FzhCallBlock_h 11 | 12 | #define PostAutoConnectionNotificaiton @"AutoConnectionNotificaiton" 13 | 14 | /** 蓝牙状态改变的block */ 15 | typedef void(^FZStateUpdateBlock)(CBManagerState *state); 16 | /** 发现一个蓝牙外设的block */ 17 | typedef void(^FZDiscoverPeripheralBlock)(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI); 18 | /** 连接完成的block*/ 19 | typedef void(^FZConnectSuccessBlock)(CBPeripheral *peripheral,CBService *service, CBCharacteristic *character); 20 | /** 连接失败的block*/ 21 | typedef void(^FZConnectFailBlock)(CBPeripheral *peripheral, NSError *error); 22 | /** 获取蓝牙外设信号的回调 */ 23 | typedef void(^FZGetRSSIBlock)(double number, NSError *error); 24 | /** 往特性中写入数据的回调 */ 25 | typedef void(^FZWriteToCharacteristicBlock)(CBCharacteristic *characteristic, NSError *error); 26 | /** 设备返回数据的回调 */ 27 | typedef void(^FZEquipmentReturnBlock)(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSString *returnStr, NSError *error); 28 | 29 | #endif /* FzhCallBlock_h */ 30 | -------------------------------------------------------------------------------- /FZBluetoothExample/封装类/FzhString.h: -------------------------------------------------------------------------------- 1 | // 2 | // FzhString.h 3 | // OkeyBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/20. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FzhString : NSObject 12 | 13 | +(FzhString *)sharedInstance; 14 | 15 | /** 16 | 异或运算 17 | 18 | @param pan 字符串1 19 | @param pinv 字符串2 20 | @return 返回异或结果 21 | */ 22 | - (NSString *)pinxCreator:(NSString *)pan withPinv:(NSString *)pinv; 23 | 24 | /** 25 | 16进制转10进制 26 | 27 | @param sixteenStr 16进制字符串 28 | @return 10进制字符串 29 | */ 30 | -(NSString *)sixteenChangeTenString:(NSString *)sixteenStr; 31 | 32 | /** 33 | 10进制转16进制 34 | 35 | @param decimal 10进制数字 36 | @return 16进制字符串 37 | */ 38 | - (NSString *)hexStringFromString:(NSInteger)decimal; 39 | 40 | /** 41 | @see 16进制字符串转2进制数据 42 | @param hex 16进制字符串 43 | @return 2进制数据 44 | */ 45 | -(NSData *)hex2data:(NSString *)hex; 46 | 47 | /** 48 | @see 16进制字符串转2进制字符串 49 | @param hex 16进制字符串 50 | @return 2进制字符串 51 | */ 52 | -(NSString *)getBinaryByhex:(NSString *)hex; 53 | 54 | /** 55 | @see 2进制字符串转16进制字符串 56 | @param binary 2进制字符串 57 | @return 16进制字符串 58 | */ 59 | -(NSString *)getHexadecimalWithBinary:(NSString *)binary; 60 | 61 | /** 62 | NSData转16进制NSString 63 | 64 | @param data data数据 65 | @return string数据 66 | */ 67 | - (NSString *)fzHexStringFromData:(NSData *)data; 68 | 69 | /** 70 | NSString转NSData 71 | 72 | @param str string数据 73 | @return data数据 74 | */ 75 | - (NSMutableData *)convertHexStrToData:(NSString *)str; 76 | 77 | /** 78 | 自定义error信息 79 | 80 | @param domain 标志字段(如:com.okey.wearkit.domain) 81 | @param code error编号 82 | @param errorStr error信息 83 | @return error 84 | */ 85 | -(NSError *)returnErrorWithDomain:(NSString *)domain Code:(int)code ErrorStr:(NSString *)errorStr; 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /FZBluetoothExample/封装类/FzhString.m: -------------------------------------------------------------------------------- 1 | // 2 | // FzhString.m 3 | // OkeyBluetoothDemo 4 | // 5 | // Created by 付正 on 2017/9/20. 6 | // Copyright © 2017年 付正. All rights reserved. 7 | // 8 | 9 | #import "FzhString.h" 10 | 11 | @implementation FzhString 12 | 13 | //创建一个单例 14 | static FzhString *dbManger=nil; 15 | +(FzhString *)sharedInstance 16 | { 17 | @synchronized(self){ 18 | if (nil==dbManger) { 19 | dbManger=[[FzhString alloc]init]; 20 | } 21 | return dbManger; 22 | } 23 | } 24 | 25 | #pragma mark --- 异或运算 26 | - (NSString *)pinxCreator:(NSString *)pan withPinv:(NSString *)pinv 27 | { 28 | if (pan.length != pinv.length) 29 | { 30 | return nil; 31 | } 32 | const char *panchar = [pan UTF8String]; 33 | const char *pinvchar = [pinv UTF8String]; 34 | NSString *temp = [[NSString alloc] init]; 35 | for (int i = 0; i < pan.length; i++) 36 | { 37 | int panValue = [self charToint:panchar[i]]; 38 | int pinvValue = [self charToint:pinvchar[i]]; 39 | 40 | temp = [temp stringByAppendingString:[NSString stringWithFormat:@"%X",panValue^pinvValue]]; 41 | } 42 | return temp; 43 | } 44 | - (int)charToint:(char)tempChar 45 | { 46 | if (tempChar >= '0' && tempChar <='9') { 47 | return tempChar - '0'; 48 | } else if (tempChar >= 'A' && tempChar <= 'F') { 49 | return tempChar - 'A' + 10; 50 | } 51 | return 0; 52 | } 53 | 54 | #pragma mark --- 2进制字符串转16进制字符串 55 | -(NSString *)getHexadecimalWithBinary:(NSString *)binary 56 | { 57 | NSMutableDictionary *hexDic = [[NSMutableDictionary alloc] init]; 58 | [hexDic setObject:@"0" forKey:@"0000"]; 59 | [hexDic setObject:@"1" forKey:@"0001"]; 60 | [hexDic setObject:@"2" forKey:@"0010"]; 61 | [hexDic setObject:@"3" forKey:@"0011"]; 62 | [hexDic setObject:@"4" forKey:@"0100"]; 63 | [hexDic setObject:@"5" forKey:@"0101"]; 64 | [hexDic setObject:@"6" forKey:@"0110"]; 65 | [hexDic setObject:@"7" forKey:@"0111"]; 66 | [hexDic setObject:@"8" forKey:@"1000"]; 67 | [hexDic setObject:@"9" forKey:@"1001"]; 68 | [hexDic setObject:@"a" forKey:@"1010"]; 69 | [hexDic setObject:@"b" forKey:@"1011"]; 70 | [hexDic setObject:@"c" forKey:@"1100"]; 71 | [hexDic setObject:@"d" forKey:@"1101"]; 72 | [hexDic setObject:@"e" forKey:@"1110"]; 73 | [hexDic setObject:@"f" forKey:@"1111"]; 74 | NSString *binaryString=@""; 75 | for (int i = 0; i < binary.length; i ++) { 76 | NSString *key = [binary substringWithRange:NSMakeRange(i, 4)]; 77 | binaryString = [binaryString stringByAppendingString:[hexDic objectForKey:key]]; 78 | i += 3; 79 | } 80 | 81 | return binaryString; 82 | } 83 | 84 | #pragma mark --- 16进制字符串转2进制字符串 85 | -(NSString *)getBinaryByhex:(NSString *)hex 86 | { 87 | NSMutableDictionary *hexDic = [[NSMutableDictionary alloc] init]; 88 | [hexDic setObject:@"0000" forKey:@"0"]; 89 | [hexDic setObject:@"0001" forKey:@"1"]; 90 | [hexDic setObject:@"0010" forKey:@"2"]; 91 | [hexDic setObject:@"0011" forKey:@"3"]; 92 | [hexDic setObject:@"0100" forKey:@"4"]; 93 | [hexDic setObject:@"0101" forKey:@"5"]; 94 | [hexDic setObject:@"0110" forKey:@"6"]; 95 | [hexDic setObject:@"0111" forKey:@"7"]; 96 | [hexDic setObject:@"1000" forKey:@"8"]; 97 | [hexDic setObject:@"1001" forKey:@"9"]; 98 | [hexDic setObject:@"1010" forKey:@"A"]; 99 | [hexDic setObject:@"1011" forKey:@"B"]; 100 | [hexDic setObject:@"1100" forKey:@"C"]; 101 | [hexDic setObject:@"1101" forKey:@"D"]; 102 | [hexDic setObject:@"1110" forKey:@"E"]; 103 | [hexDic setObject:@"1111" forKey:@"F"]; 104 | [hexDic setObject:@"1010" forKey:@"a"]; 105 | [hexDic setObject:@"1011" forKey:@"b"]; 106 | [hexDic setObject:@"1100" forKey:@"c"]; 107 | [hexDic setObject:@"1101" forKey:@"d"]; 108 | [hexDic setObject:@"1110" forKey:@"e"]; 109 | [hexDic setObject:@"1111" forKey:@"f"]; 110 | NSMutableString *binaryString=[[NSMutableString alloc] init]; 111 | for (int i=0; i<[hex length]; i++) { 112 | NSRange rage; 113 | rage.length = 1; 114 | rage.location = i; 115 | NSString *key = [hex substringWithRange:rage]; 116 | binaryString = [NSMutableString stringWithFormat:@"%@%@",binaryString,[NSString stringWithFormat:@"%@",[hexDic objectForKey:key]]]; 117 | } 118 | return binaryString; 119 | } 120 | 121 | #pragma mark --- 十进制准换为十六进制字符串 122 | - (NSString *)hexStringFromString:(NSInteger)decimal 123 | { 124 | NSString *hex =@""; 125 | NSString *letter; 126 | NSInteger number; 127 | for (int i = 0; i<9; i++) { 128 | number = decimal % 16; 129 | decimal = decimal / 16; 130 | switch (number) { 131 | case 10: 132 | letter =@"a"; break; 133 | case 11: 134 | letter =@"b"; break; 135 | case 12: 136 | letter =@"c"; break; 137 | case 13: 138 | letter =@"d"; break; 139 | case 14: 140 | letter =@"e"; break; 141 | case 15: 142 | letter =@"f"; break; 143 | default: 144 | letter = [NSString stringWithFormat:@"%ld", (long)number]; 145 | } 146 | hex = [letter stringByAppendingString:hex]; 147 | if (decimal == 0) { 148 | 149 | break; 150 | } 151 | } 152 | if (hex.length == 1) { 153 | hex = [NSString stringWithFormat:@"0%@",hex]; 154 | } 155 | return hex; 156 | } 157 | 158 | #pragma mark --- 16进制转10进制 159 | -(NSString *)sixteenChangeTenString:(NSString *)sixteenStr 160 | { 161 | NSString * temp10 = [NSString stringWithFormat:@"%lu",strtoul([sixteenStr UTF8String],0,16)]; 162 | return temp10; 163 | } 164 | 165 | #pragma mark --- 16进制转2进制数据 166 | -(NSData *)hex2data:(NSString *)hex 167 | { 168 | NSMutableData *data = [NSMutableData dataWithCapacity:hex.length / 2]; 169 | unsigned char whole_byte; 170 | char byte_chars[3] = {'\0','\0','\0'}; 171 | int i; 172 | for (i=0; i < hex.length / 2; i++) { 173 | byte_chars[0] = [hex characterAtIndex:i*2]; 174 | byte_chars[1] = [hex characterAtIndex:i*2+1]; 175 | whole_byte = strtol(byte_chars, NULL, 16); 176 | [data appendBytes:&whole_byte length:1]; 177 | } 178 | return data; 179 | } 180 | 181 | #pragma mark - NSData转16进制NSString 182 | - (NSString *)fzHexStringFromData:(NSData *)data 183 | { 184 | Byte *bytes = (Byte *)[data bytes]; 185 | //下面是Byte 转换为16进制 186 | NSString *hexStr=@""; 187 | for(int i=0;i<[data length];i++){ 188 | NSString *newHexStr = [NSString stringWithFormat:@"%x",bytes[i]&0xff];///16进制数 189 | if([newHexStr length]==1){ 190 | hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr]; 191 | }else{ 192 | hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr]; 193 | } 194 | } 195 | return hexStr; 196 | } 197 | 198 | #pragma mark - NSString转NSData 199 | - (NSMutableData *)convertHexStrToData:(NSString *)str 200 | { 201 | if (!str || [str length] == 0) { 202 | return nil; 203 | } 204 | 205 | NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:8]; 206 | NSRange range; 207 | if ([str length] %2 == 0) { 208 | range = NSMakeRange(0,2); 209 | } else { 210 | range = NSMakeRange(0,1); 211 | } 212 | for (NSInteger i = range.location; i < [str length]; i += 2) { 213 | unsigned int anInt; 214 | NSString *hexCharStr = [str substringWithRange:range]; 215 | NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr]; 216 | 217 | [scanner scanHexInt:&anInt]; 218 | NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1]; 219 | [hexData appendData:entity]; 220 | 221 | range.location += range.length; 222 | range.length = 2; 223 | } 224 | 225 | return hexData; 226 | } 227 | 228 | #pragma mark --- 自定义error 229 | -(NSError *)returnErrorWithDomain:(NSString *)domain Code:(int)code ErrorStr:(NSString *)errorStr 230 | { 231 | NSString *desc = NSLocalizedString(errorStr, @""); 232 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; 233 | NSError *error = [NSError errorWithDomain:domain 234 | code:code 235 | userInfo:userInfo]; 236 | return error; 237 | } 238 | 239 | @end 240 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 FuZheng 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FZBluetooth 2 | iOS蓝牙原生封装,助力智能硬件开发 3 | 4 | ![](https://github.com/fuzheng0301/FZBluetooth/blob/master/FZBluetooth%E5%B0%81%E8%A3%85%E7%B1%BB%E5%92%8CDemo/A62BABB1B55584D69200FCDA02F4B02F.png) 5 | 6 | 最近公司在做硬件设备,作为一名iOS开发人员,主要负责手机软件、硬件的连接方面,开发连接硬件使用的SDK,其中主要模块是蓝牙连接,通过蓝牙与硬件设备连接,发送指令使硬件工作。 7 | 功能说起来很简单,但是寻找了好几天的蓝牙方面的Demo,看到了很多前人大神们封装的Bluetooth方法,感觉对于我等小白实在是有点深奥,方法繁多,不知从何处下手。所以最后考虑再三,还是从底层基础入手,自己重新整理、封装了一份蓝牙的查找、连接、写入、断开的类,本着程序员的开源精神,分享出来,欢迎大家指正。 8 | 9 | # 使用 Cocoapods 导入 10 | FZBluetooth is available on [CocoaPods](http://cocoapods.org). Add the following to your Podfile: 11 | 12 | ```ruby 13 | pod "FZBluetooth","~>1.0.2" 14 | ``` 15 | 16 | # 目录 17 | 1. [系统蓝牙状态监听](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#系统蓝牙状态监听) 18 | 2. [蓝牙搜索](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#蓝牙搜索) 19 | 3. [蓝牙设备的连接](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#蓝牙设备的连接) 20 | 4. [设备的自动连接设置](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#设备的自动连接设置) 21 | 5. [写入数据](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#写入数据) 22 | 6. [蓝牙的断开](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#蓝牙的断开) 23 | 7. [其他](https://github.com/fuzheng0301/FZBluetooth/blob/master/README.md#其他) 24 | 25 | # 方法说明简介 26 | 27 | ## 系统蓝牙状态监听 28 | 这个很方便,在系统蓝牙方法centralManagerDidUpdateState中就可以实时获取到蓝牙状态的改变,所以用一个Block回调就可以得到状态,根据状态变化做对应操作即可。 29 | 30 | 方法代码如下: 31 | ``` 32 | /** 33 | 34 | 系统当前蓝牙的状态 35 | 36 | @paramstateBlock 实时返回当前蓝牙状态 37 | 38 | */ 39 | 40 | - (void)returnBluetoothStateWithBlock:(FZStateUpdateBlock)stateBlock; 41 | ``` 42 | 43 | ## 蓝牙搜索 44 | 蓝牙搜索的功能方法中,用系统原生的方法scanForPeripheralsWithServices:options:,在蓝牙搜索的代理方法centralManager:didDiscoverPeripheral:advertisementData:RSSI:里获取搜索结果,用Block返回搜索结果。另外添加了方法通过设置参数nameStr来筛选返回的设备名称,nameStr为设备模糊搜索参数,设备中包含nameStr字段即可返回搜索结果。 45 | 46 | 封装后的代码调用方法如下: 47 | ``` 48 | /* 49 | 50 | * 开始搜索蓝牙外设,每次在block中返回一个蓝牙外设信息 51 | 52 | *@paramnameStr  模糊搜索设备名称,目标设备名称包含字段 53 | 54 | *  返回的block参数可参考CBCentralManager 的centralManager:didDiscoverPeripheral:advertisementData:RSSI: 55 | 56 | *@paramdiscoverBlock 搜索到蓝牙外设后的回调 57 | 58 | */ 59 | 60 | - (void)scanForPeripheralsWithPrefixName:(NSString *)nameStr discoverPeripheral:(FZDiscoverPeripheralBlock)discoverBlock; 61 | ``` 62 | 63 | ## 蓝牙设备的连接 64 | 蓝牙的连接为系统方法connectPeripheral:options:,连接设备的结果分别通过代理方法centralManager:didConnectPeripheral:返回成功、centralManager:didFailToConnectPeripheral:error:返回失败,通过两个Block分别返回成功和失败。其中成功后要停止搜索stopScan,失败要返回失败原因。 65 | 66 | 代码方法如下: 67 | ``` 68 | /* 69 | 70 | *  连接某个蓝牙外设,并查询服务,特性,特性描述 71 | 72 | *@paramperipheral          要连接的蓝牙外设 73 | 74 | *@paramcompletionBlock    操作执行完的回调 75 | 76 | */ 77 | 78 | - (void)connectPeripheral:(CBPeripheral *)peripheral completeBlock:(FZConnectSuccessBlock)completionBlock failBlock:(FZConnectFailBlock)failBlock; 79 | ``` 80 | 81 | ## 设备的自动连接设置 82 | 设备的自动连接,这里我写了两种方法,大家可以根据自己喜好自由选择。 83 | ### 1.根据设备peripheral自动连接 84 | 方法代码如下: 85 | ``` 86 | /** 87 | 88 | 设置或删除自动连接设备 89 | 90 | 设置后,在代理方法connectionWithPerpheral:里会返回设备的peripheral 91 | 92 | @param setOrDel 自动连接和删除自动连接 93 | 94 | @param peripheral 设备peripheral 95 | 96 | */ 97 | 98 | -(void)createAutomaticConnectionEquipmenWithSetOrDelate:(AutomaticConnectionEquipmenEnum)setOrDel Peripheral:(CBPeripheral *)peripheral; 99 | ``` 100 | 这个方法中setOrDel有两个枚举值,分别为:SetAutomaticConnectionEquipmen(设置自动重连)和DelateAutomaticConnectionEquipmen(删除自动重连)。 101 | 102 | **使用方法:** 103 | 104 | 设置自动重连设备后,这里传入的peripheral会自动保存,服从代理:FZAutomaticConnectionDelegate,在代理方法 105 | ``` 106 | /** 107 | 108 | 自动连接的设备代理方法 109 | 110 | */ 111 | 112 | - (void)connectionWithPerpheral:(CBPeripheral *)peripheral; 113 | ``` 114 | 中可以获取到重连的设备peripheral,随后进行连接操作即可。 115 | 116 | ### 2.通过设备UUID自动连接 117 | 代码方法如下: 118 | ``` 119 | /** 120 | 121 | 通过UUID获取peripheral 122 | 123 | 用户自主记录想要自动连接的UUID,获取peripheral后调用连接方法 124 | 125 | @param UUIDString UUID 126 | 127 | @return peripheral 128 | 129 | */ 130 | 131 | - (CBPeripheral *)retrievePeripheralWithUUIDString:(NSString *)UUIDString; 132 | ``` 133 | **使用方法:** 134 | 获取到设备的UUID后,通过此方法得到设备的peripheral,然后调用连接设备的方法即可自动重新连接。 135 | 136 | ## 写入数据 137 | 写入数据,在大多数的第三方方法里会有UUID、characteristic、peripheral等很多参数,混乱不易理解。这里我封装后只留了一个characteristic特性参数,而且已经帮大家筛选出来了,可以在封装方法头文件里,连接设备成功后直接获取到。另一方面,写入内容直接用NSString类型就可以,内部会自动转成NSData格式写入设备。 138 | 139 | 写入数据原生方法为writeValue:forCharacteristic:type:,写入数据后会在代理方法peripheral:didWriteValueForCharacteristic:error:方法里得到是否写入成功,成功与否用Block返回了结果。另外,如果蓝牙设备有应答的时候,会在peripheral:didUpdateValueForCharacteristic:error:方法里返回,处理起来比较麻烦,我下面封装了两种方法,一种通过Block异步返回结果,一种为同步返回应答结果,大家可以根据需要自由选择。 140 | 141 | ### 1.异步Block方式返回结果 142 | 代码封装后的接口为: 143 | ``` 144 | /* 145 | 146 | *  往某个特性中写入数据 147 | 148 | *@paramcharacteristic 特性对象 149 | 150 | *@paramcompletionBlock 写入完成后的回调,只有type为CBCharacteristicWriteWithResponse时,才会回调 151 | 152 | */ 153 | 154 | - (void)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic completionBlock:(FZWriteToCharacteristicBlock)completionBlock returnBlock:(FZEquipmentReturnBlock)equipmentBlock; 155 | ``` 156 | ### 2.同步返回结果 157 | 接口方法为: 158 | ``` 159 | /** 160 | 161 | * 往某个特性中写入数据,同步返回结果  162 | 163 | * @param dataStr 写入的数据 164 | 165 | * @param characteristic 特性对象 166 | 167 | * @return 设备应答结果 returnStr 应答内容 error 错误信息 168 | 169 | */ 170 | 171 | -(NSDictionary *)writeValue:(NSString *)dataStr forCharacteristic:(CBCharacteristic *)characteristic; 172 | ``` 173 | 同步返回的字典中有两个参数:returnStr和error,returnStr为设备应答数据,error为错误信息,判断error为nil时为成功,取returnStr进行下一步操作即可。 174 | 175 | **这里需要注意的是:实际开发中,可以用一个叫lightBlue的蓝牙开发辅助APP,看一下设备有多少特征值,我们实际用的时候需要用哪个,这个可以直接询问硬件厂商或硬件开发人员,然后在调用写入方法前,设置封装类中的属性UUIDString的对应值,可以保证连接过程中稳定不出问题** 176 | 177 | ## 蓝牙的断开 178 | 蓝牙的断开,只留了一个方法,断开当前连接的设备,使用系统原生方法cancelPeripheralConnection:,设备的信息在连接时已自动记录,所以不需要传入参数 179 | 180 | 代码封装后的方法如下: 181 | ``` 182 | /* 183 | 184 | * 断开蓝牙连接 185 | 186 | */ 187 | 188 | - (void)readRSSICompletionBlock:(FZGetRSSIBlock)getRSSIBlock; 189 | ``` 190 | 191 | ## 其他 192 | 其他的方法,头文件里开放了”RSSI转距离Double类型数据”、”NSData转16进制字符串”、”NSString类型转NSData类型数据”三个方法。 193 | 194 | 195 | 196 | 197 | 198 | --------------------------------------------------------------------------------