├── .DS_Store ├── .gitignore ├── Demo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Demo ├── AddContactViewController.h ├── AddContactViewController.m ├── AddressBookViewController.h ├── AddressBookViewController.m ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── hand portrait.imageset │ │ ├── Contents.json │ │ ├── hand portrait@2x.png │ │ └── hand portrait@3x.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── ContactTableViewCell.h ├── ContactTableViewCell.m ├── Info.plist ├── Main.storyboard ├── SelectPhoneNumViewController.h ├── SelectPhoneNumViewController.m ├── SortAddressBookViewController.h ├── SortAddressBookViewController.m ├── ViewController.h ├── ViewController.m └── main.m ├── LICENSE ├── LJContactManager.podspec ├── LJContactManager ├── LJContactManager.h ├── LJContactManager.m ├── LJPeoplePickerDelegate.h ├── LJPeoplePickerDelegate.m ├── LJPerson.h ├── LJPerson.m ├── LJPickerDetailDelegate.h ├── LJPickerDetailDelegate.m ├── NSString+LJExtension.h └── NSString+LJExtension.m └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leejayID/LJContactManager/ad7cd248e6a15f3a8f6c80260781611323210173/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | index/ 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata/ 20 | 21 | ## Other 22 | *.moved-aside 23 | *.xcuserstate 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 52 | 53 | fastlane/report.xml 54 | fastlane/screenshots 55 | 56 | #Code Injection 57 | # 58 | # After new code Injection tools there's a generated folder /iOSInjectionProject 59 | # https://github.com/johnno1962/injectionforxcode 60 | 61 | iOSInjectionProject/ 62 | -------------------------------------------------------------------------------- /Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 449DDED620EDD08200669B84 /* NSString+LJExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 449DDED520EDD08200669B84 /* NSString+LJExtension.m */; }; 11 | B520F64E1E89232A008024CD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F64D1E89232A008024CD /* main.m */; }; 12 | B520F6511E89232A008024CD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F6501E89232A008024CD /* AppDelegate.m */; }; 13 | B520F6541E89232A008024CD /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F6531E89232A008024CD /* ViewController.m */; }; 14 | B520F6591E89232A008024CD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B520F6581E89232A008024CD /* Assets.xcassets */; }; 15 | B520F65C1E89232A008024CD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B520F65A1E89232A008024CD /* LaunchScreen.storyboard */; }; 16 | B520F6641E89236B008024CD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B520F6631E89236B008024CD /* Main.storyboard */; }; 17 | B520F6731E8923CB008024CD /* SelectPhoneNumViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F6701E8923CB008024CD /* SelectPhoneNumViewController.m */; }; 18 | B520F6741E8923CB008024CD /* SortAddressBookViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F6721E8923CB008024CD /* SortAddressBookViewController.m */; }; 19 | B520F6771E8923DB008024CD /* AddressBookViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F6761E8923DB008024CD /* AddressBookViewController.m */; }; 20 | B520F6841E892534008024CD /* AddContactViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F6831E892534008024CD /* AddContactViewController.m */; }; 21 | B520F6871E8925BB008024CD /* ContactTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B520F6861E8925BB008024CD /* ContactTableViewCell.m */; }; 22 | B545AC271E8A088700D59DF8 /* LJContactManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B545AC221E8A088700D59DF8 /* LJContactManager.m */; }; 23 | B545AC281E8A088700D59DF8 /* LJPeoplePickerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B545AC241E8A088700D59DF8 /* LJPeoplePickerDelegate.m */; }; 24 | B545AC291E8A088700D59DF8 /* LJPerson.m in Sources */ = {isa = PBXBuildFile; fileRef = B545AC261E8A088700D59DF8 /* LJPerson.m */; }; 25 | B5C4B0391EAD9A4F00AE5270 /* LJPickerDetailDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B5C4B0381EAD9A4F00AE5270 /* LJPickerDetailDelegate.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 449DDED420EDD08200669B84 /* NSString+LJExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+LJExtension.h"; sourceTree = ""; }; 30 | 449DDED520EDD08200669B84 /* NSString+LJExtension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+LJExtension.m"; sourceTree = ""; }; 31 | B520F6491E89232A008024CD /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | B520F64D1E89232A008024CD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 33 | B520F64F1E89232A008024CD /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 34 | B520F6501E89232A008024CD /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 35 | B520F6521E89232A008024CD /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 36 | B520F6531E89232A008024CD /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 37 | B520F6581E89232A008024CD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | B520F65B1E89232A008024CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 39 | B520F65D1E89232A008024CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | B520F6631E89236B008024CD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 41 | B520F66F1E8923CB008024CD /* SelectPhoneNumViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectPhoneNumViewController.h; sourceTree = ""; }; 42 | B520F6701E8923CB008024CD /* SelectPhoneNumViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SelectPhoneNumViewController.m; sourceTree = ""; }; 43 | B520F6711E8923CB008024CD /* SortAddressBookViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SortAddressBookViewController.h; sourceTree = ""; }; 44 | B520F6721E8923CB008024CD /* SortAddressBookViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SortAddressBookViewController.m; sourceTree = ""; }; 45 | B520F6751E8923DB008024CD /* AddressBookViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddressBookViewController.h; sourceTree = ""; }; 46 | B520F6761E8923DB008024CD /* AddressBookViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddressBookViewController.m; sourceTree = ""; }; 47 | B520F6821E892534008024CD /* AddContactViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddContactViewController.h; sourceTree = ""; }; 48 | B520F6831E892534008024CD /* AddContactViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddContactViewController.m; sourceTree = ""; }; 49 | B520F6851E8925BB008024CD /* ContactTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactTableViewCell.h; sourceTree = ""; }; 50 | B520F6861E8925BB008024CD /* ContactTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactTableViewCell.m; sourceTree = ""; }; 51 | B545AC211E8A088700D59DF8 /* LJContactManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LJContactManager.h; sourceTree = ""; }; 52 | B545AC221E8A088700D59DF8 /* LJContactManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LJContactManager.m; sourceTree = ""; }; 53 | B545AC231E8A088700D59DF8 /* LJPeoplePickerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LJPeoplePickerDelegate.h; sourceTree = ""; }; 54 | B545AC241E8A088700D59DF8 /* LJPeoplePickerDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LJPeoplePickerDelegate.m; sourceTree = ""; }; 55 | B545AC251E8A088700D59DF8 /* LJPerson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LJPerson.h; sourceTree = ""; }; 56 | B545AC261E8A088700D59DF8 /* LJPerson.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LJPerson.m; sourceTree = ""; }; 57 | B5C4B0371EAD9A4F00AE5270 /* LJPickerDetailDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LJPickerDetailDelegate.h; sourceTree = ""; }; 58 | B5C4B0381EAD9A4F00AE5270 /* LJPickerDetailDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LJPickerDetailDelegate.m; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | B520F6461E89232A008024CD /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | B520F6401E89232A008024CD = { 73 | isa = PBXGroup; 74 | children = ( 75 | B545AC201E8A088700D59DF8 /* LJContactManager */, 76 | B520F64B1E89232A008024CD /* Demo */, 77 | B520F64A1E89232A008024CD /* Products */, 78 | ); 79 | sourceTree = ""; 80 | }; 81 | B520F64A1E89232A008024CD /* Products */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | B520F6491E89232A008024CD /* Demo.app */, 85 | ); 86 | name = Products; 87 | sourceTree = ""; 88 | }; 89 | B520F64B1E89232A008024CD /* Demo */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | B520F64F1E89232A008024CD /* AppDelegate.h */, 93 | B520F6501E89232A008024CD /* AppDelegate.m */, 94 | B520F6521E89232A008024CD /* ViewController.h */, 95 | B520F6531E89232A008024CD /* ViewController.m */, 96 | B520F66F1E8923CB008024CD /* SelectPhoneNumViewController.h */, 97 | B520F6701E8923CB008024CD /* SelectPhoneNumViewController.m */, 98 | B520F6821E892534008024CD /* AddContactViewController.h */, 99 | B520F6831E892534008024CD /* AddContactViewController.m */, 100 | B520F6751E8923DB008024CD /* AddressBookViewController.h */, 101 | B520F6761E8923DB008024CD /* AddressBookViewController.m */, 102 | B520F6711E8923CB008024CD /* SortAddressBookViewController.h */, 103 | B520F6721E8923CB008024CD /* SortAddressBookViewController.m */, 104 | B520F6851E8925BB008024CD /* ContactTableViewCell.h */, 105 | B520F6861E8925BB008024CD /* ContactTableViewCell.m */, 106 | B520F6631E89236B008024CD /* Main.storyboard */, 107 | B520F6581E89232A008024CD /* Assets.xcassets */, 108 | B520F65A1E89232A008024CD /* LaunchScreen.storyboard */, 109 | B520F65D1E89232A008024CD /* Info.plist */, 110 | B520F64C1E89232A008024CD /* Supporting Files */, 111 | ); 112 | path = Demo; 113 | sourceTree = ""; 114 | }; 115 | B520F64C1E89232A008024CD /* Supporting Files */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | B520F64D1E89232A008024CD /* main.m */, 119 | ); 120 | name = "Supporting Files"; 121 | sourceTree = ""; 122 | }; 123 | B545AC201E8A088700D59DF8 /* LJContactManager */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | B545AC211E8A088700D59DF8 /* LJContactManager.h */, 127 | B545AC221E8A088700D59DF8 /* LJContactManager.m */, 128 | B545AC231E8A088700D59DF8 /* LJPeoplePickerDelegate.h */, 129 | B545AC241E8A088700D59DF8 /* LJPeoplePickerDelegate.m */, 130 | B5C4B0371EAD9A4F00AE5270 /* LJPickerDetailDelegate.h */, 131 | B5C4B0381EAD9A4F00AE5270 /* LJPickerDetailDelegate.m */, 132 | B545AC251E8A088700D59DF8 /* LJPerson.h */, 133 | B545AC261E8A088700D59DF8 /* LJPerson.m */, 134 | 449DDED420EDD08200669B84 /* NSString+LJExtension.h */, 135 | 449DDED520EDD08200669B84 /* NSString+LJExtension.m */, 136 | ); 137 | path = LJContactManager; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | B520F6481E89232A008024CD /* Demo */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = B520F6601E89232A008024CD /* Build configuration list for PBXNativeTarget "Demo" */; 146 | buildPhases = ( 147 | B520F6451E89232A008024CD /* Sources */, 148 | B520F6461E89232A008024CD /* Frameworks */, 149 | B520F6471E89232A008024CD /* Resources */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = Demo; 156 | productName = Demo; 157 | productReference = B520F6491E89232A008024CD /* Demo.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | B520F6411E89232A008024CD /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | LastUpgradeCheck = 0940; 167 | ORGANIZATIONNAME = LeeJay; 168 | TargetAttributes = { 169 | B520F6481E89232A008024CD = { 170 | CreatedOnToolsVersion = 8.1; 171 | DevelopmentTeam = Q87QUX3533; 172 | ProvisioningStyle = Manual; 173 | }; 174 | }; 175 | }; 176 | buildConfigurationList = B520F6441E89232A008024CD /* Build configuration list for PBXProject "Demo" */; 177 | compatibilityVersion = "Xcode 3.2"; 178 | developmentRegion = English; 179 | hasScannedForEncodings = 0; 180 | knownRegions = ( 181 | en, 182 | Base, 183 | ); 184 | mainGroup = B520F6401E89232A008024CD; 185 | productRefGroup = B520F64A1E89232A008024CD /* Products */; 186 | projectDirPath = ""; 187 | projectRoot = ""; 188 | targets = ( 189 | B520F6481E89232A008024CD /* Demo */, 190 | ); 191 | }; 192 | /* End PBXProject section */ 193 | 194 | /* Begin PBXResourcesBuildPhase section */ 195 | B520F6471E89232A008024CD /* Resources */ = { 196 | isa = PBXResourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | B520F6641E89236B008024CD /* Main.storyboard in Resources */, 200 | B520F65C1E89232A008024CD /* LaunchScreen.storyboard in Resources */, 201 | B520F6591E89232A008024CD /* Assets.xcassets in Resources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | /* End PBXResourcesBuildPhase section */ 206 | 207 | /* Begin PBXSourcesBuildPhase section */ 208 | B520F6451E89232A008024CD /* Sources */ = { 209 | isa = PBXSourcesBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | B545AC291E8A088700D59DF8 /* LJPerson.m in Sources */, 213 | B520F6541E89232A008024CD /* ViewController.m in Sources */, 214 | B520F6511E89232A008024CD /* AppDelegate.m in Sources */, 215 | 449DDED620EDD08200669B84 /* NSString+LJExtension.m in Sources */, 216 | B520F6871E8925BB008024CD /* ContactTableViewCell.m in Sources */, 217 | B520F6841E892534008024CD /* AddContactViewController.m in Sources */, 218 | B5C4B0391EAD9A4F00AE5270 /* LJPickerDetailDelegate.m in Sources */, 219 | B545AC271E8A088700D59DF8 /* LJContactManager.m in Sources */, 220 | B520F6741E8923CB008024CD /* SortAddressBookViewController.m in Sources */, 221 | B520F6731E8923CB008024CD /* SelectPhoneNumViewController.m in Sources */, 222 | B545AC281E8A088700D59DF8 /* LJPeoplePickerDelegate.m in Sources */, 223 | B520F64E1E89232A008024CD /* main.m in Sources */, 224 | B520F6771E8923DB008024CD /* AddressBookViewController.m in Sources */, 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | /* End PBXSourcesBuildPhase section */ 229 | 230 | /* Begin PBXVariantGroup section */ 231 | B520F65A1E89232A008024CD /* LaunchScreen.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | B520F65B1E89232A008024CD /* Base */, 235 | ); 236 | name = LaunchScreen.storyboard; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | B520F65E1E89232A008024CD /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 252 | CLANG_WARN_BOOL_CONVERSION = YES; 253 | CLANG_WARN_COMMA = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 267 | CLANG_WARN_STRICT_PROTOTYPES = YES; 268 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 269 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | DEBUG_INFORMATION_FORMAT = dwarf; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | ENABLE_TESTABILITY = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu99; 278 | GCC_DYNAMIC_NO_PIC = NO; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_OPTIMIZATION_LEVEL = 0; 281 | GCC_PREPROCESSOR_DEFINITIONS = ( 282 | "DEBUG=1", 283 | "$(inherited)", 284 | ); 285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 287 | GCC_WARN_UNDECLARED_SELECTOR = YES; 288 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 289 | GCC_WARN_UNUSED_FUNCTION = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 292 | MTL_ENABLE_DEBUG_INFO = YES; 293 | ONLY_ACTIVE_ARCH = YES; 294 | SDKROOT = iphoneos; 295 | TARGETED_DEVICE_FAMILY = "1,2"; 296 | }; 297 | name = Debug; 298 | }; 299 | B520F65F1E89232A008024CD /* Release */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ALWAYS_SEARCH_USER_PATHS = NO; 303 | CLANG_ANALYZER_NONNULL = YES; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_COMMA = YES; 311 | CLANG_WARN_CONSTANT_CONVERSION = YES; 312 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 314 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 315 | CLANG_WARN_EMPTY_BODY = YES; 316 | CLANG_WARN_ENUM_CONVERSION = YES; 317 | CLANG_WARN_INFINITE_RECURSION = YES; 318 | CLANG_WARN_INT_CONVERSION = YES; 319 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 320 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 321 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 323 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 324 | CLANG_WARN_STRICT_PROTOTYPES = YES; 325 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 326 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 327 | CLANG_WARN_UNREACHABLE_CODE = YES; 328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 329 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 330 | COPY_PHASE_STRIP = NO; 331 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 332 | ENABLE_NS_ASSERTIONS = NO; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | GCC_C_LANGUAGE_STANDARD = gnu99; 335 | GCC_NO_COMMON_BLOCKS = YES; 336 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 337 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 338 | GCC_WARN_UNDECLARED_SELECTOR = YES; 339 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 340 | GCC_WARN_UNUSED_FUNCTION = YES; 341 | GCC_WARN_UNUSED_VARIABLE = YES; 342 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 343 | MTL_ENABLE_DEBUG_INFO = NO; 344 | SDKROOT = iphoneos; 345 | TARGETED_DEVICE_FAMILY = "1,2"; 346 | VALIDATE_PRODUCT = YES; 347 | }; 348 | name = Release; 349 | }; 350 | B520F6611E89232A008024CD /* Debug */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 354 | CODE_SIGN_IDENTITY = "iPhone Developer"; 355 | CODE_SIGN_STYLE = Manual; 356 | DEVELOPMENT_TEAM = Q87QUX3533; 357 | INFOPLIST_FILE = Demo/Info.plist; 358 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 359 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 360 | PRODUCT_BUNDLE_IDENTIFIER = com.money.uloan; 361 | PRODUCT_NAME = "$(TARGET_NAME)"; 362 | PROVISIONING_PROFILE = "4801bcce-dd48-4478-bbca-923a875055e8"; 363 | PROVISIONING_PROFILE_SPECIFIER = MobProvisioningProfiles; 364 | }; 365 | name = Debug; 366 | }; 367 | B520F6621E89232A008024CD /* Release */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CODE_SIGN_IDENTITY = "iPhone Developer"; 372 | CODE_SIGN_STYLE = Manual; 373 | DEVELOPMENT_TEAM = Q87QUX3533; 374 | INFOPLIST_FILE = Demo/Info.plist; 375 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 377 | PRODUCT_BUNDLE_IDENTIFIER = com.money.uloan; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | PROVISIONING_PROFILE = "4801bcce-dd48-4478-bbca-923a875055e8"; 380 | PROVISIONING_PROFILE_SPECIFIER = MobProvisioningProfiles; 381 | }; 382 | name = Release; 383 | }; 384 | /* End XCBuildConfiguration section */ 385 | 386 | /* Begin XCConfigurationList section */ 387 | B520F6441E89232A008024CD /* Build configuration list for PBXProject "Demo" */ = { 388 | isa = XCConfigurationList; 389 | buildConfigurations = ( 390 | B520F65E1E89232A008024CD /* Debug */, 391 | B520F65F1E89232A008024CD /* Release */, 392 | ); 393 | defaultConfigurationIsVisible = 0; 394 | defaultConfigurationName = Release; 395 | }; 396 | B520F6601E89232A008024CD /* Build configuration list for PBXNativeTarget "Demo" */ = { 397 | isa = XCConfigurationList; 398 | buildConfigurations = ( 399 | B520F6611E89232A008024CD /* Debug */, 400 | B520F6621E89232A008024CD /* Release */, 401 | ); 402 | defaultConfigurationIsVisible = 0; 403 | defaultConfigurationName = Release; 404 | }; 405 | /* End XCConfigurationList section */ 406 | }; 407 | rootObject = B520F6411E89232A008024CD /* Project object */; 408 | } 409 | -------------------------------------------------------------------------------- /Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/AddContactViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddContactViewController.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AddContactViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/AddContactViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddContactViewController.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "AddContactViewController.h" 10 | #import "LJContactManager.h" 11 | 12 | @interface AddContactViewController () 13 | 14 | @property (weak, nonatomic) IBOutlet UIButton *phoneNumBtn; 15 | 16 | @end 17 | 18 | @implementation AddContactViewController 19 | 20 | - (IBAction)addContactAction:(id)sender 21 | { 22 | UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"添加到通讯录" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle: nil otherButtonTitles:@"创建新联系人",@"添加到现有联系人", nil]; 23 | [sheet showInView:self.view]; 24 | } 25 | 26 | - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex 27 | { 28 | if (buttonIndex == 0) 29 | { 30 | [[LJContactManager sharedInstance] createNewContactWithPhoneNum:self.phoneNumBtn.titleLabel.text controller:self]; 31 | } 32 | else if (buttonIndex == 1) 33 | { 34 | [[LJContactManager sharedInstance] addToExistingContactsWithPhoneNum:self.phoneNumBtn.titleLabel.text controller:self]; 35 | } 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Demo/AddressBookViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddressBookViewController.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/24. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AddressBookViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/AddressBookViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddressBookViewController.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/24. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "AddressBookViewController.h" 10 | #import "LJContactManager.h" 11 | #import "LJPerson.h" 12 | 13 | @interface AddressBookViewController () 14 | 15 | @property (nonatomic, copy) NSArray *dataSource; 16 | 17 | @end 18 | 19 | @implementation AddressBookViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | 25 | [[LJContactManager sharedInstance] accessContactsComplection:^(BOOL succeed, NSArray *datas) { 26 | 27 | self.dataSource = datas; 28 | [self.tableView reloadData]; 29 | 30 | for (LJPerson *person in datas) 31 | { 32 | NSLog(@"名字列表:fullName = %@, firstName = %@, lastName = %@", person.fullName, person.familyName, person.givenName); 33 | 34 | for (LJPhone *model in person.phones) 35 | { 36 | NSLog(@"号码:phone = %@, label = %@", model.phone,model.label); 37 | } 38 | 39 | for (LJEmail *model in person.emails) 40 | { 41 | NSLog(@"电子邮件:email = %@, label = %@", model.email, model.label); 42 | } 43 | 44 | for (LJAddress *model in person.addresses) 45 | { 46 | NSLog(@"地址:address = %@, label = %@", model.city, model.label); 47 | } 48 | for (LJMessage *model in person.messages) 49 | { 50 | NSLog(@"即时通讯:service = %@, userName = %@", model.service, model.userName); 51 | } 52 | 53 | NSLog(@"生日:brithdayDate = %@",person.birthday.brithdayDate); 54 | 55 | for (LJSocialProfile *model in person.socials) 56 | { 57 | NSLog(@"社交:service = %@, username = %@, urlString = %@", model.service, model.username, model.urlString); 58 | } 59 | 60 | for (LJContactRelation *model in person.relations) 61 | { 62 | NSLog(@"关联人:label = %@, name = %@", model.label, model.name); 63 | } 64 | 65 | for (LJUrlAddress *model in person.urls) 66 | { 67 | NSLog(@"URL:label = %@, urlString = %@", model.label,model.urlString); 68 | } 69 | 70 | NSLog(@"---------------------*******------------------------------------"); 71 | } 72 | }]; 73 | } 74 | 75 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 76 | { 77 | return self.dataSource.count; 78 | } 79 | 80 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 81 | { 82 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TableViewCell"]; 83 | LJPerson *personModel = self.dataSource[indexPath.row]; 84 | cell.textLabel.text = personModel.fullName; 85 | LJPhone *phoneModel = personModel.phones.firstObject; 86 | cell.detailTextLabel.text = phoneModel.phone; 87 | return cell; 88 | } 89 | 90 | - (void)dealloc 91 | { 92 | NSLog(@"dealloc"); 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "LJContactManager.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | 20 | // Override point for customization after application launch. 21 | 22 | [LJContactManager sharedInstance].contactChangeHandler = ^{ 23 | 24 | NSLog(@"通讯录修改咯"); 25 | 26 | [[LJContactManager sharedInstance] accessContactsComplection:^(BOOL succeed, NSArray *contacts) { 27 | NSLog(@"%@", contacts); 28 | }]; 29 | 30 | [[LJContactManager sharedInstance] accessSectionContactsComplection:^(BOOL succeed, NSArray *contacts, NSArray *keys) { 31 | NSLog(@"%@--%@", contacts, keys); 32 | }]; 33 | }; 34 | 35 | return YES; 36 | } 37 | 38 | - (void)applicationWillResignActive:(UIApplication *)application { 39 | // 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. 40 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 41 | } 42 | 43 | 44 | - (void)applicationDidEnterBackground:(UIApplication *)application { 45 | // 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. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | 50 | - (void)applicationWillEnterForeground:(UIApplication *)application { 51 | // 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. 52 | } 53 | 54 | 55 | - (void)applicationDidBecomeActive:(UIApplication *)application { 56 | // 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. 57 | } 58 | 59 | 60 | - (void)applicationWillTerminate:(UIApplication *)application { 61 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/Assets.xcassets/hand portrait.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "hand portrait@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "hand portrait@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Demo/Assets.xcassets/hand portrait.imageset/hand portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leejayID/LJContactManager/ad7cd248e6a15f3a8f6c80260781611323210173/Demo/Assets.xcassets/hand portrait.imageset/hand portrait@2x.png -------------------------------------------------------------------------------- /Demo/Assets.xcassets/hand portrait.imageset/hand portrait@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leejayID/LJContactManager/ad7cd248e6a15f3a8f6c80260781611323210173/Demo/Assets.xcassets/hand portrait.imageset/hand portrait@3x.png -------------------------------------------------------------------------------- /Demo/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 | -------------------------------------------------------------------------------- /Demo/ContactTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ContactTableViewCell.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class LJPerson; 12 | 13 | @interface ContactTableViewCell : UITableViewCell 14 | 15 | @property (nonatomic, strong) LJPerson *model; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Demo/ContactTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ContactTableViewCell.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "ContactTableViewCell.h" 10 | #import "LJPerson.h" 11 | 12 | @interface ContactTableViewCell () 13 | 14 | @property (weak, nonatomic) IBOutlet UIImageView *iconImageV; 15 | @property (weak, nonatomic) IBOutlet UILabel *nameLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *phoneNumLabel; 17 | 18 | @end 19 | 20 | @implementation ContactTableViewCell 21 | 22 | - (void)setModel:(LJPerson *)model 23 | { 24 | self.iconImageV.image = model.image ? model.image : [UIImage imageNamed:@"hand portrait"]; 25 | self.nameLabel.text = model.fullName; 26 | LJPhone *phoneModel = model.phones.firstObject; 27 | self.phoneNumLabel.text = phoneModel.phone; 28 | } 29 | 30 | - (void)awakeFromNib { 31 | [super awakeFromNib]; 32 | // Initialization code 33 | 34 | self.iconImageV.layer.cornerRadius = 30; 35 | self.iconImageV.layer.masksToBounds = YES; 36 | } 37 | 38 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 39 | [super setSelected:selected animated:animated]; 40 | 41 | // Configure the view for the selected state 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | zh_CN 7 | CFBundleDisplayName 8 | 通讯录Demo 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSContactsUsageDescription 26 | 亲,我要访问下你的通讯录哦? 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Demo/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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 175 | 185 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 293 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 351 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | -------------------------------------------------------------------------------- /Demo/SelectPhoneNumViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SelectPhoneNumViewController.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SelectPhoneNumViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/SelectPhoneNumViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SelectPhoneNumViewController.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "SelectPhoneNumViewController.h" 10 | #import "LJContactManager.h" 11 | 12 | @interface SelectPhoneNumViewController () 13 | 14 | @property (weak, nonatomic) IBOutlet UITextField *nameTextField; 15 | @property (weak, nonatomic) IBOutlet UITextField *phoneNumTextField; 16 | 17 | @end 18 | 19 | @implementation SelectPhoneNumViewController 20 | 21 | - (IBAction)selectPhoneNumAction:(id)sender 22 | { 23 | [[LJContactManager sharedInstance] selectContactAtController:self complection:^(NSString *name, NSString *phone) { 24 | self.nameTextField.text = name; 25 | self.phoneNumTextField.text = phone; 26 | }]; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Demo/SortAddressBookViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SortAddressBookViewController.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SortAddressBookViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/SortAddressBookViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SortAddressBookViewController.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "SortAddressBookViewController.h" 10 | #import "LJPerson.h" 11 | #import "LJContactManager.h" 12 | #import "ContactTableViewCell.h" 13 | 14 | @interface SortAddressBookViewController () 15 | 16 | @property (nonatomic, copy) NSArray *dataSource; 17 | @property (nonatomic, copy) NSArray *keys; 18 | 19 | @end 20 | 21 | @implementation SortAddressBookViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | // Do any additional setup after loading the view. 26 | 27 | [[LJContactManager sharedInstance] accessSectionContactsComplection:^(BOOL succeed, NSArray *contacts, NSArray *keys) { 28 | 29 | self.dataSource = contacts; 30 | self.keys = keys; 31 | [self.tableView reloadData]; 32 | 33 | for (LJSectionPerson *sectionModel in contacts) 34 | { 35 | NSLog(@"---------------------***%@***------------------------------------",sectionModel.key); 36 | 37 | for (LJPerson *person in sectionModel.persons) 38 | { 39 | NSLog(@"名字列表:fullName = %@, firstName = %@, lastName = %@", person.fullName, person.familyName, person.givenName); 40 | 41 | for (LJPhone *model in person.phones) 42 | { 43 | NSLog(@"号码:phone = %@, label = %@", model.phone,model.label); 44 | } 45 | 46 | for (LJEmail *model in person.emails) 47 | { 48 | NSLog(@"电子邮件:email = %@, label = %@", model.email, model.label); 49 | } 50 | 51 | for (LJAddress *model in person.addresses) 52 | { 53 | NSLog(@"地址:address = %@, label = %@", model.city, model.label); 54 | } 55 | for (LJMessage *model in person.messages) 56 | { 57 | NSLog(@"即时通讯:service = %@, userName = %@", model.service, model.userName); 58 | } 59 | 60 | NSLog(@"生日:brithdayDate = %@",person.birthday.brithdayDate); 61 | 62 | for (LJSocialProfile *model in person.socials) 63 | { 64 | NSLog(@"社交:service = %@, username = %@, urlString = %@", model.service, model.username, model.urlString); 65 | } 66 | 67 | for (LJContactRelation *model in person.relations) 68 | { 69 | NSLog(@"关联人:label = %@, name = %@", model.label, model.name); 70 | } 71 | 72 | for (LJUrlAddress *model in person.urls) 73 | { 74 | NSLog(@"URL:label = %@, urlString = %@", model.label,model.urlString); 75 | } 76 | 77 | } 78 | } 79 | 80 | }]; 81 | } 82 | 83 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 84 | { 85 | return self.dataSource.count; 86 | } 87 | 88 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 89 | { 90 | LJSectionPerson *sectionModel = self.dataSource[section]; 91 | return sectionModel.persons.count; 92 | } 93 | 94 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 95 | { 96 | ContactTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SectionCell"]; 97 | LJSectionPerson *sectionModel = self.dataSource[indexPath.section]; 98 | LJPerson *personModel = sectionModel.persons[indexPath.row]; 99 | cell.model = personModel; 100 | return cell; 101 | } 102 | 103 | - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView 104 | { 105 | return self.keys; 106 | } 107 | 108 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 109 | { 110 | return 30; 111 | } 112 | 113 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 114 | { 115 | LJSectionPerson *sectionModel = self.dataSource[section]; 116 | return sectionModel.key; 117 | } 118 | 119 | - (void)dealloc 120 | { 121 | NSLog(@"dealloc"); 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /Demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UITableViewController 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /Demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/3/27. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 LeeJay 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 | -------------------------------------------------------------------------------- /LJContactManager.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = 'LJContactManager' 4 | s.version = '1.0.7' 5 | s.summary = '获取通讯录所有信息' 6 | s.description = '一行代码获取已排序和未排序的通讯录,一行代码添加号码至通讯录,操作通讯录的 UI' 7 | s.homepage = 'https://github.com/leejayID/LJContactManager.git' 8 | s.license = 'MIT' 9 | s.authors = {'李健' => 'leejay_email@163.com'} 10 | s.platform = :ios, '8.0' 11 | s.source = {:git => 'https://github.com/leejayID/LJContactManager.git', :tag => s.version} 12 | s.source_files = 'LJContactManager', 'LJContactManager/**/*.{h,m}' 13 | s.requires_arc = true 14 | end 15 | -------------------------------------------------------------------------------- /LJContactManager/LJContactManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJAddressBookManager.h 3 | // LJContactManager 4 | // 5 | // Created by LeeJay on 2017/3/22. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class LJPerson, LJSectionPerson; 12 | 13 | /** 14 | 通讯录变更回调(未分组的通讯录) 15 | */ 16 | typedef void (^LJContactChangeHandler) (void); 17 | 18 | @interface LJContactManager : NSObject 19 | 20 | + (instancetype)sharedInstance; 21 | 22 | /** 23 | 通讯录变更回调 24 | */ 25 | @property (nonatomic, copy) LJContactChangeHandler contactChangeHandler; 26 | 27 | /** 28 | 请求授权 29 | 30 | @param completion 回调 31 | */ 32 | - (void)requestAddressBookAuthorization:(void (^) (BOOL authorization))completion; 33 | 34 | /** 35 | 选择联系人 36 | 37 | @param controller 控制器 38 | @param completcion 回调 39 | */ 40 | - (void)selectContactAtController:(UIViewController *)controller 41 | complection:(void (^)(NSString *name, NSString *phone))completcion; 42 | 43 | /** 44 | 创建新联系人 45 | 46 | @param phoneNum 手机号 47 | @param controller 当前 Controller 48 | */ 49 | - (void)createNewContactWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller; 50 | 51 | /** 52 | 添加到现有联系人 53 | 54 | @param phoneNum 手机号 55 | @param controller 当前 Controller 56 | */ 57 | - (void)addToExistingContactsWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller; 58 | 59 | /** 60 | 获取联系人列表(未分组的通讯录) 61 | 62 | @param completcion 回调 63 | */ 64 | - (void)accessContactsComplection:(void (^)(BOOL succeed, NSArray *contacts))completcion; 65 | 66 | /** 67 | 获取联系人列表(已分组的通讯录) 68 | 69 | @param completcion 回调 70 | */ 71 | - (void)accessSectionContactsComplection:(void (^)(BOOL succeed, NSArray *contacts, NSArray *keys))completcion; 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /LJContactManager/LJContactManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJAddressBookManager.m 3 | // LJContactManager 4 | // 5 | // Created by LeeJay on 2017/3/22. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "LJContactManager.h" 10 | #import 11 | #import 12 | #import "LJPerson.h" 13 | #import "LJPeoplePickerDelegate.h" 14 | #import 15 | #import 16 | #import "LJPickerDetailDelegate.h" 17 | #import "NSString+LJExtension.h" 18 | 19 | #define IOS9_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) 20 | 21 | @interface LJContactManager () 22 | 23 | @property (nonatomic, copy) void (^handler) (NSString *, NSString *); 24 | @property (nonatomic, assign) BOOL isAdd; 25 | @property (nonatomic, copy) NSArray *keys; 26 | @property (nonatomic, strong) LJPeoplePickerDelegate *pickerDelegate; 27 | @property (nonatomic, strong) LJPickerDetailDelegate *pickerDetailDelegate; 28 | @property (nonatomic) ABAddressBookRef addressBook; 29 | @property (nonatomic, strong) CNContactStore *contactStore; 30 | @property (nonatomic) dispatch_queue_t queue; 31 | 32 | @end 33 | 34 | @implementation LJContactManager 35 | 36 | - (instancetype)init 37 | { 38 | self = [super init]; 39 | if (self) 40 | { 41 | _queue = dispatch_queue_create("com.addressBook.queue", DISPATCH_QUEUE_SERIAL); 42 | 43 | if (IOS9_OR_LATER) 44 | { 45 | _contactStore = [CNContactStore new]; 46 | [[NSNotificationCenter defaultCenter] addObserver:self 47 | selector:@selector(_contactStoreDidChange) 48 | name:CNContactStoreDidChangeNotification 49 | object:nil]; 50 | } 51 | else 52 | { 53 | _addressBook = ABAddressBookCreate(); 54 | ABAddressBookRegisterExternalChangeCallback(self.addressBook, _addressBookChange, nil); 55 | } 56 | } 57 | return self; 58 | } 59 | 60 | + (instancetype)sharedInstance 61 | { 62 | static id shared_instance = nil; 63 | static dispatch_once_t onceToken; 64 | dispatch_once(&onceToken, ^{ 65 | shared_instance = [[self alloc] init]; 66 | }); 67 | return shared_instance; 68 | } 69 | 70 | - (NSArray *)keys 71 | { 72 | if (!_keys) 73 | { 74 | _keys = @[[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName], 75 | CNContactPhoneNumbersKey, 76 | CNContactOrganizationNameKey, 77 | CNContactDepartmentNameKey, 78 | CNContactJobTitleKey, 79 | CNContactPhoneticGivenNameKey, 80 | CNContactPhoneticFamilyNameKey, 81 | CNContactPhoneticMiddleNameKey, 82 | CNContactImageDataKey, 83 | CNContactThumbnailImageDataKey, 84 | CNContactEmailAddressesKey, 85 | CNContactPostalAddressesKey, 86 | CNContactBirthdayKey, 87 | CNContactNonGregorianBirthdayKey, 88 | CNContactInstantMessageAddressesKey, 89 | CNContactSocialProfilesKey, 90 | CNContactRelationsKey, 91 | CNContactUrlAddressesKey]; 92 | 93 | } 94 | return _keys; 95 | } 96 | 97 | - (LJPeoplePickerDelegate *)pickerDelegate 98 | { 99 | if (!_pickerDelegate) 100 | { 101 | _pickerDelegate = [LJPeoplePickerDelegate new]; 102 | } 103 | return _pickerDelegate; 104 | } 105 | 106 | - (LJPickerDetailDelegate *)pickerDetailDelegate 107 | { 108 | if (!_pickerDetailDelegate) 109 | { 110 | _pickerDetailDelegate = [LJPickerDetailDelegate new]; 111 | __weak typeof(self) weakSelf = self; 112 | _pickerDetailDelegate.handler = ^(NSString *name, NSString *phoneNum) { 113 | NSString *newPhoneNum = [NSString lj_filterSpecialString:phoneNum]; 114 | weakSelf.handler(name, newPhoneNum); 115 | }; 116 | } 117 | return _pickerDetailDelegate; 118 | } 119 | 120 | #pragma mark - Public 121 | 122 | - (void)selectContactAtController:(UIViewController *)controller 123 | complection:(void (^)(NSString *, NSString *))completcion 124 | { 125 | self.isAdd = NO; 126 | [self _presentFromController:controller]; 127 | 128 | self.handler = completcion; 129 | } 130 | 131 | - (void)createNewContactWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller 132 | { 133 | if (IOS9_OR_LATER) 134 | { 135 | CNMutableContact *contact = [[CNMutableContact alloc] init]; 136 | CNLabeledValue *labelValue = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile 137 | value:[CNPhoneNumber phoneNumberWithStringValue:phoneNum]]; 138 | contact.phoneNumbers = @[labelValue]; 139 | CNContactViewController *contactController = [CNContactViewController viewControllerForNewContact:contact]; 140 | contactController.delegate = self; 141 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:contactController]; 142 | [controller presentViewController:nav animated:YES completion:nil]; 143 | } 144 | else 145 | { 146 | ABNewPersonViewController *picker = [[ABNewPersonViewController alloc] init]; 147 | ABRecordRef newPerson = ABPersonCreate(); 148 | ABMutableMultiValueRef multiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType); 149 | CFErrorRef error = NULL; 150 | ABMultiValueAddValueAndLabel(multiValue, (__bridge CFTypeRef)(phoneNum), kABPersonPhoneMobileLabel, NULL); 151 | ABRecordSetValue(newPerson, kABPersonPhoneProperty, multiValue , &error); 152 | CFRelease(multiValue); 153 | picker.displayedPerson = newPerson; 154 | CFRelease(newPerson); 155 | picker.newPersonViewDelegate = self; 156 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:picker]; 157 | [controller presentViewController:nav animated:YES completion:nil]; 158 | } 159 | } 160 | 161 | - (void)addToExistingContactsWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller 162 | { 163 | self.isAdd = YES; 164 | self.pickerDelegate.phoneNum = phoneNum; 165 | self.pickerDelegate.controller = controller; 166 | 167 | [self _presentFromController:controller]; 168 | } 169 | 170 | - (void)accessContactsComplection:(void (^)(BOOL, NSArray *))completcion 171 | { 172 | [self requestAddressBookAuthorization:^(BOOL authorization) { 173 | 174 | if (authorization) 175 | { 176 | if (IOS9_OR_LATER) 177 | { 178 | [self _asynAccessContactStoreWithSort:NO completcion:^(NSArray *datas, NSArray *keys) { 179 | 180 | if (completcion) 181 | { 182 | completcion(YES, datas); 183 | } 184 | }]; 185 | } 186 | else 187 | { 188 | [self _asynAccessAddressBookWithSort:NO completcion:^(NSArray *datas, NSArray *keys) { 189 | 190 | if (completcion) 191 | { 192 | completcion(YES, datas); 193 | } 194 | }]; 195 | } 196 | } 197 | else 198 | { 199 | if (completcion) 200 | { 201 | completcion(NO, nil); 202 | } 203 | } 204 | }]; 205 | } 206 | 207 | - (void)accessSectionContactsComplection:(void (^)(BOOL, NSArray *, NSArray *))completcion 208 | { 209 | [self requestAddressBookAuthorization:^(BOOL authorization) { 210 | 211 | if (authorization) 212 | { 213 | if (IOS9_OR_LATER) 214 | { 215 | [self _asynAccessContactStoreWithSort:YES completcion:^(NSArray *datas, NSArray *keys) { 216 | 217 | if (completcion) 218 | { 219 | completcion(YES, datas, keys); 220 | } 221 | }]; 222 | } 223 | else 224 | { 225 | [self _asynAccessAddressBookWithSort:YES completcion:^(NSArray *datas, NSArray *keys) { 226 | 227 | if (completcion) 228 | { 229 | completcion(YES, datas, keys); 230 | } 231 | }]; 232 | } 233 | } 234 | else 235 | { 236 | if (completcion) 237 | { 238 | completcion(NO, nil, nil); 239 | } 240 | } 241 | }]; 242 | } 243 | 244 | #pragma mark - ABNewPersonViewControllerDelegate 245 | 246 | - (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(nullable ABRecordRef)person 247 | { 248 | [newPersonView dismissViewControllerAnimated:YES completion:nil]; 249 | } 250 | 251 | #pragma mark - CNContactViewControllerDelegate 252 | 253 | - (void)contactViewController:(CNContactViewController *)viewController didCompleteWithContact:(nullable CNContact *)contact 254 | { 255 | [viewController dismissViewControllerAnimated:YES completion:nil]; 256 | } 257 | 258 | - (void)requestAddressBookAuthorization:(void (^)(BOOL authorization))completion 259 | { 260 | if (IOS9_OR_LATER) 261 | { 262 | CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]; 263 | 264 | if (status == CNAuthorizationStatusNotDetermined) 265 | { 266 | [self _authorizationAddressBook:^(BOOL succeed) { 267 | _blockExecute(completion, succeed); 268 | }]; 269 | } 270 | else 271 | { 272 | _blockExecute(completion, status == CNAuthorizationStatusAuthorized); 273 | } 274 | } 275 | else 276 | { 277 | if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) 278 | { 279 | [self _authorizationAddressBook:^(BOOL succeed) { 280 | _blockExecute(completion, succeed); 281 | }]; 282 | } 283 | else 284 | { 285 | _blockExecute(completion, ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized); 286 | } 287 | } 288 | } 289 | 290 | #pragma mark - Private 291 | 292 | - (void)_authorizationAddressBook:(void (^) (BOOL succeed))completion 293 | { 294 | if (IOS9_OR_LATER) 295 | { 296 | [_contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) { 297 | if (completion) 298 | { 299 | completion(granted); 300 | } 301 | }]; 302 | } 303 | else 304 | { 305 | ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error) { 306 | if (completion) 307 | { 308 | completion(granted); 309 | } 310 | }); 311 | } 312 | } 313 | 314 | void _blockExecute(void (^completion)(BOOL authorizationA), BOOL authorizationB) 315 | { 316 | if (completion) 317 | { 318 | if ([NSThread isMainThread]) 319 | { 320 | completion(authorizationB); 321 | } 322 | else 323 | { 324 | dispatch_async(dispatch_get_main_queue(), ^{ 325 | completion(authorizationB); 326 | }); 327 | } 328 | } 329 | } 330 | 331 | - (void)_presentFromController:(UIViewController *)controller 332 | { 333 | if (IOS9_OR_LATER) 334 | { 335 | CNContactPickerViewController *pc = [[CNContactPickerViewController alloc] init]; 336 | if (self.isAdd) 337 | { 338 | pc.delegate = self.pickerDelegate; 339 | } 340 | else 341 | { 342 | pc.delegate = self.pickerDetailDelegate; 343 | } 344 | 345 | pc.displayedPropertyKeys = @[CNContactPhoneNumbersKey]; 346 | 347 | [self requestAddressBookAuthorization:^(BOOL authorization) { 348 | if (authorization) 349 | { 350 | [controller presentViewController:pc animated:YES completion:nil]; 351 | } 352 | else 353 | { 354 | [self _showAlertFromController:controller]; 355 | } 356 | }]; 357 | } 358 | else 359 | { 360 | ABPeoplePickerNavigationController *pvc = [[ABPeoplePickerNavigationController alloc] init]; 361 | pvc.displayedProperties = @[@(kABPersonPhoneProperty)]; 362 | 363 | if (self.isAdd) 364 | { 365 | pvc.peoplePickerDelegate = self.pickerDelegate; 366 | } 367 | else 368 | { 369 | pvc.peoplePickerDelegate = self.pickerDetailDelegate; 370 | } 371 | 372 | [self requestAddressBookAuthorization:^(BOOL authorization) { 373 | 374 | if (authorization) 375 | { 376 | [controller presentViewController:pvc animated:YES completion:nil]; 377 | } 378 | else 379 | { 380 | [self _showAlertFromController:controller]; 381 | } 382 | 383 | }]; 384 | } 385 | } 386 | 387 | - (void)_showAlertFromController:(UIViewController *)controller 388 | { 389 | UIAlertController *alertControl = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"您的通讯录暂未允许访问,请去设置->隐私里面授权!" preferredStyle:UIAlertControllerStyleAlert]; 390 | [alertControl addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 391 | }])]; 392 | [alertControl addAction:([UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 393 | NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; 394 | if ([[UIApplication sharedApplication] canOpenURL:url]) { 395 | if (@available(iOS 10.0, *)) { 396 | [[UIApplication sharedApplication] openURL:url options:@{} 397 | completionHandler:^(BOOL success) { 398 | }]; 399 | } else { 400 | [[UIApplication sharedApplication] openURL:url]; 401 | } 402 | } 403 | }])]; 404 | [controller presentViewController:alertControl animated:YES completion:nil]; 405 | } 406 | 407 | - (void)_asynAccessAddressBookWithSort:(BOOL)isSort completcion:(void (^)(NSArray *, NSArray *))completcion 408 | { 409 | NSMutableArray *datas = [NSMutableArray array]; 410 | 411 | __weak typeof(self) weakSelf = self; 412 | dispatch_async(_queue, ^{ 413 | 414 | CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(weakSelf.addressBook); 415 | CFIndex count = CFArrayGetCount(allPeople); 416 | 417 | for (int i = 0; i < count; i++) 418 | { 419 | ABRecordRef record = CFArrayGetValueAtIndex(allPeople, i); 420 | LJPerson *personModel = [[LJPerson alloc] initWithRecord:record]; 421 | [datas addObject:personModel]; 422 | } 423 | 424 | CFRelease(allPeople); 425 | 426 | if (!isSort) 427 | { 428 | dispatch_async(dispatch_get_main_queue(), ^{ 429 | 430 | if (completcion) 431 | { 432 | completcion(datas, nil); 433 | } 434 | 435 | }); 436 | 437 | return ; 438 | } 439 | 440 | [self _sortNameWithDatas:datas completcion:^(NSArray *persons, NSArray *keys) { 441 | 442 | dispatch_async(dispatch_get_main_queue(), ^{ 443 | 444 | if (completcion) 445 | { 446 | completcion(persons, keys); 447 | } 448 | 449 | }); 450 | 451 | }]; 452 | 453 | }); 454 | } 455 | 456 | - (void)_asynAccessContactStoreWithSort:(BOOL)isSort completcion:(void (^)(NSArray *, NSArray *))completcion 457 | { 458 | __weak typeof(self) weakSelf = self; 459 | dispatch_async(_queue, ^{ 460 | 461 | NSMutableArray *datas = [NSMutableArray array]; 462 | CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:self.keys]; 463 | [weakSelf.contactStore enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) { 464 | 465 | LJPerson *person = [[LJPerson alloc] initWithCNContact:contact]; 466 | [datas addObject:person]; 467 | 468 | }]; 469 | 470 | if (!isSort) 471 | { 472 | dispatch_async(dispatch_get_main_queue(), ^{ 473 | 474 | if (completcion) 475 | { 476 | completcion(datas, nil); 477 | } 478 | 479 | }); 480 | 481 | return; 482 | } 483 | 484 | [self _sortNameWithDatas:datas completcion:^(NSArray *persons, NSArray *keys) { 485 | 486 | dispatch_async(dispatch_get_main_queue(), ^{ 487 | 488 | if (completcion) 489 | { 490 | completcion(persons, keys); 491 | } 492 | 493 | }); 494 | 495 | }]; 496 | 497 | }); 498 | } 499 | 500 | - (void)_sortNameWithDatas:(NSArray *)datas completcion:(void (^)(NSArray *, NSArray *))completcion 501 | { 502 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 503 | 504 | for (LJPerson *person in datas) 505 | { 506 | // 拼音首字母 507 | NSString *firstLetter = nil; 508 | 509 | if (person.fullName.length == 0) 510 | { 511 | firstLetter = @"#"; 512 | } 513 | else 514 | { 515 | NSString *pinyinString = [NSString lj_pinyinForString:person.fullName]; 516 | person.pinyin = pinyinString; 517 | NSString *upperStr = [[pinyinString substringToIndex:1] uppercaseString]; 518 | NSString *regex = @"^[A-Z]$"; 519 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex]; 520 | firstLetter = [predicate evaluateWithObject:upperStr] ? upperStr : @"#"; 521 | } 522 | 523 | if (dict[firstLetter]) 524 | { 525 | [dict[firstLetter] addObject:person]; 526 | } 527 | else 528 | { 529 | NSMutableArray *arr = [NSMutableArray arrayWithObjects:person, nil]; 530 | dict[firstLetter] = arr; 531 | } 532 | } 533 | 534 | NSMutableArray *keys = [[[dict allKeys] sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; 535 | 536 | if ([keys.firstObject isEqualToString:@"#"]) 537 | { 538 | [keys addObject:keys.firstObject]; 539 | [keys removeObjectAtIndex:0]; 540 | } 541 | 542 | NSMutableArray *persons = [NSMutableArray array]; 543 | 544 | [keys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL * _Nonnull stop) { 545 | 546 | LJSectionPerson *person = [LJSectionPerson new]; 547 | person.key = key; 548 | 549 | // 组内按照拼音排序 550 | NSArray *personsArr = [dict[key] sortedArrayUsingComparator:^NSComparisonResult(LJPerson *person1, LJPerson *person2) { 551 | 552 | NSComparisonResult result = [person1.pinyin compare:person2.pinyin]; 553 | return result; 554 | }]; 555 | 556 | person.persons = personsArr; 557 | 558 | [persons addObject:person]; 559 | }]; 560 | 561 | if (completcion) 562 | { 563 | completcion(persons, keys); 564 | } 565 | } 566 | 567 | void _addressBookChange(ABAddressBookRef addressBook, CFDictionaryRef info, void *context) 568 | { 569 | if ([LJContactManager sharedInstance].contactChangeHandler) 570 | { 571 | [LJContactManager sharedInstance].contactChangeHandler(); 572 | } 573 | } 574 | 575 | - (void)_contactStoreDidChange 576 | { 577 | if ([LJContactManager sharedInstance].contactChangeHandler) 578 | { 579 | [LJContactManager sharedInstance].contactChangeHandler(); 580 | } 581 | } 582 | 583 | - (void)dealloc 584 | { 585 | if (IOS9_OR_LATER) 586 | { 587 | [[NSNotificationCenter defaultCenter] removeObserver:self name:CNContactStoreDidChangeNotification object:nil]; 588 | } 589 | else 590 | { 591 | ABAddressBookUnregisterExternalChangeCallback(_addressBook, _addressBookChange, nil); 592 | if (_addressBook) 593 | { 594 | CFRelease(_addressBook); 595 | _addressBook = NULL; 596 | } 597 | } 598 | } 599 | 600 | @end 601 | 602 | 603 | -------------------------------------------------------------------------------- /LJContactManager/LJPeoplePickerDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJPeoplePickerDelegate.h 3 | // LJContactManager 4 | // 5 | // Created by LeeJay on 2017/3/22. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface LJPeoplePickerDelegate : NSObject 14 | 15 | @property (nonatomic, copy) NSString *phoneNum; 16 | @property (nonatomic, weak) UIViewController *controller; 17 | @property (nonatomic, copy) void (^completcion) (BOOL succeed); 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LJContactManager/LJPeoplePickerDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJPeoplePickerDelegate.m 3 | // LJContactManager 4 | // 5 | // Created by LeeJay on 2017/3/22. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "LJPeoplePickerDelegate.h" 10 | 11 | @implementation LJPeoplePickerDelegate 12 | 13 | #pragma mark - ABPeoplePickerNavigationControllerDelegate 14 | 15 | - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person 16 | { 17 | [peoplePicker dismissViewControllerAnimated:YES completion:^{ 18 | [self _addToExistingContactsWithPhoneNum:self.phoneNum person:person controller:self.controller]; 19 | }]; 20 | } 21 | 22 | #pragma mark - CNContactPickerDelegate 23 | 24 | - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact; 25 | { 26 | [picker dismissViewControllerAnimated:YES completion:^{ 27 | [self _addToExistingContactsWithPhoneNum:self.phoneNum contact:contact controller:self.controller]; 28 | }]; 29 | } 30 | 31 | #pragma mark - ABNewPersonViewControllerDelegate 32 | 33 | - (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(nullable ABRecordRef)person 34 | { 35 | if (self.completcion) 36 | { 37 | self.completcion(YES); 38 | } 39 | [newPersonView dismissViewControllerAnimated:YES completion:nil]; 40 | } 41 | 42 | - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker 43 | { 44 | if (self.completcion) 45 | { 46 | self.completcion(NO); 47 | } 48 | } 49 | 50 | #pragma mark - CNContactViewControllerDelegate 51 | 52 | - (void)contactViewController:(CNContactViewController *)viewController didCompleteWithContact:(CNContact *)contact 53 | { 54 | if (self.completcion) 55 | { 56 | self.completcion(YES); 57 | } 58 | [viewController dismissViewControllerAnimated:YES completion:nil]; 59 | } 60 | 61 | - (void)contactPickerDidCancel:(CNContactPickerViewController *)picker 62 | { 63 | if (self.completcion) 64 | { 65 | self.completcion(NO); 66 | } 67 | } 68 | 69 | #pragma mark - Private 70 | 71 | /** 72 | 将号码添加某人的通讯录 (iOS 9 以下) 73 | 74 | @param phoneNum 号码 75 | @param person 联系人 76 | @param controller 控制器 77 | */ 78 | - (void)_addToExistingContactsWithPhoneNum:(NSString *)phoneNum 79 | person:(ABRecordRef)person 80 | controller:(UIViewController *)controller 81 | { 82 | ABMutableMultiValueRef phoneMulti = ABRecordCopyValue(person, kABPersonPhoneProperty); 83 | 84 | NSMutableArray *labels = [NSMutableArray array]; 85 | NSMutableArray *phones = [NSMutableArray array]; 86 | 87 | for (CFIndex i = 0; i < ABMultiValueGetCount(phoneMulti); i++) 88 | { 89 | NSString *phone = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneMulti, i)); 90 | NSString *label = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(phoneMulti, i)); 91 | [phones addObject:phone]; 92 | [labels addObject:label]; 93 | } 94 | CFRelease(phoneMulti); 95 | 96 | ABMutableMultiValueRef multiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType); 97 | CFErrorRef error = NULL; 98 | 99 | for (int i = 0; i < phones.count; i++) 100 | { 101 | ABMultiValueAddValueAndLabel(multiValue, (__bridge CFTypeRef)(phones[i]), (__bridge CFStringRef)(labels[i]), NULL); 102 | } 103 | 104 | ABMultiValueAddValueAndLabel(multiValue, (__bridge CFTypeRef)(phoneNum), kABPersonPhoneMobileLabel, NULL); 105 | 106 | ABRecordSetValue(person, kABPersonPhoneProperty, multiValue, &error); 107 | 108 | CFRelease(multiValue); 109 | 110 | ABNewPersonViewController *picker = [[ABNewPersonViewController alloc] init]; 111 | picker.displayedPerson = person; 112 | picker.newPersonViewDelegate = self; 113 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:picker]; 114 | [controller presentViewController:nav animated:YES completion:nil]; 115 | } 116 | 117 | /** 118 | 将号码添加某人的通讯录 (iOS 9 以上) 119 | 120 | @param phoneNum 号码 121 | @param contact 联系人 122 | @param controller 控制器 123 | */ 124 | - (void)_addToExistingContactsWithPhoneNum:(NSString *)phoneNum 125 | contact:(CNContact *)contact 126 | controller:(UIViewController *)controller 127 | { 128 | CNMutableContact *mutableContact = [contact mutableCopy]; 129 | 130 | CNLabeledValue *phoneNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile 131 | value:[CNPhoneNumber phoneNumberWithStringValue:phoneNum]]; 132 | 133 | if (mutableContact.phoneNumbers.count > 0) 134 | { 135 | NSMutableArray *phoneNumbers = [mutableContact.phoneNumbers mutableCopy]; 136 | [phoneNumbers addObject:phoneNumber]; 137 | mutableContact.phoneNumbers = phoneNumbers; 138 | } 139 | else 140 | { 141 | mutableContact.phoneNumbers = @[phoneNumber]; 142 | } 143 | 144 | CNContactViewController *contactController = [CNContactViewController viewControllerForNewContact:mutableContact]; 145 | contactController.delegate = self; 146 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:contactController]; 147 | [controller presentViewController:nav animated:YES completion:nil]; 148 | } 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /LJContactManager/LJPerson.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJPerson.h 3 | // LJContactManager 4 | // 5 | // Created by LeeJay on 2017/3/22. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @class LJPhone, LJEmail, LJAddress, LJBirthday, LJMessage, LJSocialProfile, LJContactRelation, LJUrlAddress; 14 | 15 | typedef NS_ENUM(NSUInteger, LJContactType) 16 | { 17 | LJContactTypePerson = 0, 18 | LJContactTypeOrigination, 19 | }; 20 | 21 | @interface LJPerson : NSObject 22 | 23 | /** 24 | 联系人类型 25 | */ 26 | @property (nonatomic) LJContactType contactType; 27 | 28 | /** 29 | 姓名 30 | */ 31 | @property (nonatomic, copy) NSString *fullName; 32 | 33 | /** 34 | 姓名拼音 35 | */ 36 | @property (nonatomic, copy) NSString *pinyin; 37 | 38 | /** 39 | 姓 40 | */ 41 | @property (nonatomic, copy) NSString *familyName; 42 | 43 | /** 44 | 名 45 | */ 46 | @property (nonatomic, copy) NSString *givenName; 47 | 48 | /** 49 | 姓名前缀 50 | */ 51 | @property (nonatomic, copy) NSString *namePrefix; 52 | 53 | /** 54 | 姓名后缀 55 | */ 56 | @property (nonatomic, copy) NSString *nameSuffix; 57 | 58 | /** 59 | 昵称 60 | */ 61 | @property (nonatomic, copy) NSString *nickname; 62 | 63 | /** 64 | 中间名 65 | */ 66 | @property (nonatomic, copy) NSString *middleName; 67 | 68 | /** 69 | 公司 70 | */ 71 | @property (nonatomic, copy) NSString *organizationName; 72 | 73 | /** 74 | 部门 75 | */ 76 | @property (nonatomic, copy) NSString *departmentName; 77 | 78 | /** 79 | 职位 80 | */ 81 | @property (nonatomic, copy) NSString *jobTitle; 82 | 83 | /** 84 | 备注 85 | */ 86 | @property (nonatomic, copy) NSString *note; 87 | 88 | /** 89 | 名的拼音或音标 90 | */ 91 | @property (nonatomic, copy) NSString *phoneticGivenName; 92 | 93 | /** 94 | 中间名的拼音或音标 95 | */ 96 | @property (nonatomic, copy) NSString *phoneticMiddleName; 97 | 98 | /** 99 | 姓的拼音或音标 100 | */ 101 | @property (nonatomic, copy) NSString *phoneticFamilyName; 102 | 103 | /** 104 | 头像 Data 105 | */ 106 | @property (nonatomic, copy) NSData *imageData; 107 | 108 | /** 109 | 头像图片 110 | */ 111 | @property (nonatomic, strong) UIImage *image; 112 | 113 | /** 114 | 头像的缩略图 Data 115 | */ 116 | @property (nonatomic, copy) NSData *thumbnailImageData; 117 | 118 | /** 119 | 头像缩略图片 120 | */ 121 | @property (nonatomic, strong) UIImage *thumbnailImage; 122 | 123 | /** 124 | 获取创建当前联系人的时间 125 | */ 126 | @property (nonatomic, strong) NSDate *creationDate; 127 | 128 | /** 129 | 获取最近一次修改当前联系人的时间 130 | */ 131 | @property (nonatomic, strong) NSDate *modificationDate; 132 | 133 | /** 134 | 电话号码数组 135 | */ 136 | @property (nonatomic, copy) NSArray *phones; 137 | 138 | /** 139 | 邮箱数组 140 | */ 141 | @property (nonatomic, copy) NSArray *emails; 142 | 143 | /** 144 | 地址数组 145 | */ 146 | @property (nonatomic, copy) NSArray *addresses; 147 | 148 | /** 149 | 生日对象 150 | */ 151 | @property (nonatomic, strong) LJBirthday *birthday; 152 | 153 | /** 154 | 即时通讯数组 155 | */ 156 | @property (nonatomic, copy) NSArray *messages; 157 | 158 | /** 159 | 社交数组 160 | */ 161 | @property (nonatomic, copy) NSArray *socials; 162 | 163 | /** 164 | 关联人数组 165 | */ 166 | @property (nonatomic, copy) NSArray *relations; 167 | 168 | /** 169 | url数组 170 | */ 171 | @property (nonatomic, copy) NSArray *urls; 172 | 173 | /** 174 | 便利构造 (Contacts) 175 | 176 | @param contact 通讯录 177 | @return 对象 178 | */ 179 | - (instancetype)initWithCNContact:(CNContact *)contact; 180 | 181 | /** 182 | 便利构造 (AddressBook) 183 | 184 | @param record 记录 185 | @return 对象 186 | */ 187 | - (instancetype)initWithRecord:(ABRecordRef)record; 188 | 189 | @end 190 | 191 | /// 电话 192 | @interface LJPhone : NSObject 193 | 194 | /** 195 | 电话 196 | */ 197 | @property (nonatomic, copy) NSString *phone; 198 | 199 | /** 200 | 标签 201 | */ 202 | @property (nonatomic, copy) NSString *label; 203 | 204 | /** 205 | 便利构造 (Contacts) 206 | 207 | @param labeledValue 标签和值 208 | @return 对象 209 | */ 210 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue; 211 | 212 | /** 213 | 便利构造 (AddressBook) 214 | 215 | @param multiValue 标签和值 216 | @param index 下标 217 | @return 对象 218 | */ 219 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index; 220 | 221 | @end 222 | 223 | /// 电子邮件 224 | @interface LJEmail : NSObject 225 | 226 | /** 227 | 邮箱 228 | */ 229 | @property (nonatomic, copy) NSString *email; 230 | 231 | /** 232 | 标签 233 | */ 234 | @property (nonatomic, copy) NSString *label; 235 | 236 | /** 237 | 便利构造 (Contacts) 238 | 239 | @param labeledValue 标签和值 240 | @return 对象 241 | */ 242 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue; 243 | 244 | /** 245 | 便利构造 (AddressBook) 246 | 247 | @param multiValue 标签和值 248 | @param index 下标 249 | @return 对象 250 | */ 251 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index; 252 | 253 | @end 254 | 255 | /// 地址 256 | @interface LJAddress : NSObject 257 | 258 | /** 259 | 标签 260 | */ 261 | @property (nonatomic, copy) NSString *label; 262 | 263 | /** 264 | 街道 265 | */ 266 | @property (nonatomic, copy) NSString *street; 267 | 268 | /** 269 | 城市 270 | */ 271 | @property (nonatomic, copy) NSString *city; 272 | 273 | /** 274 | 州 275 | */ 276 | @property (nonatomic, copy) NSString *state; 277 | 278 | /** 279 | 邮政编码 280 | */ 281 | @property (nonatomic, copy) NSString *postalCode; 282 | 283 | /** 284 | 城市 285 | */ 286 | @property (nonatomic, copy) NSString *country; 287 | 288 | /** 289 | 国家代码 290 | */ 291 | @property (nonatomic, copy) NSString *ISOCountryCode; 292 | 293 | /** 294 | 标准格式化地址 295 | */ 296 | @property (nonatomic, copy) NSString *formatterAddress NS_AVAILABLE_IOS(9_0); 297 | 298 | /** 299 | 便利构造 (Contacts) 300 | 301 | @param labeledValue 标签和值 302 | @return 对象 303 | */ 304 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue; 305 | 306 | /** 307 | 便利构造 (AddressBook) 308 | 309 | @param multiValue 标签和值 310 | @param index 下标 311 | @return 对象 312 | */ 313 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index; 314 | 315 | @end 316 | 317 | /// 生日 318 | @interface LJBirthday : NSObject 319 | 320 | /** 321 | 生日日期 322 | */ 323 | @property (nonatomic, strong) NSDate *brithdayDate; 324 | 325 | /** 326 | 农历标识符(chinese) 327 | */ 328 | @property (nonatomic, copy) NSString *calendarIdentifier; 329 | 330 | /** 331 | 纪元 332 | */ 333 | @property (nonatomic, assign) NSInteger era; 334 | 335 | /** 336 | 年 337 | */ 338 | @property (nonatomic, assign) NSInteger year; 339 | 340 | /** 341 | 月 342 | */ 343 | @property (nonatomic, assign) NSInteger month; 344 | 345 | /** 346 | 日 347 | */ 348 | @property (nonatomic, assign) NSInteger day; 349 | 350 | /** 351 | 便利构造 (Contacts) 352 | 353 | @param contact 通讯录 354 | @return 对象 355 | */ 356 | - (instancetype)initWithCNContact:(CNContact *)contact; 357 | 358 | /** 359 | 便利构造 (AddressBook) 360 | 361 | @param record 记录 362 | @return 对象 363 | */ 364 | - (instancetype)initWithRecord:(ABRecordRef)record; 365 | 366 | @end 367 | 368 | /// 即时通讯 369 | @interface LJMessage : NSObject 370 | 371 | /** 372 | 即时通讯名字(QQ) 373 | */ 374 | @property (nonatomic, copy) NSString *service; 375 | 376 | /** 377 | 账号 378 | */ 379 | @property (nonatomic, copy) NSString *userName; 380 | 381 | /** 382 | 便利构造 (Contacts) 383 | 384 | @param labeledValue 标签和值 385 | @return 对象 386 | */ 387 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue; 388 | 389 | /** 390 | 便利构造 (AddressBook) 391 | 392 | @param multiValue 标签和值 393 | @param index 下标 394 | @return 对象 395 | */ 396 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index; 397 | 398 | @end 399 | 400 | /// 社交 401 | @interface LJSocialProfile : NSObject 402 | 403 | /** 404 | 社交名字(Facebook) 405 | */ 406 | @property (nonatomic, copy) NSString *service; 407 | 408 | /** 409 | 账号 410 | */ 411 | @property (nonatomic, copy) NSString *username; 412 | 413 | /** 414 | url字符串 415 | */ 416 | @property (nonatomic, copy) NSString *urlString; 417 | 418 | /** 419 | 便利构造 (Contacts) 420 | 421 | @param labeledValue 标签和值 422 | @return 对象 423 | */ 424 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue; 425 | 426 | /** 427 | 便利构造 (AddressBook) 428 | 429 | @param multiValue 标签和值 430 | @param index 下标 431 | @return 对象 432 | */ 433 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index; 434 | 435 | @end 436 | 437 | /// URL 438 | @interface LJUrlAddress : NSObject 439 | 440 | /** 441 | 标签 442 | */ 443 | @property (nonatomic, copy) NSString *label; 444 | 445 | /** 446 | url字符串 447 | */ 448 | @property (nonatomic, copy) NSString *urlString; 449 | 450 | /** 451 | 便利构造 (Contacts) 452 | 453 | @param labeledValue 标签和值 454 | @return 对象 455 | */ 456 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue; 457 | 458 | /** 459 | 便利构造 (AddressBook) 460 | 461 | @param multiValue 标签和值 462 | @param index 下标 463 | @return 对象 464 | */ 465 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index; 466 | 467 | @end 468 | 469 | /// 关联人 470 | @interface LJContactRelation : NSObject 471 | 472 | /** 473 | 标签(父亲,朋友等) 474 | */ 475 | @property (nonatomic, copy) NSString *label; 476 | 477 | /** 478 | 名字 479 | */ 480 | @property (nonatomic, copy) NSString *name; 481 | 482 | /** 483 | 便利构造 (Contacts) 484 | 485 | @param labeledValue 标签和值 486 | @return 对象 487 | */ 488 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue; 489 | 490 | /** 491 | 便利构造 (AddressBook) 492 | 493 | @param multiValue 标签和值 494 | @param index 下标 495 | @return 对象 496 | */ 497 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index; 498 | 499 | @end 500 | 501 | /// 排序分组模型 502 | @interface LJSectionPerson : NSObject 503 | 504 | @property (nonatomic, copy) NSString *key; 505 | 506 | @property (nonatomic, copy) NSArray *persons; 507 | 508 | @end 509 | -------------------------------------------------------------------------------- /LJContactManager/LJPerson.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJPerson.m 3 | // LJContactManager 4 | // 5 | // Created by LeeJay on 2017/3/22. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "LJPerson.h" 10 | #import "NSString+LJExtension.h" 11 | 12 | @implementation LJPerson 13 | 14 | - (instancetype)initWithCNContact:(CNContact *)contact 15 | { 16 | self = [super init]; 17 | if (self) 18 | { 19 | self.contactType = contact.contactType == CNContactTypePerson ? LJContactTypePerson : LJContactTypeOrigination; 20 | 21 | self.fullName = [CNContactFormatter stringFromContact:contact style:CNContactFormatterStyleFullName]; 22 | self.familyName = contact.familyName; 23 | self.givenName = contact.givenName; 24 | self.nameSuffix = contact.nameSuffix; 25 | self.namePrefix = contact.namePrefix; 26 | self.nickname = contact.nickname; 27 | self.middleName = contact.middleName; 28 | 29 | if ([contact isKeyAvailable:CNContactOrganizationNameKey]) 30 | { 31 | self.organizationName = contact.organizationName; 32 | } 33 | 34 | if ([contact isKeyAvailable:CNContactDepartmentNameKey]) 35 | { 36 | self.departmentName = contact.departmentName; 37 | } 38 | 39 | if ([contact isKeyAvailable:CNContactJobTitleKey]) 40 | { 41 | self.jobTitle = contact.jobTitle; 42 | } 43 | 44 | if ([contact isKeyAvailable:CNContactNoteKey]) 45 | { 46 | self.note = contact.note; 47 | } 48 | 49 | if ([contact isKeyAvailable:CNContactPhoneticFamilyNameKey]) 50 | { 51 | self.phoneticFamilyName = contact.phoneticFamilyName; 52 | } 53 | if ([contact isKeyAvailable:CNContactPhoneticGivenNameKey]) 54 | { 55 | self.phoneticGivenName = contact.phoneticGivenName; 56 | } 57 | 58 | if ([contact isKeyAvailable:CNContactPhoneticMiddleNameKey]) 59 | { 60 | self.phoneticMiddleName = contact.phoneticMiddleName; 61 | } 62 | 63 | if ([contact isKeyAvailable:CNContactImageDataKey]) 64 | { 65 | self.imageData = contact.imageData; 66 | self.image = [UIImage imageWithData:contact.imageData]; 67 | } 68 | 69 | if ([contact isKeyAvailable:CNContactThumbnailImageDataKey]) 70 | { 71 | self.thumbnailImageData = contact.thumbnailImageData; 72 | self.thumbnailImage = [UIImage imageWithData:contact.thumbnailImageData]; 73 | } 74 | 75 | if ([contact isKeyAvailable:CNContactPhoneNumbersKey]) 76 | { 77 | // 号码 78 | NSMutableArray *phones = [NSMutableArray array]; 79 | for (CNLabeledValue *labeledValue in contact.phoneNumbers) 80 | { 81 | LJPhone *phoneModel = [[LJPhone alloc] initWithLabeledValue:labeledValue]; 82 | [phones addObject:phoneModel]; 83 | } 84 | self.phones = phones; 85 | } 86 | 87 | if ([contact isKeyAvailable:CNContactEmailAddressesKey]) 88 | { 89 | // 电子邮件 90 | NSMutableArray *emails = [NSMutableArray array]; 91 | for (CNLabeledValue *labeledValue in contact.emailAddresses) 92 | { 93 | LJEmail *emailModel = [[LJEmail alloc] initWithLabeledValue:labeledValue]; 94 | [emails addObject:emailModel]; 95 | } 96 | self.emails = emails; 97 | } 98 | 99 | if ([contact isKeyAvailable:CNContactPostalAddressesKey]) 100 | { 101 | // 地址 102 | NSMutableArray *addresses = [NSMutableArray array]; 103 | for (CNLabeledValue *labeledValue in contact.postalAddresses) 104 | { 105 | LJAddress *addressModel = [[LJAddress alloc] initWithLabeledValue:labeledValue]; 106 | [addresses addObject:addressModel]; 107 | } 108 | self.addresses = addresses; 109 | } 110 | 111 | // 生日 112 | LJBirthday *birthday = [[LJBirthday alloc] initWithCNContact:contact]; 113 | self.birthday = birthday; 114 | 115 | if ([contact isKeyAvailable:CNContactInstantMessageAddressesKey]) 116 | { 117 | // 即时通讯 118 | NSMutableArray *messages = [NSMutableArray array]; 119 | for (CNLabeledValue *labeledValue in contact.instantMessageAddresses) 120 | { 121 | LJMessage *messageModel = [[LJMessage alloc] initWithLabeledValue:labeledValue]; 122 | [messages addObject:messageModel]; 123 | } 124 | self.messages = messages; 125 | } 126 | 127 | if ([contact isKeyAvailable:CNContactSocialProfilesKey]) 128 | { 129 | // 社交 130 | NSMutableArray *socials = [NSMutableArray array]; 131 | for (CNLabeledValue *labeledValue in contact.socialProfiles) 132 | { 133 | LJSocialProfile *socialModel = [[LJSocialProfile alloc] initWithLabeledValue:labeledValue]; 134 | [socials addObject:socialModel]; 135 | } 136 | self.socials = socials; 137 | } 138 | 139 | if ([contact isKeyAvailable:CNContactRelationsKey]) 140 | { 141 | // 关联人 142 | NSMutableArray *relations = [NSMutableArray array]; 143 | for (CNLabeledValue *labeledValue in contact.contactRelations) 144 | { 145 | LJContactRelation *relationModel = [[LJContactRelation alloc] initWithLabeledValue:labeledValue]; 146 | [relations addObject:relationModel]; 147 | } 148 | self.relations = relations; 149 | } 150 | 151 | if ([contact isKeyAvailable:CNContactUrlAddressesKey]) 152 | { 153 | // URL 154 | NSMutableArray *urlAddresses = [NSMutableArray array]; 155 | for (CNLabeledValue *labeledValue in contact.urlAddresses) 156 | { 157 | LJUrlAddress *urlModel = [[LJUrlAddress alloc] initWithLabeledValue:labeledValue]; 158 | [urlAddresses addObject:urlModel]; 159 | } 160 | self.urls = urlAddresses; 161 | } 162 | } 163 | return self; 164 | } 165 | 166 | - (instancetype)initWithRecord:(ABRecordRef)record 167 | { 168 | self = [super init]; 169 | if (self) 170 | { 171 | CFNumberRef type = ABRecordCopyValue(record, kABPersonKindProperty); 172 | self.contactType = type == kABPersonKindPerson ? LJContactTypePerson : LJContactTypeOrigination; 173 | CFRelease(type); 174 | NSString *fullName = CFBridgingRelease(ABRecordCopyCompositeName(record)); 175 | NSString *firstName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonFirstNameProperty)); 176 | NSString *lastName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonLastNameProperty)); 177 | 178 | 179 | NSString *namePrefix = CFBridgingRelease(ABRecordCopyValue(record, kABPersonPrefixProperty)); 180 | NSString *nameSuffix = CFBridgingRelease(ABRecordCopyValue(record, kABPersonSuffixProperty)); 181 | NSString *nickname = CFBridgingRelease(ABRecordCopyValue(record, kABPersonNicknameProperty)); 182 | NSString *middleName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonMiddleNameProperty)); 183 | NSString *organizationName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonOrganizationProperty)); 184 | NSString *departmentName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonDepartmentProperty)); 185 | NSString *jobTitle = CFBridgingRelease(ABRecordCopyValue(record, kABPersonJobTitleProperty)); 186 | NSString *note = CFBridgingRelease(ABRecordCopyValue(record, kABPersonNoteProperty)); 187 | NSString *phoneticFamilyName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonFirstNamePhoneticProperty)); 188 | NSString *phoneticGivenName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonLastNamePhoneticProperty)); 189 | NSString *phoneticMiddleName = CFBridgingRelease(ABRecordCopyValue(record, kABPersonMiddleNamePhoneticProperty)); 190 | 191 | NSData *imageData = CFBridgingRelease(ABPersonCopyImageDataWithFormat(record, kABPersonImageFormatOriginalSize)); 192 | 193 | NSData *thumbnailImageData = CFBridgingRelease(ABPersonCopyImageDataWithFormat(record, kABPersonImageFormatThumbnail)); 194 | 195 | NSDate *creationDate = CFBridgingRelease(ABRecordCopyValue(record, kABPersonCreationDateProperty)); 196 | NSDate *modificationDate = CFBridgingRelease(ABRecordCopyValue(record, kABPersonModificationDateProperty)); 197 | 198 | self.fullName = fullName; 199 | self.familyName = firstName; 200 | self.givenName = lastName; 201 | self.namePrefix = namePrefix; 202 | self.nameSuffix = nameSuffix; 203 | self.nickname = nickname; 204 | self.middleName = middleName; 205 | self.organizationName = organizationName; 206 | self.departmentName = departmentName; 207 | self.jobTitle = jobTitle; 208 | self.note = note; 209 | self.phoneticFamilyName = phoneticFamilyName; 210 | self.phoneticMiddleName = phoneticMiddleName; 211 | self.phoneticGivenName = phoneticGivenName; 212 | self.imageData = imageData; 213 | self.image = [UIImage imageWithData:imageData]; 214 | self.thumbnailImageData = thumbnailImageData; 215 | self.thumbnailImage = [UIImage imageWithData:thumbnailImageData]; 216 | self.creationDate = creationDate; 217 | self.modificationDate = modificationDate; 218 | 219 | // 号码 220 | ABMultiValueRef multiPhones = ABRecordCopyValue(record, kABPersonPhoneProperty); 221 | CFIndex phoneCount = ABMultiValueGetCount(multiPhones); 222 | NSMutableArray *phones = [NSMutableArray array]; 223 | for (CFIndex i = 0; i < phoneCount; i++) 224 | { 225 | LJPhone *phoneModel = [[LJPhone alloc] initWithMultiValue:multiPhones index:i]; 226 | [phones addObject:phoneModel]; 227 | } 228 | CFRelease(multiPhones); 229 | self.phones = phones; 230 | 231 | // 电子邮件 232 | ABMultiValueRef multiEmails = ABRecordCopyValue(record, kABPersonEmailProperty); 233 | CFIndex emailCount = ABMultiValueGetCount(multiEmails); 234 | NSMutableArray *emails = [NSMutableArray array]; 235 | for (CFIndex i = 0; i < emailCount; i++) 236 | { 237 | LJEmail *emailModel = [[LJEmail alloc] initWithMultiValue:multiEmails index:i]; 238 | [emails addObject:emailModel]; 239 | } 240 | CFRelease(multiEmails); 241 | self.emails = emails; 242 | 243 | // 地址 244 | ABMultiValueRef multiAddresses = ABRecordCopyValue(record, kABPersonAddressProperty); 245 | CFIndex addressCount = ABMultiValueGetCount(multiAddresses); 246 | NSMutableArray *addresses = [NSMutableArray array]; 247 | for (CFIndex i = 0; i < addressCount; i++) 248 | { 249 | LJAddress *addressModel = [[LJAddress alloc] initWithMultiValue:multiAddresses index:i]; 250 | [addresses addObject:addressModel]; 251 | } 252 | CFRelease(multiAddresses); 253 | self.addresses = addresses; 254 | 255 | // 生日 256 | LJBirthday *birthday = [[LJBirthday alloc] initWithRecord:record]; 257 | self.birthday = birthday; 258 | 259 | // 即时通讯 260 | ABMultiValueRef multiMessages = ABRecordCopyValue(record, kABPersonInstantMessageProperty); 261 | CFIndex messageCount = ABMultiValueGetCount(multiMessages); 262 | NSMutableArray *messages = [NSMutableArray array]; 263 | for (CFIndex i = 0; i < messageCount; i++) 264 | { 265 | LJMessage *messageModel = [[LJMessage alloc] initWithMultiValue:multiMessages index:i]; 266 | [messages addObject:messageModel]; 267 | } 268 | CFRelease(multiMessages); 269 | self.messages = messages; 270 | 271 | // 社交 272 | ABMultiValueRef multiSocials = ABRecordCopyValue(record, kABPersonSocialProfileProperty); 273 | CFIndex socialCount = ABMultiValueGetCount(multiSocials); 274 | NSMutableArray *socials = [NSMutableArray array]; 275 | for (CFIndex i = 0; i < socialCount; i++) 276 | { 277 | LJSocialProfile *socialModel = [[LJSocialProfile alloc] initWithMultiValue:multiSocials index:i]; 278 | [socials addObject:socialModel]; 279 | } 280 | CFRelease(multiSocials); 281 | self.socials = socials; 282 | 283 | // 关联人 284 | ABMultiValueRef multiRelations = ABRecordCopyValue(record, kABPersonRelatedNamesProperty); 285 | CFIndex relationCount = ABMultiValueGetCount(multiRelations); 286 | NSMutableArray *relations = [NSMutableArray array]; 287 | for (CFIndex i = 0; i < relationCount; i++) 288 | { 289 | LJContactRelation *relationModel = [[LJContactRelation alloc] initWithMultiValue:multiRelations index:i]; 290 | [relations addObject:relationModel]; 291 | } 292 | CFRelease(multiRelations); 293 | self.relations = relations; 294 | 295 | // URL 296 | ABMultiValueRef multiURLs = ABRecordCopyValue(record, kABPersonURLProperty); 297 | CFIndex urlCount = ABMultiValueGetCount(multiURLs); 298 | NSMutableArray *urls = [NSMutableArray array]; 299 | for (CFIndex i = 0; i < urlCount; i ++) 300 | { 301 | LJUrlAddress *urlModel = [[LJUrlAddress alloc] initWithMultiValue:multiURLs index:i]; 302 | [urls addObject:urlModel]; 303 | } 304 | CFRelease(multiURLs); 305 | self.urls = urls; 306 | } 307 | return self; 308 | } 309 | 310 | @end 311 | 312 | @implementation LJPhone 313 | 314 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue 315 | { 316 | self = [super init]; 317 | if (self) 318 | { 319 | CNPhoneNumber *phoneValue = labeledValue.value; 320 | NSString *phoneNumber = phoneValue.stringValue; 321 | self.phone = [NSString lj_filterSpecialString:phoneNumber]; 322 | self.label = [CNLabeledValue localizedStringForLabel:labeledValue.label]; 323 | } 324 | return self; 325 | } 326 | 327 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index 328 | { 329 | self = [super init]; 330 | if (self) 331 | { 332 | NSString *label = (NSString *)CFBridgingRelease(ABMultiValueCopyLabelAtIndex(multiValue, index)); 333 | CFStringRef localizedRef = ABAddressBookCopyLocalizedLabel((__bridge CFStringRef)label); 334 | NSString *localized = nil; 335 | if (localizedRef) 336 | { 337 | localized = [NSString stringWithString:(__bridge NSString *)localizedRef]; 338 | CFRelease(localizedRef); 339 | } 340 | self.label = localized; 341 | NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(multiValue, index)); 342 | self.phone = [NSString lj_filterSpecialString:phoneNumber]; 343 | } 344 | return self; 345 | } 346 | 347 | + (NSString *)localizedLabel:(NSString*)label 348 | { 349 | CFStringRef localizedRef = ABAddressBookCopyLocalizedLabel((__bridge CFStringRef)label); 350 | NSString *localized = nil; 351 | if (localizedRef){ 352 | localized = [NSString stringWithString:(__bridge NSString*)localizedRef]; 353 | CFRelease(localizedRef); 354 | } 355 | return localized; 356 | } 357 | 358 | @end 359 | 360 | @implementation LJEmail 361 | 362 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue 363 | { 364 | self = [super init]; 365 | if (self) 366 | { 367 | self.label = [CNLabeledValue localizedStringForLabel:labeledValue.label]; 368 | self.email = labeledValue.value; 369 | } 370 | return self; 371 | } 372 | 373 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index 374 | { 375 | self = [super init]; 376 | if (self) 377 | { 378 | NSString *label = (NSString *)CFBridgingRelease(ABMultiValueCopyLabelAtIndex(multiValue, index)); 379 | CFStringRef localizedRef = ABAddressBookCopyLocalizedLabel((__bridge CFStringRef)label); 380 | NSString *localized = nil; 381 | if (localizedRef) 382 | { 383 | localized = [NSString stringWithString:(__bridge NSString *)localizedRef]; 384 | CFRelease(localizedRef); 385 | } 386 | self.label = localized; 387 | NSString *emial = CFBridgingRelease(ABMultiValueCopyValueAtIndex(multiValue, index)); 388 | self.email = emial; 389 | } 390 | return self; 391 | } 392 | 393 | @end 394 | 395 | @implementation LJAddress 396 | 397 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue 398 | { 399 | self = [super init]; 400 | if (self) 401 | { 402 | CNPostalAddress *addressValue = labeledValue.value; 403 | self.label = [CNLabeledValue localizedStringForLabel:labeledValue.label]; 404 | self.street = addressValue.street; 405 | self.state = addressValue.state; 406 | self.city = addressValue.city; 407 | self.postalCode = addressValue.postalCode; 408 | self.country = addressValue.country; 409 | self.ISOCountryCode = addressValue.ISOCountryCode; 410 | 411 | self.formatterAddress = [CNPostalAddressFormatter stringFromPostalAddress:addressValue style:CNPostalAddressFormatterStyleMailingAddress]; 412 | } 413 | return self; 414 | } 415 | 416 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index 417 | { 418 | self = [super init]; 419 | if (self) 420 | { 421 | CFStringRef label = ABMultiValueCopyLabelAtIndex(multiValue, index); 422 | CFStringRef localLabel = ABAddressBookCopyLocalizedLabel(label); 423 | CFRelease(label); 424 | self.label = CFBridgingRelease(localLabel); 425 | 426 | NSDictionary *dict = CFBridgingRelease((ABMultiValueCopyValueAtIndex(multiValue, index))); 427 | self.country = [dict valueForKey:(__bridge NSString *)kABPersonAddressCountryKey]; 428 | self.city = [dict valueForKey:(__bridge NSString *)kABPersonAddressCityKey]; 429 | self.state = [dict valueForKey:(__bridge NSString *)kABPersonAddressStateKey]; 430 | self.street = [dict valueForKey:(__bridge NSString *)kABPersonAddressStreetKey]; 431 | self.postalCode = [dict valueForKey:(__bridge NSString *)kABPersonAddressZIPKey]; 432 | self.ISOCountryCode = [dict valueForKey:(__bridge NSString *)kABPersonAddressCountryCodeKey]; 433 | } 434 | return self; 435 | } 436 | 437 | @end 438 | 439 | @implementation LJBirthday 440 | 441 | - (instancetype)initWithCNContact:(CNContact *)contact 442 | { 443 | self = [super init]; 444 | if (self) 445 | { 446 | if ([contact isKeyAvailable:CNContactBirthdayKey]) 447 | { 448 | self.brithdayDate = contact.birthday.date; 449 | } 450 | 451 | if ([contact isKeyAvailable:CNContactNonGregorianBirthdayKey]) 452 | { 453 | self.calendarIdentifier = contact.nonGregorianBirthday.calendar.calendarIdentifier; 454 | self.era = contact.nonGregorianBirthday.era; 455 | self.day = contact.nonGregorianBirthday.day; 456 | self.month = contact.nonGregorianBirthday.month; 457 | self.year = contact.nonGregorianBirthday.year; 458 | } 459 | } 460 | return self; 461 | } 462 | 463 | - (instancetype)initWithRecord:(ABRecordRef)record 464 | { 465 | self = [super init]; 466 | if (self) 467 | { 468 | self.brithdayDate = CFBridgingRelease(ABRecordCopyValue(record, kABPersonBirthdayProperty)); 469 | 470 | NSDictionary *dict = CFBridgingRelease((ABRecordCopyValue(record, kABPersonAlternateBirthdayProperty))); 471 | self.calendarIdentifier = dict[@"calendarIdentifier"]; 472 | self.era = [(NSNumber *)dict[@"era"] integerValue]; 473 | self.year = [(NSNumber *)dict[@"year"] integerValue]; 474 | self.month = [(NSNumber *)dict[@"month"] integerValue]; 475 | self.day = [(NSNumber *)dict[@"day"] integerValue]; 476 | } 477 | return self; 478 | } 479 | 480 | @end 481 | 482 | @implementation LJMessage 483 | 484 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue 485 | { 486 | self = [super init]; 487 | if (self) 488 | { 489 | CNInstantMessageAddress *messageValue = labeledValue.value; 490 | self.service = messageValue.service; 491 | self.userName = messageValue.username; 492 | } 493 | return self; 494 | } 495 | 496 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index 497 | { 498 | self = [super init]; 499 | if (self) 500 | { 501 | NSDictionary *dict = CFBridgingRelease(ABMultiValueCopyValueAtIndex(multiValue, index)); 502 | self.service = dict[@"service"]; 503 | self.userName = dict[@"username"]; 504 | } 505 | return self; 506 | } 507 | 508 | @end 509 | 510 | @implementation LJSocialProfile 511 | 512 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue 513 | { 514 | self = [super init]; 515 | if (self) 516 | { 517 | CNSocialProfile *socialValue = labeledValue.value; 518 | self.service = socialValue.service; 519 | self.username = socialValue.username; 520 | self.urlString = socialValue.urlString; 521 | } 522 | return self; 523 | } 524 | 525 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index 526 | { 527 | self = [super init]; 528 | if (self) 529 | { 530 | NSDictionary *dict = CFBridgingRelease(ABMultiValueCopyValueAtIndex(multiValue, index)); 531 | self.service = dict[@"service"]; 532 | self.username = dict[@"username"]; 533 | self.urlString = dict[@"url"]; 534 | } 535 | return self; 536 | } 537 | 538 | @end 539 | 540 | @implementation LJUrlAddress 541 | 542 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue 543 | { 544 | self = [super init]; 545 | if (self) 546 | { 547 | self.label = [CNLabeledValue localizedStringForLabel:labeledValue.label]; 548 | self.urlString = labeledValue.value; 549 | } 550 | return self; 551 | } 552 | 553 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index 554 | { 555 | self = [super init]; 556 | if (self) 557 | { 558 | CFStringRef label = ABMultiValueCopyLabelAtIndex(multiValue, index); 559 | CFStringRef localLabel = ABAddressBookCopyLocalizedLabel(label); 560 | CFRelease(label); 561 | self.label = CFBridgingRelease(localLabel); 562 | self.urlString = CFBridgingRelease(ABMultiValueCopyValueAtIndex(multiValue, index)); 563 | } 564 | return self; 565 | } 566 | 567 | @end 568 | 569 | @implementation LJContactRelation 570 | 571 | - (instancetype)initWithLabeledValue:(CNLabeledValue *)labeledValue 572 | { 573 | self = [super init]; 574 | if (self) 575 | { 576 | CNContactRelation *relationValue = labeledValue.value; 577 | self.label = [CNLabeledValue localizedStringForLabel:labeledValue.label];; 578 | self.name = relationValue.name; 579 | } 580 | return self; 581 | } 582 | 583 | - (instancetype)initWithMultiValue:(ABMultiValueRef)multiValue index:(CFIndex)index 584 | { 585 | self = [super init]; 586 | if (self) 587 | { 588 | CFStringRef label = ABMultiValueCopyLabelAtIndex(multiValue, index); 589 | CFStringRef localLabel = ABAddressBookCopyLocalizedLabel(label); 590 | CFRelease(label); 591 | self.label = CFBridgingRelease(localLabel); 592 | self.name = CFBridgingRelease(ABMultiValueCopyValueAtIndex(multiValue, index)); 593 | } 594 | return self; 595 | } 596 | 597 | @end 598 | 599 | @implementation LJSectionPerson 600 | 601 | 602 | @end 603 | 604 | 605 | -------------------------------------------------------------------------------- /LJContactManager/LJPickerDetailDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJPickerDetailDelegate.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/4/24. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface LJPickerDetailDelegate : NSObject 14 | 15 | @property (nonatomic, copy) void (^handler) (NSString *name, NSString *phoneNum); 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LJContactManager/LJPickerDetailDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJPickerDetailDelegate.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2017/4/24. 6 | // Copyright © 2017年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "LJPickerDetailDelegate.h" 10 | 11 | @implementation LJPickerDetailDelegate 12 | 13 | #pragma mark - ABPeoplePickerNavigationControllerDelegate 14 | 15 | - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker 16 | didSelectPerson:(ABRecordRef)person 17 | property:(ABPropertyID)property 18 | identifier:(ABMultiValueIdentifier)identifier 19 | { 20 | NSString *name = CFBridgingRelease(ABRecordCopyCompositeName(person)); 21 | 22 | ABMultiValueRef multi = ABRecordCopyValue(person, kABPersonPhoneProperty); 23 | long index = ABMultiValueGetIndexForIdentifier(multi, identifier); 24 | NSString *phone = CFBridgingRelease(ABMultiValueCopyValueAtIndex(multi, index)); 25 | CFRelease(multi); 26 | 27 | if (self.handler) 28 | { 29 | self.handler(name, phone); 30 | } 31 | 32 | [peoplePicker dismissViewControllerAnimated:YES completion:nil]; 33 | } 34 | 35 | #pragma mark - CNContactPickerDelegate 36 | 37 | - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty 38 | { 39 | CNContact *contact = contactProperty.contact; 40 | 41 | NSString *name = [CNContactFormatter stringFromContact:contact style:CNContactFormatterStyleFullName]; 42 | CNPhoneNumber *phoneValue= contactProperty.value; 43 | if ([phoneValue isKindOfClass:[CNPhoneNumber class]]) 44 | { 45 | if (self.handler) 46 | { 47 | self.handler(name, phoneValue.stringValue); 48 | } 49 | } 50 | else 51 | { 52 | // 邮箱 53 | if (self.handler) 54 | { 55 | self.handler(name, (NSString *)phoneValue); 56 | } 57 | } 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /LJContactManager/NSString+LJExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+LJExtension.h 3 | // Demo 4 | // 5 | // Created by LeeJay on 2018/7/5. 6 | // Copyright © 2018年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (LJExtension) 12 | 13 | /** 14 | 去除手机号的特殊字段 15 | 16 | @param string 手机号 17 | @return 处理过的手机号 18 | */ 19 | + (NSString *)lj_filterSpecialString:(NSString *)string; 20 | 21 | /** 22 | 字符串转拼音 23 | 24 | @param string 字符串 25 | @return 拼音 26 | */ 27 | + (NSString *)lj_pinyinForString:(NSString *)string; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /LJContactManager/NSString+LJExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+LJExtension.m 3 | // Demo 4 | // 5 | // Created by LeeJay on 2018/7/5. 6 | // Copyright © 2018年 LeeJay. All rights reserved. 7 | // 8 | 9 | #import "NSString+LJExtension.h" 10 | 11 | @implementation NSString (LJExtension) 12 | 13 | + (NSString *)lj_filterSpecialString:(NSString *)string 14 | { 15 | if (string == nil) 16 | { 17 | return @""; 18 | } 19 | 20 | string = [string stringByReplacingOccurrencesOfString:@"+86" withString:@""]; 21 | string = [string stringByReplacingOccurrencesOfString:@"-" withString:@""]; 22 | string = [string stringByReplacingOccurrencesOfString:@"(" withString:@""]; 23 | string = [string stringByReplacingOccurrencesOfString:@")" withString:@""]; 24 | string = [string stringByReplacingOccurrencesOfString:@" " withString:@""]; 25 | string = [string stringByReplacingOccurrencesOfString:@" " withString:@""]; 26 | return string; 27 | } 28 | 29 | + (NSString *)lj_pinyinForString:(NSString *)string 30 | { 31 | if (string.length == 0) 32 | { 33 | return nil; 34 | } 35 | 36 | NSMutableString *mutableString = [NSMutableString stringWithString:string]; 37 | CFStringTransform((CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, false); 38 | NSMutableString *pinyinString = [[mutableString stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:[NSLocale currentLocale]] mutableCopy]; 39 | 40 | NSString *str = [string substringToIndex:1]; 41 | 42 | // 多音字处理http://blog.csdn.net/qq_29307685/article/details/51532147 43 | if ([str isEqualToString:@"长"]) 44 | { 45 | [pinyinString replaceCharactersInRange:NSMakeRange(0, 5) withString:@"chang"]; 46 | } 47 | if ([str isEqualToString:@"沈"]) 48 | { 49 | [pinyinString replaceCharactersInRange:NSMakeRange(0, 4) withString:@"shen"]; 50 | } 51 | if ([str isEqualToString:@"厦"]) 52 | { 53 | [pinyinString replaceCharactersInRange:NSMakeRange(0, 3) withString:@"xia"]; 54 | } 55 | if ([str isEqualToString:@"地"]) 56 | { 57 | [pinyinString replaceCharactersInRange:NSMakeRange(0, 2) withString:@"di"]; 58 | } 59 | if ([str isEqualToString:@"重"]) 60 | { 61 | [pinyinString replaceCharactersInRange:NSMakeRange(0, 5) withString:@"chong"]; 62 | } 63 | 64 | return [[pinyinString lowercaseString] copy]; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LJContactManager 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/LJContactManager.svg?style=flat)](http://cocoadocs.org/docsets/LJContactManager) [![License](https://img.shields.io/cocoapods/l/LJContactManager.svg?style=flat)](http://cocoadocs.org/docsets/LJContactManager) [![Platform](https://img.shields.io/cocoapods/p/LJContactManager.svg?style=flat)](http://cocoadocs.org/docsets/LJContactManager) ![Language](https://img.shields.io/badge/Language-%20Objective%20C%20-blue.svg) 4 | 5 | ## 介绍 6 | 7 | LJContanctManager 是一款操作通讯录的类库,iOS 9 之前使用的是 AddressBook 和 AddressBookUI 系统库,iOS 9 之后使用苹果新推出的 Contacts 和 ContactsUI 框架,一行代码搞定通讯录。 8 | 9 | ## 安装 10 | 11 | ### CocoaPods 12 | 13 | 1. 在 Podfile 中添加 `pod 'LJContactManager'`。 14 | 2. 执行 `pod install` 或 `pod update`。 15 | 3. 导入 \。 16 | 17 | ### 手动安装 18 | 19 | 1. 下载 LJContactManager 文件夹内的所有内容。 20 | 2. 将 LJContactManager 内的源文件添加(拖放)到你的工程。 21 | 3. 导入 `LJContactManager.h`。 22 | 23 | ## 使用 24 | 25 | 主要提供以下的方法: 26 | 27 | * 选择联系人 28 | 29 | ```objc 30 | /** 31 | 选择联系人 32 | 33 | @param controller 控制器 34 | @param completcion 回调 35 | */ 36 | - (void)selectContactAtController:(UIViewController *)controller 37 | complection:(void (^)(NSString *name, NSString *phone))completcion; 38 | ``` 39 | 40 | ![](http://upload-images.jianshu.io/upload_images/1321491-f2d513a6e5c7959d.gif?imageMogr2/auto-orient/strip) 41 | 42 | * 创建新联系人 43 | 44 | ```objc 45 | /** 46 | 创建新联系人 47 | 48 | @param phoneNum 手机号 49 | @param controller 当前 Controller 50 | */ 51 | - (void)createNewContactWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller; 52 | ``` 53 | 54 | ![](http://upload-images.jianshu.io/upload_images/1321491-3f02dc899eedb2c8.gif?imageMogr2/auto-orient/strip) 55 | 56 | * 添加到现有联系人 57 | 58 | ```objc 59 | /** 60 | 添加到现有联系人 61 | 62 | @param phoneNum 手机号 63 | @param controller 当前 Controller 64 | */ 65 | - (void)addToExistingContactsWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller; 66 | ``` 67 | 68 | ![](http://upload-images.jianshu.io/upload_images/1321491-8108d6477fd5bba8.gif?imageMogr2/auto-orient/strip) 69 | 70 | * 获取联系人列表(未分组的通讯录) 71 | 72 | ```objc 73 | /** 74 | 获取联系人列表(未分组的通讯录) 75 | 76 | @param completcion 回调 77 | */ 78 | - (void)accessContactsComplection:(void (^)(BOOL succeed, NSArray *contacts))completcion; 79 | ``` 80 | 81 | ![](http://upload-images.jianshu.io/upload_images/1321491-bcc3c9ec2339a87c.gif?imageMogr2/auto-orient/strip) 82 | 83 | * 获取联系人列表(已分组的通讯录) 84 | 85 | ```objc 86 | /** 87 | 获取联系人列表(已分组的通讯录) 88 | 89 | @param completcion 回调 90 | */ 91 | - (void)accessSectionContactsComplection:(void (^)(BOOL succeed, NSArray *contacts, NSArray *keys))completcion; 92 | ``` 93 | 94 | ![](http://upload-images.jianshu.io/upload_images/1321491-a4df44dca74e8f47.gif?imageMogr2/auto-orient/strip) 95 | 96 | * 通讯录变更回调 97 | 98 | ```objc 99 | /** 100 | 通讯录变更回调 101 | */ 102 | @property (nonatomic, copy) LJContactChangeHandler contactChangeHandler; 103 | ``` 104 | 105 | ## 系统要求 106 | 107 | 该项目最低支持 iOS 8.0 和 Xcode 7.0。 108 | 109 | ## 相关文章 110 | 111 | [iOS 通讯录开发的所有姿势](http://www.jianshu.com/p/55d1c90f62c8) 112 | 113 | --------------------------------------------------------------------------------