├── .DS_Store ├── ChooseLocation.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── HelloYeah.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ ├── Sekorm.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── dashen.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── HelloYeah.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── ChooseLocation.xcscheme │ │ └── xcschememanagement.plist │ ├── Sekorm.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── ChooseLocation.xcscheme │ │ └── xcschememanagement.plist │ └── dashen.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── ChooseLocation ├── .DS_Store ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── btn_check.imageset │ │ ├── Contents.json │ │ ├── btn_check@2x.png │ │ └── btn_check@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Category │ ├── .DS_Store │ ├── UIView+Frame.h │ └── UIView+Frame.m ├── ChooseLocation │ ├── .DS_Store │ ├── Model │ │ ├── AddressItem.h │ │ └── AddressItem.m │ └── View │ │ ├── AddressTableViewCell.h │ │ ├── AddressTableViewCell.m │ │ ├── AddressTableViewCell.xib │ │ ├── AddressView.h │ │ ├── AddressView.m │ │ ├── ChooseLocationView.h │ │ └── ChooseLocationView.m ├── Cities.json ├── Info.plist ├── Tool │ ├── CitiesDataTool.h │ └── CitiesDataTool.m ├── ViewController.h ├── ViewController.m ├── fmdb │ ├── FMDB.h │ ├── FMDatabase.h │ ├── FMDatabase.m │ ├── FMDatabaseAdditions.h │ ├── FMDatabaseAdditions.m │ ├── FMDatabasePool.h │ ├── FMDatabasePool.m │ ├── FMDatabaseQueue.h │ ├── FMDatabaseQueue.m │ ├── FMResultSet.h │ ├── FMResultSet.m │ └── Info.plist └── main.m ├── ChooseLocationTests ├── ChooseLocationTests.m └── Info.plist ├── ChooseLocationUITests ├── ChooseLocationUITests.m └── Info.plist └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/.DS_Store -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B48D89D91D6AE14D00814887 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B48D89D81D6AE14D00814887 /* main.m */; }; 11 | B48D89DC1D6AE14D00814887 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B48D89DB1D6AE14D00814887 /* AppDelegate.m */; }; 12 | B48D89DF1D6AE14D00814887 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B48D89DE1D6AE14D00814887 /* ViewController.m */; }; 13 | B48D89E21D6AE14D00814887 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B48D89E01D6AE14D00814887 /* Main.storyboard */; }; 14 | B48D89E41D6AE14D00814887 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B48D89E31D6AE14D00814887 /* Assets.xcassets */; }; 15 | B48D89E71D6AE14D00814887 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B48D89E51D6AE14D00814887 /* LaunchScreen.storyboard */; }; 16 | B48D89F21D6AE14D00814887 /* ChooseLocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B48D89F11D6AE14D00814887 /* ChooseLocationTests.m */; }; 17 | B48D89FD1D6AE14D00814887 /* ChooseLocationUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = B48D89FC1D6AE14D00814887 /* ChooseLocationUITests.m */; }; 18 | B4BB8FDD1DBEED1B00EF753C /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FD21DBEED1B00EF753C /* FMDatabase.m */; }; 19 | B4BB8FDE1DBEED1B00EF753C /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FD41DBEED1B00EF753C /* FMDatabaseAdditions.m */; }; 20 | B4BB8FDF1DBEED1B00EF753C /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FD61DBEED1B00EF753C /* FMDatabasePool.m */; }; 21 | B4BB8FE01DBEED1B00EF753C /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FD81DBEED1B00EF753C /* FMDatabaseQueue.m */; }; 22 | B4BB8FE11DBEED1B00EF753C /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FDB1DBEED1B00EF753C /* FMResultSet.m */; }; 23 | B4BB8FE21DBEED1B00EF753C /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B4BB8FDC1DBEED1B00EF753C /* Info.plist */; }; 24 | B4BB8FE81DBEEEFB00EF753C /* UIView+Frame.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FE71DBEEEFB00EF753C /* UIView+Frame.m */; }; 25 | B4BB8FF51DBEEF0300EF753C /* AddressItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FEC1DBEEF0300EF753C /* AddressItem.m */; }; 26 | B4BB8FF61DBEEF0300EF753C /* AddressTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FEF1DBEEF0300EF753C /* AddressTableViewCell.m */; }; 27 | B4BB8FF71DBEEF0300EF753C /* AddressTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B4BB8FF01DBEEF0300EF753C /* AddressTableViewCell.xib */; }; 28 | B4BB8FF81DBEEF0300EF753C /* AddressView.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FF21DBEEF0300EF753C /* AddressView.m */; }; 29 | B4BB8FF91DBEEF0300EF753C /* ChooseLocationView.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB8FF41DBEEF0300EF753C /* ChooseLocationView.m */; }; 30 | B4BB8FFD1DBEF8D900EF753C /* Cities.json in Resources */ = {isa = PBXBuildFile; fileRef = B4BB8FFC1DBEF8D900EF753C /* Cities.json */; }; 31 | B4BB90011DBEF98F00EF753C /* CitiesDataTool.m in Sources */ = {isa = PBXBuildFile; fileRef = B4BB90001DBEF98F00EF753C /* CitiesDataTool.m */; }; 32 | B4BB90021DBF659D00EF753C /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B4BB8FE31DBEEDDB00EF753C /* libsqlite3.0.tbd */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | B48D89EE1D6AE14D00814887 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = B48D89CC1D6AE14D00814887 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = B48D89D31D6AE14D00814887; 41 | remoteInfo = ChooseLocation; 42 | }; 43 | B48D89F91D6AE14D00814887 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = B48D89CC1D6AE14D00814887 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = B48D89D31D6AE14D00814887; 48 | remoteInfo = ChooseLocation; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | B48D89D41D6AE14D00814887 /* ChooseLocation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChooseLocation.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | B48D89D81D6AE14D00814887 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | B48D89DA1D6AE14D00814887 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | B48D89DB1D6AE14D00814887 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | B48D89DD1D6AE14D00814887 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 58 | B48D89DE1D6AE14D00814887 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 59 | B48D89E11D6AE14D00814887 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | B48D89E31D6AE14D00814887 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 61 | B48D89E61D6AE14D00814887 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 62 | B48D89E81D6AE14D00814887 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | B48D89ED1D6AE14D00814887 /* ChooseLocationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChooseLocationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | B48D89F11D6AE14D00814887 /* ChooseLocationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChooseLocationTests.m; sourceTree = ""; }; 65 | B48D89F31D6AE14D00814887 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | B48D89F81D6AE14D00814887 /* ChooseLocationUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChooseLocationUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | B48D89FC1D6AE14D00814887 /* ChooseLocationUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChooseLocationUITests.m; sourceTree = ""; }; 68 | B48D89FE1D6AE14D00814887 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | B4BB8FD11DBEED1B00EF753C /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = ""; }; 70 | B4BB8FD21DBEED1B00EF753C /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = ""; }; 71 | B4BB8FD31DBEED1B00EF753C /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = ""; }; 72 | B4BB8FD41DBEED1B00EF753C /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseAdditions.m; sourceTree = ""; }; 73 | B4BB8FD51DBEED1B00EF753C /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabasePool.h; sourceTree = ""; }; 74 | B4BB8FD61DBEED1B00EF753C /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabasePool.m; sourceTree = ""; }; 75 | B4BB8FD71DBEED1B00EF753C /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseQueue.h; sourceTree = ""; }; 76 | B4BB8FD81DBEED1B00EF753C /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseQueue.m; sourceTree = ""; }; 77 | B4BB8FD91DBEED1B00EF753C /* FMDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDB.h; sourceTree = ""; }; 78 | B4BB8FDA1DBEED1B00EF753C /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = ""; }; 79 | B4BB8FDB1DBEED1B00EF753C /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = ""; }; 80 | B4BB8FDC1DBEED1B00EF753C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | B4BB8FE31DBEEDDB00EF753C /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; 82 | B4BB8FE61DBEEEFB00EF753C /* UIView+Frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Frame.h"; sourceTree = ""; }; 83 | B4BB8FE71DBEEEFB00EF753C /* UIView+Frame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Frame.m"; sourceTree = ""; }; 84 | B4BB8FEB1DBEEF0300EF753C /* AddressItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddressItem.h; sourceTree = ""; }; 85 | B4BB8FEC1DBEEF0300EF753C /* AddressItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddressItem.m; sourceTree = ""; }; 86 | B4BB8FEE1DBEEF0300EF753C /* AddressTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddressTableViewCell.h; sourceTree = ""; }; 87 | B4BB8FEF1DBEEF0300EF753C /* AddressTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddressTableViewCell.m; sourceTree = ""; }; 88 | B4BB8FF01DBEEF0300EF753C /* AddressTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AddressTableViewCell.xib; sourceTree = ""; }; 89 | B4BB8FF11DBEEF0300EF753C /* AddressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddressView.h; sourceTree = ""; }; 90 | B4BB8FF21DBEEF0300EF753C /* AddressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddressView.m; sourceTree = ""; }; 91 | B4BB8FF31DBEEF0300EF753C /* ChooseLocationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChooseLocationView.h; sourceTree = ""; }; 92 | B4BB8FF41DBEEF0300EF753C /* ChooseLocationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChooseLocationView.m; sourceTree = ""; }; 93 | B4BB8FFC1DBEF8D900EF753C /* Cities.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Cities.json; sourceTree = ""; }; 94 | B4BB8FFF1DBEF98F00EF753C /* CitiesDataTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CitiesDataTool.h; sourceTree = ""; }; 95 | B4BB90001DBEF98F00EF753C /* CitiesDataTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CitiesDataTool.m; sourceTree = ""; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | B48D89D11D6AE14D00814887 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | B4BB90021DBF659D00EF753C /* libsqlite3.0.tbd in Frameworks */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | B48D89EA1D6AE14D00814887 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | B48D89F51D6AE14D00814887 /* Frameworks */ = { 115 | isa = PBXFrameworksBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | B48D89CB1D6AE14D00814887 = { 125 | isa = PBXGroup; 126 | children = ( 127 | B4BB8FE31DBEEDDB00EF753C /* libsqlite3.0.tbd */, 128 | B48D89D61D6AE14D00814887 /* ChooseLocation */, 129 | B48D89F01D6AE14D00814887 /* ChooseLocationTests */, 130 | B48D89FB1D6AE14D00814887 /* ChooseLocationUITests */, 131 | B48D89D51D6AE14D00814887 /* Products */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | B48D89D51D6AE14D00814887 /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | B48D89D41D6AE14D00814887 /* ChooseLocation.app */, 139 | B48D89ED1D6AE14D00814887 /* ChooseLocationTests.xctest */, 140 | B48D89F81D6AE14D00814887 /* ChooseLocationUITests.xctest */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | B48D89D61D6AE14D00814887 /* ChooseLocation */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | B4BB8FE91DBEEF0300EF753C /* ChooseLocation */, 149 | B4BB8FE51DBEEEFB00EF753C /* Category */, 150 | B4BB8FD01DBEED1B00EF753C /* fmdb */, 151 | B4BB8FFE1DBEF97600EF753C /* Tool */, 152 | B48D89DD1D6AE14D00814887 /* ViewController.h */, 153 | B48D89DE1D6AE14D00814887 /* ViewController.m */, 154 | B48D89E31D6AE14D00814887 /* Assets.xcassets */, 155 | B48D89E81D6AE14D00814887 /* Info.plist */, 156 | B48D89D71D6AE14D00814887 /* Supporting Files */, 157 | ); 158 | path = ChooseLocation; 159 | sourceTree = ""; 160 | }; 161 | B48D89D71D6AE14D00814887 /* Supporting Files */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | B48D89E01D6AE14D00814887 /* Main.storyboard */, 165 | B48D89E51D6AE14D00814887 /* LaunchScreen.storyboard */, 166 | B48D89DA1D6AE14D00814887 /* AppDelegate.h */, 167 | B48D89DB1D6AE14D00814887 /* AppDelegate.m */, 168 | B4BB8FFC1DBEF8D900EF753C /* Cities.json */, 169 | B48D89D81D6AE14D00814887 /* main.m */, 170 | ); 171 | name = "Supporting Files"; 172 | sourceTree = ""; 173 | }; 174 | B48D89F01D6AE14D00814887 /* ChooseLocationTests */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | B48D89F11D6AE14D00814887 /* ChooseLocationTests.m */, 178 | B48D89F31D6AE14D00814887 /* Info.plist */, 179 | ); 180 | path = ChooseLocationTests; 181 | sourceTree = ""; 182 | }; 183 | B48D89FB1D6AE14D00814887 /* ChooseLocationUITests */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | B48D89FC1D6AE14D00814887 /* ChooseLocationUITests.m */, 187 | B48D89FE1D6AE14D00814887 /* Info.plist */, 188 | ); 189 | path = ChooseLocationUITests; 190 | sourceTree = ""; 191 | }; 192 | B4BB8FD01DBEED1B00EF753C /* fmdb */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | B4BB8FD11DBEED1B00EF753C /* FMDatabase.h */, 196 | B4BB8FD21DBEED1B00EF753C /* FMDatabase.m */, 197 | B4BB8FD31DBEED1B00EF753C /* FMDatabaseAdditions.h */, 198 | B4BB8FD41DBEED1B00EF753C /* FMDatabaseAdditions.m */, 199 | B4BB8FD51DBEED1B00EF753C /* FMDatabasePool.h */, 200 | B4BB8FD61DBEED1B00EF753C /* FMDatabasePool.m */, 201 | B4BB8FD71DBEED1B00EF753C /* FMDatabaseQueue.h */, 202 | B4BB8FD81DBEED1B00EF753C /* FMDatabaseQueue.m */, 203 | B4BB8FD91DBEED1B00EF753C /* FMDB.h */, 204 | B4BB8FDA1DBEED1B00EF753C /* FMResultSet.h */, 205 | B4BB8FDB1DBEED1B00EF753C /* FMResultSet.m */, 206 | B4BB8FDC1DBEED1B00EF753C /* Info.plist */, 207 | ); 208 | path = fmdb; 209 | sourceTree = ""; 210 | }; 211 | B4BB8FE51DBEEEFB00EF753C /* Category */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | B4BB8FE61DBEEEFB00EF753C /* UIView+Frame.h */, 215 | B4BB8FE71DBEEEFB00EF753C /* UIView+Frame.m */, 216 | ); 217 | path = Category; 218 | sourceTree = ""; 219 | }; 220 | B4BB8FE91DBEEF0300EF753C /* ChooseLocation */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | B4BB8FEA1DBEEF0300EF753C /* Model */, 224 | B4BB8FED1DBEEF0300EF753C /* View */, 225 | ); 226 | path = ChooseLocation; 227 | sourceTree = ""; 228 | }; 229 | B4BB8FEA1DBEEF0300EF753C /* Model */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | B4BB8FEB1DBEEF0300EF753C /* AddressItem.h */, 233 | B4BB8FEC1DBEEF0300EF753C /* AddressItem.m */, 234 | ); 235 | path = Model; 236 | sourceTree = ""; 237 | }; 238 | B4BB8FED1DBEEF0300EF753C /* View */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | B4BB8FF31DBEEF0300EF753C /* ChooseLocationView.h */, 242 | B4BB8FF41DBEEF0300EF753C /* ChooseLocationView.m */, 243 | B4BB8FF11DBEEF0300EF753C /* AddressView.h */, 244 | B4BB8FF21DBEEF0300EF753C /* AddressView.m */, 245 | B4BB8FEE1DBEEF0300EF753C /* AddressTableViewCell.h */, 246 | B4BB8FEF1DBEEF0300EF753C /* AddressTableViewCell.m */, 247 | B4BB8FF01DBEEF0300EF753C /* AddressTableViewCell.xib */, 248 | ); 249 | path = View; 250 | sourceTree = ""; 251 | }; 252 | B4BB8FFE1DBEF97600EF753C /* Tool */ = { 253 | isa = PBXGroup; 254 | children = ( 255 | B4BB8FFF1DBEF98F00EF753C /* CitiesDataTool.h */, 256 | B4BB90001DBEF98F00EF753C /* CitiesDataTool.m */, 257 | ); 258 | path = Tool; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXGroup section */ 262 | 263 | /* Begin PBXNativeTarget section */ 264 | B48D89D31D6AE14D00814887 /* ChooseLocation */ = { 265 | isa = PBXNativeTarget; 266 | buildConfigurationList = B48D8A011D6AE14D00814887 /* Build configuration list for PBXNativeTarget "ChooseLocation" */; 267 | buildPhases = ( 268 | B48D89D01D6AE14D00814887 /* Sources */, 269 | B48D89D11D6AE14D00814887 /* Frameworks */, 270 | B48D89D21D6AE14D00814887 /* Resources */, 271 | ); 272 | buildRules = ( 273 | ); 274 | dependencies = ( 275 | ); 276 | name = ChooseLocation; 277 | productName = ChooseLocation; 278 | productReference = B48D89D41D6AE14D00814887 /* ChooseLocation.app */; 279 | productType = "com.apple.product-type.application"; 280 | }; 281 | B48D89EC1D6AE14D00814887 /* ChooseLocationTests */ = { 282 | isa = PBXNativeTarget; 283 | buildConfigurationList = B48D8A041D6AE14D00814887 /* Build configuration list for PBXNativeTarget "ChooseLocationTests" */; 284 | buildPhases = ( 285 | B48D89E91D6AE14D00814887 /* Sources */, 286 | B48D89EA1D6AE14D00814887 /* Frameworks */, 287 | B48D89EB1D6AE14D00814887 /* Resources */, 288 | ); 289 | buildRules = ( 290 | ); 291 | dependencies = ( 292 | B48D89EF1D6AE14D00814887 /* PBXTargetDependency */, 293 | ); 294 | name = ChooseLocationTests; 295 | productName = ChooseLocationTests; 296 | productReference = B48D89ED1D6AE14D00814887 /* ChooseLocationTests.xctest */; 297 | productType = "com.apple.product-type.bundle.unit-test"; 298 | }; 299 | B48D89F71D6AE14D00814887 /* ChooseLocationUITests */ = { 300 | isa = PBXNativeTarget; 301 | buildConfigurationList = B48D8A071D6AE14D00814887 /* Build configuration list for PBXNativeTarget "ChooseLocationUITests" */; 302 | buildPhases = ( 303 | B48D89F41D6AE14D00814887 /* Sources */, 304 | B48D89F51D6AE14D00814887 /* Frameworks */, 305 | B48D89F61D6AE14D00814887 /* Resources */, 306 | ); 307 | buildRules = ( 308 | ); 309 | dependencies = ( 310 | B48D89FA1D6AE14D00814887 /* PBXTargetDependency */, 311 | ); 312 | name = ChooseLocationUITests; 313 | productName = ChooseLocationUITests; 314 | productReference = B48D89F81D6AE14D00814887 /* ChooseLocationUITests.xctest */; 315 | productType = "com.apple.product-type.bundle.ui-testing"; 316 | }; 317 | /* End PBXNativeTarget section */ 318 | 319 | /* Begin PBXProject section */ 320 | B48D89CC1D6AE14D00814887 /* Project object */ = { 321 | isa = PBXProject; 322 | attributes = { 323 | LastUpgradeCheck = 0720; 324 | ORGANIZATIONNAME = HY; 325 | TargetAttributes = { 326 | B48D89D31D6AE14D00814887 = { 327 | CreatedOnToolsVersion = 7.2; 328 | DevelopmentTeam = HQVNC23RTF; 329 | ProvisioningStyle = Manual; 330 | }; 331 | B48D89EC1D6AE14D00814887 = { 332 | CreatedOnToolsVersion = 7.2; 333 | TestTargetID = B48D89D31D6AE14D00814887; 334 | }; 335 | B48D89F71D6AE14D00814887 = { 336 | CreatedOnToolsVersion = 7.2; 337 | TestTargetID = B48D89D31D6AE14D00814887; 338 | }; 339 | }; 340 | }; 341 | buildConfigurationList = B48D89CF1D6AE14D00814887 /* Build configuration list for PBXProject "ChooseLocation" */; 342 | compatibilityVersion = "Xcode 3.2"; 343 | developmentRegion = English; 344 | hasScannedForEncodings = 0; 345 | knownRegions = ( 346 | en, 347 | Base, 348 | ); 349 | mainGroup = B48D89CB1D6AE14D00814887; 350 | productRefGroup = B48D89D51D6AE14D00814887 /* Products */; 351 | projectDirPath = ""; 352 | projectRoot = ""; 353 | targets = ( 354 | B48D89D31D6AE14D00814887 /* ChooseLocation */, 355 | B48D89EC1D6AE14D00814887 /* ChooseLocationTests */, 356 | B48D89F71D6AE14D00814887 /* ChooseLocationUITests */, 357 | ); 358 | }; 359 | /* End PBXProject section */ 360 | 361 | /* Begin PBXResourcesBuildPhase section */ 362 | B48D89D21D6AE14D00814887 /* Resources */ = { 363 | isa = PBXResourcesBuildPhase; 364 | buildActionMask = 2147483647; 365 | files = ( 366 | B48D89E71D6AE14D00814887 /* LaunchScreen.storyboard in Resources */, 367 | B4BB8FFD1DBEF8D900EF753C /* Cities.json in Resources */, 368 | B48D89E41D6AE14D00814887 /* Assets.xcassets in Resources */, 369 | B48D89E21D6AE14D00814887 /* Main.storyboard in Resources */, 370 | B4BB8FF71DBEEF0300EF753C /* AddressTableViewCell.xib in Resources */, 371 | B4BB8FE21DBEED1B00EF753C /* Info.plist in Resources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | B48D89EB1D6AE14D00814887 /* Resources */ = { 376 | isa = PBXResourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | B48D89F61D6AE14D00814887 /* Resources */ = { 383 | isa = PBXResourcesBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | }; 389 | /* End PBXResourcesBuildPhase section */ 390 | 391 | /* Begin PBXSourcesBuildPhase section */ 392 | B48D89D01D6AE14D00814887 /* Sources */ = { 393 | isa = PBXSourcesBuildPhase; 394 | buildActionMask = 2147483647; 395 | files = ( 396 | B48D89DF1D6AE14D00814887 /* ViewController.m in Sources */, 397 | B4BB8FF51DBEEF0300EF753C /* AddressItem.m in Sources */, 398 | B4BB8FDD1DBEED1B00EF753C /* FMDatabase.m in Sources */, 399 | B4BB8FF61DBEEF0300EF753C /* AddressTableViewCell.m in Sources */, 400 | B4BB8FE01DBEED1B00EF753C /* FMDatabaseQueue.m in Sources */, 401 | B4BB8FE81DBEEEFB00EF753C /* UIView+Frame.m in Sources */, 402 | B4BB8FE11DBEED1B00EF753C /* FMResultSet.m in Sources */, 403 | B4BB8FDF1DBEED1B00EF753C /* FMDatabasePool.m in Sources */, 404 | B48D89DC1D6AE14D00814887 /* AppDelegate.m in Sources */, 405 | B4BB8FF81DBEEF0300EF753C /* AddressView.m in Sources */, 406 | B48D89D91D6AE14D00814887 /* main.m in Sources */, 407 | B4BB8FDE1DBEED1B00EF753C /* FMDatabaseAdditions.m in Sources */, 408 | B4BB90011DBEF98F00EF753C /* CitiesDataTool.m in Sources */, 409 | B4BB8FF91DBEEF0300EF753C /* ChooseLocationView.m in Sources */, 410 | ); 411 | runOnlyForDeploymentPostprocessing = 0; 412 | }; 413 | B48D89E91D6AE14D00814887 /* Sources */ = { 414 | isa = PBXSourcesBuildPhase; 415 | buildActionMask = 2147483647; 416 | files = ( 417 | B48D89F21D6AE14D00814887 /* ChooseLocationTests.m in Sources */, 418 | ); 419 | runOnlyForDeploymentPostprocessing = 0; 420 | }; 421 | B48D89F41D6AE14D00814887 /* Sources */ = { 422 | isa = PBXSourcesBuildPhase; 423 | buildActionMask = 2147483647; 424 | files = ( 425 | B48D89FD1D6AE14D00814887 /* ChooseLocationUITests.m in Sources */, 426 | ); 427 | runOnlyForDeploymentPostprocessing = 0; 428 | }; 429 | /* End PBXSourcesBuildPhase section */ 430 | 431 | /* Begin PBXTargetDependency section */ 432 | B48D89EF1D6AE14D00814887 /* PBXTargetDependency */ = { 433 | isa = PBXTargetDependency; 434 | target = B48D89D31D6AE14D00814887 /* ChooseLocation */; 435 | targetProxy = B48D89EE1D6AE14D00814887 /* PBXContainerItemProxy */; 436 | }; 437 | B48D89FA1D6AE14D00814887 /* PBXTargetDependency */ = { 438 | isa = PBXTargetDependency; 439 | target = B48D89D31D6AE14D00814887 /* ChooseLocation */; 440 | targetProxy = B48D89F91D6AE14D00814887 /* PBXContainerItemProxy */; 441 | }; 442 | /* End PBXTargetDependency section */ 443 | 444 | /* Begin PBXVariantGroup section */ 445 | B48D89E01D6AE14D00814887 /* Main.storyboard */ = { 446 | isa = PBXVariantGroup; 447 | children = ( 448 | B48D89E11D6AE14D00814887 /* Base */, 449 | ); 450 | name = Main.storyboard; 451 | sourceTree = ""; 452 | }; 453 | B48D89E51D6AE14D00814887 /* LaunchScreen.storyboard */ = { 454 | isa = PBXVariantGroup; 455 | children = ( 456 | B48D89E61D6AE14D00814887 /* Base */, 457 | ); 458 | name = LaunchScreen.storyboard; 459 | sourceTree = ""; 460 | }; 461 | /* End PBXVariantGroup section */ 462 | 463 | /* Begin XCBuildConfiguration section */ 464 | B48D89FF1D6AE14D00814887 /* Debug */ = { 465 | isa = XCBuildConfiguration; 466 | buildSettings = { 467 | ALWAYS_SEARCH_USER_PATHS = NO; 468 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 469 | CLANG_CXX_LIBRARY = "libc++"; 470 | CLANG_ENABLE_MODULES = YES; 471 | CLANG_ENABLE_OBJC_ARC = YES; 472 | CLANG_WARN_BOOL_CONVERSION = YES; 473 | CLANG_WARN_CONSTANT_CONVERSION = YES; 474 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 475 | CLANG_WARN_EMPTY_BODY = YES; 476 | CLANG_WARN_ENUM_CONVERSION = YES; 477 | CLANG_WARN_INT_CONVERSION = YES; 478 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 479 | CLANG_WARN_UNREACHABLE_CODE = YES; 480 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 481 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 482 | COPY_PHASE_STRIP = NO; 483 | DEBUG_INFORMATION_FORMAT = dwarf; 484 | ENABLE_STRICT_OBJC_MSGSEND = YES; 485 | ENABLE_TESTABILITY = YES; 486 | GCC_C_LANGUAGE_STANDARD = gnu99; 487 | GCC_DYNAMIC_NO_PIC = NO; 488 | GCC_NO_COMMON_BLOCKS = YES; 489 | GCC_OPTIMIZATION_LEVEL = 0; 490 | GCC_PREPROCESSOR_DEFINITIONS = ( 491 | "DEBUG=1", 492 | "$(inherited)", 493 | ); 494 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 495 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 496 | GCC_WARN_UNDECLARED_SELECTOR = YES; 497 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 498 | GCC_WARN_UNUSED_FUNCTION = YES; 499 | GCC_WARN_UNUSED_VARIABLE = YES; 500 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 501 | MTL_ENABLE_DEBUG_INFO = YES; 502 | ONLY_ACTIVE_ARCH = YES; 503 | SDKROOT = iphoneos; 504 | }; 505 | name = Debug; 506 | }; 507 | B48D8A001D6AE14D00814887 /* Release */ = { 508 | isa = XCBuildConfiguration; 509 | buildSettings = { 510 | ALWAYS_SEARCH_USER_PATHS = NO; 511 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 512 | CLANG_CXX_LIBRARY = "libc++"; 513 | CLANG_ENABLE_MODULES = YES; 514 | CLANG_ENABLE_OBJC_ARC = YES; 515 | CLANG_WARN_BOOL_CONVERSION = YES; 516 | CLANG_WARN_CONSTANT_CONVERSION = YES; 517 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 518 | CLANG_WARN_EMPTY_BODY = YES; 519 | CLANG_WARN_ENUM_CONVERSION = YES; 520 | CLANG_WARN_INT_CONVERSION = YES; 521 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 522 | CLANG_WARN_UNREACHABLE_CODE = YES; 523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 524 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 525 | COPY_PHASE_STRIP = NO; 526 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 527 | ENABLE_NS_ASSERTIONS = NO; 528 | ENABLE_STRICT_OBJC_MSGSEND = YES; 529 | GCC_C_LANGUAGE_STANDARD = gnu99; 530 | GCC_NO_COMMON_BLOCKS = YES; 531 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 532 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 533 | GCC_WARN_UNDECLARED_SELECTOR = YES; 534 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 535 | GCC_WARN_UNUSED_FUNCTION = YES; 536 | GCC_WARN_UNUSED_VARIABLE = YES; 537 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 538 | MTL_ENABLE_DEBUG_INFO = NO; 539 | SDKROOT = iphoneos; 540 | VALIDATE_PRODUCT = YES; 541 | }; 542 | name = Release; 543 | }; 544 | B48D8A021D6AE14D00814887 /* Debug */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 548 | CODE_SIGN_IDENTITY = "iPhone Developer"; 549 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 550 | CODE_SIGN_STYLE = Manual; 551 | DEVELOPMENT_TEAM = HQVNC23RTF; 552 | INFOPLIST_FILE = ChooseLocation/Info.plist; 553 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 554 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 555 | PRODUCT_BUNDLE_IDENTIFIER = com.sekorm.SinfoEp; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | PROVISIONING_PROFILE = "4f86f9c7-84ec-4bdf-8349-c9127637d677"; 558 | PROVISIONING_PROFILE_SPECIFIER = SinfoEpInHouse; 559 | }; 560 | name = Debug; 561 | }; 562 | B48D8A031D6AE14D00814887 /* Release */ = { 563 | isa = XCBuildConfiguration; 564 | buildSettings = { 565 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 566 | CODE_SIGN_IDENTITY = "iPhone Developer"; 567 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 568 | CODE_SIGN_STYLE = Manual; 569 | DEVELOPMENT_TEAM = HQVNC23RTF; 570 | INFOPLIST_FILE = ChooseLocation/Info.plist; 571 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 572 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 573 | PRODUCT_BUNDLE_IDENTIFIER = com.sekorm.SinfoEp; 574 | PRODUCT_NAME = "$(TARGET_NAME)"; 575 | PROVISIONING_PROFILE = "4f86f9c7-84ec-4bdf-8349-c9127637d677"; 576 | PROVISIONING_PROFILE_SPECIFIER = SinfoEpInHouse; 577 | }; 578 | name = Release; 579 | }; 580 | B48D8A051D6AE14D00814887 /* Debug */ = { 581 | isa = XCBuildConfiguration; 582 | buildSettings = { 583 | BUNDLE_LOADER = "$(TEST_HOST)"; 584 | INFOPLIST_FILE = ChooseLocationTests/Info.plist; 585 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 586 | PRODUCT_BUNDLE_IDENTIFIER = com.yeliang.ChooseLocationTests; 587 | PRODUCT_NAME = "$(TARGET_NAME)"; 588 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChooseLocation.app/ChooseLocation"; 589 | }; 590 | name = Debug; 591 | }; 592 | B48D8A061D6AE14D00814887 /* Release */ = { 593 | isa = XCBuildConfiguration; 594 | buildSettings = { 595 | BUNDLE_LOADER = "$(TEST_HOST)"; 596 | INFOPLIST_FILE = ChooseLocationTests/Info.plist; 597 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 598 | PRODUCT_BUNDLE_IDENTIFIER = com.yeliang.ChooseLocationTests; 599 | PRODUCT_NAME = "$(TARGET_NAME)"; 600 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChooseLocation.app/ChooseLocation"; 601 | }; 602 | name = Release; 603 | }; 604 | B48D8A081D6AE14D00814887 /* Debug */ = { 605 | isa = XCBuildConfiguration; 606 | buildSettings = { 607 | INFOPLIST_FILE = ChooseLocationUITests/Info.plist; 608 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 609 | PRODUCT_BUNDLE_IDENTIFIER = com.yeliang.ChooseLocationUITests; 610 | PRODUCT_NAME = "$(TARGET_NAME)"; 611 | TEST_TARGET_NAME = ChooseLocation; 612 | USES_XCTRUNNER = YES; 613 | }; 614 | name = Debug; 615 | }; 616 | B48D8A091D6AE14D00814887 /* Release */ = { 617 | isa = XCBuildConfiguration; 618 | buildSettings = { 619 | INFOPLIST_FILE = ChooseLocationUITests/Info.plist; 620 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 621 | PRODUCT_BUNDLE_IDENTIFIER = com.yeliang.ChooseLocationUITests; 622 | PRODUCT_NAME = "$(TARGET_NAME)"; 623 | TEST_TARGET_NAME = ChooseLocation; 624 | USES_XCTRUNNER = YES; 625 | }; 626 | name = Release; 627 | }; 628 | /* End XCBuildConfiguration section */ 629 | 630 | /* Begin XCConfigurationList section */ 631 | B48D89CF1D6AE14D00814887 /* Build configuration list for PBXProject "ChooseLocation" */ = { 632 | isa = XCConfigurationList; 633 | buildConfigurations = ( 634 | B48D89FF1D6AE14D00814887 /* Debug */, 635 | B48D8A001D6AE14D00814887 /* Release */, 636 | ); 637 | defaultConfigurationIsVisible = 0; 638 | defaultConfigurationName = Release; 639 | }; 640 | B48D8A011D6AE14D00814887 /* Build configuration list for PBXNativeTarget "ChooseLocation" */ = { 641 | isa = XCConfigurationList; 642 | buildConfigurations = ( 643 | B48D8A021D6AE14D00814887 /* Debug */, 644 | B48D8A031D6AE14D00814887 /* Release */, 645 | ); 646 | defaultConfigurationIsVisible = 0; 647 | defaultConfigurationName = Release; 648 | }; 649 | B48D8A041D6AE14D00814887 /* Build configuration list for PBXNativeTarget "ChooseLocationTests" */ = { 650 | isa = XCConfigurationList; 651 | buildConfigurations = ( 652 | B48D8A051D6AE14D00814887 /* Debug */, 653 | B48D8A061D6AE14D00814887 /* Release */, 654 | ); 655 | defaultConfigurationIsVisible = 0; 656 | defaultConfigurationName = Release; 657 | }; 658 | B48D8A071D6AE14D00814887 /* Build configuration list for PBXNativeTarget "ChooseLocationUITests" */ = { 659 | isa = XCConfigurationList; 660 | buildConfigurations = ( 661 | B48D8A081D6AE14D00814887 /* Debug */, 662 | B48D8A091D6AE14D00814887 /* Release */, 663 | ); 664 | defaultConfigurationIsVisible = 0; 665 | defaultConfigurationName = Release; 666 | }; 667 | /* End XCConfigurationList section */ 668 | }; 669 | rootObject = B48D89CC1D6AE14D00814887 /* Project object */; 670 | } 671 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/project.xcworkspace/xcuserdata/HelloYeah.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation.xcodeproj/project.xcworkspace/xcuserdata/HelloYeah.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/project.xcworkspace/xcuserdata/Sekorm.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation.xcodeproj/project.xcworkspace/xcuserdata/Sekorm.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/project.xcworkspace/xcuserdata/dashen.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation.xcodeproj/project.xcworkspace/xcuserdata/dashen.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/xcuserdata/HelloYeah.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/xcuserdata/HelloYeah.xcuserdatad/xcschemes/ChooseLocation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/xcuserdata/HelloYeah.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ChooseLocation.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B48D89D31D6AE14D00814887 16 | 17 | primary 18 | 19 | 20 | B48D89EC1D6AE14D00814887 21 | 22 | primary 23 | 24 | 25 | B48D89F71D6AE14D00814887 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/xcuserdata/Sekorm.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/xcuserdata/Sekorm.xcuserdatad/xcschemes/ChooseLocation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/xcuserdata/Sekorm.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ChooseLocation.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B48D89D31D6AE14D00814887 16 | 17 | primary 18 | 19 | 20 | B48D89EC1D6AE14D00814887 21 | 22 | primary 23 | 24 | 25 | B48D89F71D6AE14D00814887 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ChooseLocation.xcodeproj/xcuserdata/dashen.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ChooseLocation.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ChooseLocation/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation/.DS_Store -------------------------------------------------------------------------------- /ChooseLocation/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ChooseLocation/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /ChooseLocation/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ChooseLocation/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ChooseLocation/Assets.xcassets/btn_check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "btn_check@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "btn_check@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ChooseLocation/Assets.xcassets/btn_check.imageset/btn_check@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation/Assets.xcassets/btn_check.imageset/btn_check@2x.png -------------------------------------------------------------------------------- /ChooseLocation/Assets.xcassets/btn_check.imageset/btn_check@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation/Assets.xcassets/btn_check.imageset/btn_check@3x.png -------------------------------------------------------------------------------- /ChooseLocation/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 | -------------------------------------------------------------------------------- /ChooseLocation/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ChooseLocation/Category/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation/Category/.DS_Store -------------------------------------------------------------------------------- /ChooseLocation/Category/UIView+Frame.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Frame.h 3 | // Sinfo 4 | // 5 | // Created by xiaoyu on 16/6/29. 6 | // Copyright © 2016年 YaoZhong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | SKOscillatoryAnimationToBigger, 13 | SKOscillatoryAnimationToSmaller, 14 | } SKOscillatoryAnimationType; 15 | 16 | @interface UIView (Frame) 17 | @property (nonatomic) CGFloat left; 18 | @property (nonatomic) CGFloat top; 19 | @property (nonatomic) CGFloat right; 20 | @property (nonatomic) CGFloat bottom; 21 | @property (nonatomic) CGFloat width; 22 | @property (nonatomic) CGFloat height; 23 | @property (nonatomic) CGFloat centerX; ///< Shortcut for center.x 24 | @property (nonatomic) CGFloat centerY; ///< Shortcut for center.y 25 | @property (nonatomic) CGPoint origin; 26 | @property (nonatomic) CGSize size; 27 | @property (nonatomic, readonly) CGFloat screenX; 28 | @property (nonatomic, readonly) CGFloat screenY; 29 | 30 | + (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(SKOscillatoryAnimationType)type; 31 | @end 32 | -------------------------------------------------------------------------------- /ChooseLocation/Category/UIView+Frame.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Frame.m 3 | // Sinfo 4 | // 5 | // Created by xiaoyu on 16/6/29. 6 | // Copyright © 2016年 YaoZhong. All rights reserved. 7 | // 8 | 9 | #import "UIView+Frame.h" 10 | 11 | @implementation UIView (Frame) 12 | - (CGFloat)left 13 | { 14 | return self.frame.origin.x; 15 | } 16 | 17 | - (void)setLeft:(CGFloat)x 18 | { 19 | CGRect frame = self.frame; 20 | frame.origin.x = x; 21 | self.frame = frame; 22 | } 23 | 24 | - (CGFloat)top 25 | { 26 | return self.frame.origin.y; 27 | } 28 | 29 | - (void)setTop:(CGFloat)y 30 | { 31 | CGRect frame = self.frame; 32 | frame.origin.y = y; 33 | self.frame = frame; 34 | } 35 | 36 | - (CGFloat)right 37 | { 38 | return self.frame.origin.x + self.frame.size.width; 39 | } 40 | 41 | - (void)setRight:(CGFloat)right 42 | { 43 | CGRect frame = self.frame; 44 | frame.origin.x = right - frame.size.width; 45 | self.frame = frame; 46 | } 47 | 48 | - (CGFloat)bottom 49 | { 50 | return self.frame.origin.y + self.frame.size.height; 51 | } 52 | 53 | - (void)setBottom:(CGFloat)bottom 54 | { 55 | CGRect frame = self.frame; 56 | frame.origin.y = bottom - frame.size.height; 57 | self.frame = frame; 58 | } 59 | 60 | - (CGFloat)width 61 | { 62 | return self.frame.size.width; 63 | } 64 | 65 | - (void)setWidth:(CGFloat)width 66 | { 67 | CGRect frame = self.frame; 68 | frame.size.width = width; 69 | self.frame = frame; 70 | } 71 | 72 | - (CGFloat)height 73 | { 74 | return self.frame.size.height; 75 | } 76 | 77 | - (void)setHeight:(CGFloat)height 78 | { 79 | CGRect frame = self.frame; 80 | frame.size.height = height; 81 | self.frame = frame; 82 | } 83 | 84 | - (CGFloat)screenX 85 | { 86 | CGFloat x = 0; 87 | for (UIView* view = self; view; view = view.superview) { 88 | x += view.left; 89 | } 90 | return x; 91 | } 92 | 93 | - (CGFloat)screenY 94 | { 95 | CGFloat y = 0; 96 | for (UIView* view = self; view; view = view.superview) { 97 | y += view.top; 98 | } 99 | return y; 100 | } 101 | 102 | - (CGPoint)origin 103 | { 104 | return self.frame.origin; 105 | } 106 | 107 | - (void)setOrigin:(CGPoint)origin 108 | { 109 | CGRect frame = self.frame; 110 | frame.origin = origin; 111 | self.frame = frame; 112 | } 113 | 114 | - (CGFloat)centerX 115 | { 116 | return self.center.x; 117 | } 118 | 119 | - (void)setCenterX:(CGFloat)centerX 120 | { 121 | self.center = CGPointMake(centerX, self.center.y); 122 | } 123 | 124 | - (CGFloat)centerY 125 | { 126 | return self.center.y; 127 | } 128 | 129 | - (void)setCenterY:(CGFloat)centerY 130 | { 131 | self.center = CGPointMake(self.center.x, centerY); 132 | } 133 | 134 | - (CGSize)size 135 | { 136 | return self.frame.size; 137 | } 138 | 139 | - (void)setSize:(CGSize)size 140 | { 141 | CGRect frame = self.frame; 142 | frame.size = size; 143 | self.frame = frame; 144 | } 145 | 146 | + (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(SKOscillatoryAnimationType)type{ 147 | NSNumber *animationScale1 = type == SKOscillatoryAnimationToBigger ? @(1.15) : @(0.5); 148 | NSNumber *animationScale2 = type == SKOscillatoryAnimationToBigger ? @(0.92) : @(1.15); 149 | 150 | [UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{ 151 | [layer setValue:animationScale1 forKeyPath:@"transform.scale"]; 152 | } completion:^(BOOL finished) { 153 | [UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{ 154 | [layer setValue:animationScale2 forKeyPath:@"transform.scale"]; 155 | } completion:^(BOOL finished) { 156 | [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{ 157 | [layer setValue:@(1.0) forKeyPath:@"transform.scale"]; 158 | } completion:nil]; 159 | }]; 160 | }]; 161 | } 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloYeah/ChooseLocation/b10c18cd471865c28051aa358018abca8016590a/ChooseLocation/ChooseLocation/.DS_Store -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/Model/AddressItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddressItem.h 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/26. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AddressItem : NSObject 12 | @property (nonatomic,copy) NSString * code; 13 | @property (nonatomic,copy) NSString * sheng; 14 | @property (nonatomic,copy) NSString * di; 15 | @property (nonatomic,copy) NSString * xian; 16 | @property (nonatomic,copy) NSString * name; 17 | @property (nonatomic,copy) NSString * level; 18 | @property (nonatomic,assign) BOOL isSelected; 19 | - (instancetype)initWithDict:(NSDictionary *)dict; 20 | @end 21 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/Model/AddressItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddressItem.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/26. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import "AddressItem.h" 10 | 11 | @implementation AddressItem 12 | 13 | - (instancetype)initWithDict:(NSDictionary *)dict{ 14 | 15 | if (self = [super init]) { 16 | [self setValuesForKeysWithDictionary:dict]; 17 | } 18 | return self; 19 | } 20 | 21 | - (void)setValue:(id)value forUndefinedKey:(NSString *)key{ 22 | 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/View/AddressTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddressTableViewCell.h 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/26. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class AddressItem; 12 | @interface AddressTableViewCell : UITableViewCell 13 | @property (nonatomic,strong) AddressItem * item; 14 | @end 15 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/View/AddressTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddressTableViewCell.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/26. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import "AddressTableViewCell.h" 10 | #import "AddressItem.h" 11 | 12 | @interface AddressTableViewCell () 13 | @property (weak, nonatomic) IBOutlet UILabel *addressLabel; 14 | @property (weak, nonatomic) IBOutlet UIImageView *selectFlag; 15 | @end 16 | @implementation AddressTableViewCell 17 | 18 | - (void)setItem:(AddressItem *)item{ 19 | 20 | _item = item; 21 | _addressLabel.text = item.name; 22 | _addressLabel.textColor = item.isSelected ? [UIColor orangeColor] : [UIColor blackColor] ; 23 | _selectFlag.hidden = !item.isSelected; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/View/AddressTableViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/View/AddressView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddressView.h 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/25. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AddressView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/View/AddressView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddressView.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/25. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import "AddressView.h" 10 | #import "UIView+Frame.h" 11 | 12 | static CGFloat const HYBarItemMargin = 20; 13 | @interface AddressView () 14 | @property (nonatomic,strong) NSMutableArray * btnArray; 15 | @end 16 | 17 | @implementation AddressView 18 | 19 | - (void)layoutSubviews{ 20 | 21 | [super layoutSubviews]; 22 | 23 | for (NSInteger i = 0; i <= self.btnArray.count - 1 ; i++) { 24 | 25 | UIView * view = self.btnArray[i]; 26 | if (i == 0) { 27 | view.left = HYBarItemMargin; 28 | } 29 | if (i > 0) { 30 | UIView * preView = self.btnArray[i - 1]; 31 | view.left = HYBarItemMargin + preView.right; 32 | } 33 | 34 | } 35 | } 36 | 37 | - (NSMutableArray *)btnArray{ 38 | 39 | NSMutableArray * mArray = [NSMutableArray array]; 40 | for (UIView * view in self.subviews) { 41 | if ([view isKindOfClass:[UIButton class]]) { 42 | [mArray addObject:view]; 43 | } 44 | } 45 | _btnArray = mArray; 46 | return _btnArray; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/View/ChooseLocationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChooseLocationView.h 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChooseLocationView : UIView 12 | 13 | @property (nonatomic, copy) NSString * address; 14 | 15 | @property (nonatomic, copy) void(^chooseFinish)(); 16 | 17 | @property (nonatomic,copy) NSString * areaCode; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ChooseLocation/ChooseLocation/View/ChooseLocationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChooseLocationView.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import "ChooseLocationView.h" 10 | #import "AddressView.h" 11 | #import "UIView+Frame.h" 12 | #import "AddressTableViewCell.h" 13 | #import "AddressItem.h" 14 | #import "CitiesDataTool.h" 15 | 16 | #define HYScreenW [UIScreen mainScreen].bounds.size.width 17 | 18 | static CGFloat const kHYTopViewHeight = 40; //顶部视图的高度 19 | static CGFloat const kHYTopTabbarHeight = 30; //地址标签栏的高度 20 | 21 | @interface ChooseLocationView () 22 | @property (nonatomic,weak) AddressView * topTabbar; 23 | @property (nonatomic,weak) UIScrollView * contentView; 24 | @property (nonatomic,weak) UIView * underLine; 25 | @property (nonatomic,strong) NSArray * dataSouce; 26 | @property (nonatomic,strong) NSArray * cityDataSouce; 27 | @property (nonatomic,strong) NSArray * districtDataSouce; 28 | @property (nonatomic,strong) NSMutableArray * tableViews; 29 | @property (nonatomic,strong) NSMutableArray * topTabbarItems; 30 | @property (nonatomic,weak) UIButton * selectedBtn; 31 | @end 32 | 33 | @implementation ChooseLocationView 34 | 35 | - (instancetype)initWithFrame:(CGRect)frame 36 | { 37 | self = [super initWithFrame:frame]; 38 | if (self) { 39 | [self setUp]; 40 | } 41 | return self; 42 | } 43 | 44 | #pragma mark - setUp UI 45 | 46 | - (void)setUp{ 47 | 48 | UIView * topView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, kHYTopViewHeight)]; 49 | [self addSubview:topView]; 50 | UILabel * titleLabel = [[UILabel alloc]init]; 51 | titleLabel.text = @"所在地区"; 52 | [titleLabel sizeToFit]; 53 | [topView addSubview:titleLabel]; 54 | titleLabel.centerY = topView.height * 0.5; 55 | titleLabel.centerX = topView.width * 0.5; 56 | UIView * separateLine = [self separateLine]; 57 | [topView addSubview: separateLine]; 58 | separateLine.top = topView.height - separateLine.height; 59 | topView.backgroundColor = [UIColor whiteColor]; 60 | 61 | 62 | AddressView * topTabbar = [[AddressView alloc]initWithFrame:CGRectMake(0, topView.height, self.frame.size.width, kHYTopViewHeight)]; 63 | [self addSubview:topTabbar]; 64 | _topTabbar = topTabbar; 65 | [self addTopBarItem]; 66 | UIView * separateLine1 = [self separateLine]; 67 | [topTabbar addSubview: separateLine1]; 68 | separateLine1.top = topTabbar.height - separateLine.height; 69 | [_topTabbar layoutIfNeeded]; 70 | topTabbar.backgroundColor = [UIColor whiteColor]; 71 | 72 | UIView * underLine = [[UIView alloc] initWithFrame:CGRectZero]; 73 | [topTabbar addSubview:underLine]; 74 | _underLine = underLine; 75 | underLine.height = 2.0f; 76 | UIButton * btn = self.topTabbarItems.lastObject; 77 | [self changeUnderLineFrame:btn]; 78 | underLine.top = separateLine1.top - underLine.height; 79 | 80 | _underLine.backgroundColor = [UIColor orangeColor]; 81 | UIScrollView * contentView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, CGRectGetMaxY(topTabbar.frame), self.frame.size.width, self.height - kHYTopViewHeight - kHYTopTabbarHeight)]; 82 | contentView.contentSize = CGSizeMake(HYScreenW, 0); 83 | [self addSubview:contentView]; 84 | _contentView = contentView; 85 | _contentView.pagingEnabled = YES; 86 | _contentView.backgroundColor = [UIColor whiteColor]; 87 | [self addTableView]; 88 | _contentView.delegate = self; 89 | } 90 | 91 | 92 | - (void)addTableView{ 93 | 94 | UITableView * tabbleView = [[UITableView alloc]initWithFrame:CGRectMake(self.tableViews.count * HYScreenW, 0, HYScreenW, _contentView.height)]; 95 | [_contentView addSubview:tabbleView]; 96 | [self.tableViews addObject:tabbleView]; 97 | tabbleView.separatorStyle = UITableViewCellSeparatorStyleNone; 98 | tabbleView.delegate = self; 99 | tabbleView.dataSource = self; 100 | tabbleView.contentInset = UIEdgeInsetsMake(0, 0, 44, 0); 101 | [tabbleView registerNib:[UINib nibWithNibName:@"AddressTableViewCell" bundle:nil] forCellReuseIdentifier:@"AddressTableViewCell"]; 102 | } 103 | 104 | - (void)addTopBarItem{ 105 | 106 | UIButton * topBarItem = [UIButton buttonWithType:UIButtonTypeCustom]; 107 | [topBarItem setTitle:@"请选择" forState:UIControlStateNormal]; 108 | [topBarItem setTitleColor:[UIColor colorWithRed:43/255.0 green:43/255.0 blue:43/255.0 alpha:1] forState:UIControlStateNormal]; 109 | [topBarItem setTitleColor:[UIColor orangeColor] forState:UIControlStateSelected]; 110 | [topBarItem sizeToFit]; 111 | topBarItem.centerY = _topTabbar.height * 0.5; 112 | [self.topTabbarItems addObject:topBarItem]; 113 | [_topTabbar addSubview:topBarItem]; 114 | [topBarItem addTarget:self action:@selector(topBarItemClick:) forControlEvents:UIControlEventTouchUpInside]; 115 | } 116 | 117 | #pragma mark - TableViewDatasouce 118 | 119 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 120 | 121 | if([self.tableViews indexOfObject:tableView] == 0){ 122 | return self.dataSouce.count; 123 | }else if ([self.tableViews indexOfObject:tableView] == 1){ 124 | return self.cityDataSouce.count; 125 | }else if ([self.tableViews indexOfObject:tableView] == 2){ 126 | return self.districtDataSouce.count; 127 | } 128 | return self.dataSouce.count; 129 | } 130 | 131 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 132 | 133 | AddressTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"AddressTableViewCell" forIndexPath:indexPath]; 134 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 135 | AddressItem * item; 136 | //省级别 137 | if([self.tableViews indexOfObject:tableView] == 0){ 138 | item = self.dataSouce[indexPath.row]; 139 | //市级别 140 | }else if ([self.tableViews indexOfObject:tableView] == 1){ 141 | item = self.cityDataSouce[indexPath.row]; 142 | //县级别 143 | }else if ([self.tableViews indexOfObject:tableView] == 2){ 144 | item = self.districtDataSouce[indexPath.row]; 145 | } 146 | cell.item = item; 147 | return cell; 148 | } 149 | 150 | #pragma mark - TableViewDelegate 151 | - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 152 | 153 | if([self.tableViews indexOfObject:tableView] == 0){ 154 | 155 | 156 | //1.1 获取下一级别的数据源(市级别,如果是直辖市时,下级则为区级别) 157 | AddressItem * provinceItem = self.dataSouce[indexPath.row]; 158 | self.cityDataSouce = [[CitiesDataTool sharedManager] queryAllRecordWithShengID:[provinceItem.code substringWithRange:(NSRange){0,2}]]; 159 | if(self.cityDataSouce.count == 0){ 160 | for (int i = 0; i < self.tableViews.count && self.tableViews.count != 1; i++) { 161 | [self removeLastItem]; 162 | } 163 | [self setUpAddress:provinceItem.name]; 164 | return indexPath; 165 | } 166 | //1.1 判断是否是第一次选择,不是,则重新选择省,切换省. 167 | NSIndexPath * indexPath0 = [tableView indexPathForSelectedRow]; 168 | 169 | if ([indexPath0 compare:indexPath] != NSOrderedSame && indexPath0) { 170 | 171 | for (int i = 0; i < self.tableViews.count && self.tableViews.count != 1; i++) { 172 | [self removeLastItem]; 173 | } 174 | [self addTopBarItem]; 175 | [self addTableView]; 176 | [self scrollToNextItem:provinceItem.name]; 177 | return indexPath; 178 | 179 | }else if ([indexPath0 compare:indexPath] == NSOrderedSame && indexPath0){ 180 | 181 | for (int i = 0; i < self.tableViews.count && self.tableViews.count != 1 ; i++) { 182 | [self removeLastItem]; 183 | } 184 | [self addTopBarItem]; 185 | [self addTableView]; 186 | [self scrollToNextItem:provinceItem.name]; 187 | return indexPath; 188 | } 189 | 190 | //之前未选中省,第一次选择省 191 | [self addTopBarItem]; 192 | [self addTableView]; 193 | AddressItem * item = self.dataSouce[indexPath.row]; 194 | [self scrollToNextItem:item.name ]; 195 | 196 | }else if ([self.tableViews indexOfObject:tableView] == 1){ 197 | 198 | AddressItem * cityItem = self.cityDataSouce[indexPath.row]; 199 | self.districtDataSouce = [[CitiesDataTool sharedManager] queryAllRecordWithShengID:cityItem.sheng cityID:cityItem.di]; 200 | NSIndexPath * indexPath0 = [tableView indexPathForSelectedRow]; 201 | 202 | if ([indexPath0 compare:indexPath] != NSOrderedSame && indexPath0) { 203 | 204 | for (int i = 0; i < self.tableViews.count - 1; i++) { 205 | [self removeLastItem]; 206 | } 207 | [self addTopBarItem]; 208 | [self addTableView]; 209 | [self scrollToNextItem:cityItem.name]; 210 | return indexPath; 211 | 212 | }else if ([indexPath0 compare:indexPath] == NSOrderedSame && indexPath0){ 213 | 214 | [self scrollToNextItem:cityItem.name]; 215 | return indexPath; 216 | } 217 | 218 | [self addTopBarItem]; 219 | [self addTableView]; 220 | AddressItem * item = self.cityDataSouce[indexPath.row]; 221 | [self scrollToNextItem:item.name]; 222 | 223 | }else if ([self.tableViews indexOfObject:tableView] == 2){ 224 | 225 | AddressItem * item = self.districtDataSouce[indexPath.row]; 226 | [self setUpAddress:item.name]; 227 | } 228 | return indexPath; 229 | } 230 | 231 | 232 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 233 | 234 | AddressItem * item; 235 | if([self.tableViews indexOfObject:tableView] == 0){ 236 | item = self.dataSouce[indexPath.row]; 237 | }else if ([self.tableViews indexOfObject:tableView] == 1){ 238 | item = self.cityDataSouce[indexPath.row]; 239 | }else if ([self.tableViews indexOfObject:tableView] == 2){ 240 | item = self.districtDataSouce[indexPath.row]; 241 | } 242 | item.isSelected = YES; 243 | [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 244 | [tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; 245 | } 246 | 247 | - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{ 248 | 249 | AddressItem * item; 250 | if([self.tableViews indexOfObject:tableView] == 0){ 251 | item = self.dataSouce[indexPath.row]; 252 | }else if ([self.tableViews indexOfObject:tableView] == 1){ 253 | item = self.cityDataSouce[indexPath.row]; 254 | }else if ([self.tableViews indexOfObject:tableView] == 2){ 255 | item = self.districtDataSouce[indexPath.row]; 256 | } 257 | item.isSelected = NO; 258 | [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 259 | 260 | } 261 | 262 | 263 | 264 | #pragma mark - private 265 | 266 | //点击按钮,滚动到对应位置 267 | - (void)topBarItemClick:(UIButton *)btn{ 268 | 269 | NSInteger index = [self.topTabbarItems indexOfObject:btn]; 270 | 271 | [UIView animateWithDuration:0.5 animations:^{ 272 | self.contentView.contentOffset = CGPointMake(index * HYScreenW, 0); 273 | [self changeUnderLineFrame:btn]; 274 | }]; 275 | } 276 | 277 | //调整指示条位置 278 | - (void)changeUnderLineFrame:(UIButton *)btn{ 279 | 280 | _selectedBtn.selected = NO; 281 | btn.selected = YES; 282 | _selectedBtn = btn; 283 | _underLine.left = btn.left; 284 | _underLine.width = btn.width; 285 | } 286 | 287 | //完成地址选择,执行chooseFinish代码块 288 | - (void)setUpAddress:(NSString *)address{ 289 | 290 | NSInteger index = self.contentView.contentOffset.x / HYScreenW; 291 | UIButton * btn = self.topTabbarItems[index]; 292 | [btn setTitle:address forState:UIControlStateNormal]; 293 | [btn sizeToFit]; 294 | [_topTabbar layoutIfNeeded]; 295 | [self changeUnderLineFrame:btn]; 296 | NSMutableString * addressStr = [[NSMutableString alloc] init]; 297 | for (UIButton * btn in self.topTabbarItems) { 298 | if ([btn.currentTitle isEqualToString:@"县"] || [btn.currentTitle isEqualToString:@"市辖区"] ) { 299 | continue; 300 | } 301 | [addressStr appendString:btn.currentTitle]; 302 | [addressStr appendString:@" "]; 303 | } 304 | self.address = addressStr; 305 | 306 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 307 | self.hidden = YES; 308 | if (self.chooseFinish) { 309 | self.chooseFinish(); 310 | } 311 | }); 312 | } 313 | 314 | //当重新选择省或者市的时候,需要将下级视图移除。 315 | - (void)removeLastItem{ 316 | 317 | [self.tableViews.lastObject performSelector:@selector(removeFromSuperview) withObject:nil withObject:nil]; 318 | [self.tableViews removeLastObject]; 319 | 320 | [self.topTabbarItems.lastObject performSelector:@selector(removeFromSuperview) withObject:nil withObject:nil]; 321 | [self.topTabbarItems removeLastObject]; 322 | } 323 | 324 | //滚动到下级界面,并重新设置顶部按钮条上对应按钮的title 325 | - (void)scrollToNextItem:(NSString *)preTitle{ 326 | 327 | NSInteger index = self.contentView.contentOffset.x / HYScreenW; 328 | UIButton * btn = self.topTabbarItems[index]; 329 | [btn setTitle:preTitle forState:UIControlStateNormal]; 330 | [btn sizeToFit]; 331 | [_topTabbar layoutIfNeeded]; 332 | [UIView animateWithDuration:0.25 animations:^{ 333 | self.contentView.contentSize = (CGSize){self.tableViews.count * HYScreenW,0}; 334 | CGPoint offset = self.contentView.contentOffset; 335 | self.contentView.contentOffset = CGPointMake(offset.x + HYScreenW, offset.y); 336 | [self changeUnderLineFrame: [self.topTabbar.subviews lastObject]]; 337 | }]; 338 | } 339 | 340 | 341 | #pragma mark - 342 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 343 | { 344 | if(scrollView != self.contentView) return; 345 | __weak typeof(self)weakSelf = self; 346 | [UIView animateWithDuration:0.25 animations:^{ 347 | NSInteger index = scrollView.contentOffset.x / HYScreenW; 348 | UIButton * btn = weakSelf.topTabbarItems[index]; 349 | [weakSelf changeUnderLineFrame:btn]; 350 | }]; 351 | } 352 | 353 | #pragma mark - 开始就有地址时. 354 | 355 | - (void)setAreaCode:(NSString *)areaCode{ 356 | 357 | _areaCode = areaCode; 358 | //2.1 添加市级别,地区级别列表 359 | [self addTableView]; 360 | [self addTableView]; 361 | 362 | self.cityDataSouce = [[CitiesDataTool sharedManager] queryAllRecordWithShengID:[self.areaCode substringWithRange:(NSRange){0,2}]]; 363 | self.districtDataSouce = [[CitiesDataTool sharedManager] queryAllRecordWithShengID:[self.areaCode substringWithRange:(NSRange){0,2}] cityID:[self.areaCode substringWithRange:(NSRange){2,2}]];// 364 | 365 | //2.3 添加底部对应按钮 366 | [self addTopBarItem]; 367 | [self addTopBarItem]; 368 | 369 | NSString * code = [self.areaCode stringByReplacingCharactersInRange:(NSRange){2,4} withString:@"0000"]; 370 | NSString * provinceName = [[CitiesDataTool sharedManager] queryAllRecordWithAreaCode:code]; 371 | UIButton * firstBtn = self.topTabbarItems.firstObject; 372 | [firstBtn setTitle:provinceName forState:UIControlStateNormal]; 373 | 374 | NSString * cityName = [[CitiesDataTool sharedManager] queryAllRecordWithAreaCode:[self.areaCode stringByReplacingCharactersInRange:(NSRange){4,2} withString:@"00"]]; 375 | UIButton * midBtn = self.topTabbarItems[1]; 376 | [midBtn setTitle:cityName forState:UIControlStateNormal]; 377 | 378 | NSString * districtName = [[CitiesDataTool sharedManager] queryAllRecordWithAreaCode:self.areaCode]; 379 | UIButton * lastBtn = self.topTabbarItems.lastObject; 380 | [lastBtn setTitle:districtName forState:UIControlStateNormal]; 381 | [self.topTabbarItems makeObjectsPerformSelector:@selector(sizeToFit)]; 382 | [_topTabbar layoutIfNeeded]; 383 | 384 | 385 | [self changeUnderLineFrame:lastBtn]; 386 | 387 | //2.4 设置偏移量 388 | self.contentView.contentSize = (CGSize){self.tableViews.count * HYScreenW,0}; 389 | CGPoint offset = self.contentView.contentOffset; 390 | self.contentView.contentOffset = CGPointMake((self.tableViews.count - 1) * HYScreenW, offset.y); 391 | 392 | [self setSelectedProvince:provinceName andCity:cityName andDistrict:districtName]; 393 | } 394 | 395 | //初始化选中状态 396 | - (void)setSelectedProvince:(NSString *)provinceName andCity:(NSString *)cityName andDistrict:(NSString *)districtName { 397 | 398 | for (AddressItem * item in self.dataSouce) { 399 | if ([item.name isEqualToString:provinceName]) { 400 | NSIndexPath * indexPath = [NSIndexPath indexPathForRow:[self.dataSouce indexOfObject:item] inSection:0]; 401 | UITableView * tableView = self.tableViews.firstObject; 402 | [tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle]; 403 | [self tableView:tableView didSelectRowAtIndexPath:indexPath]; 404 | break; 405 | } 406 | } 407 | 408 | for (int i = 0; i < self.cityDataSouce.count; i++) { 409 | AddressItem * item = self.cityDataSouce[i]; 410 | 411 | if ([item.name isEqualToString:cityName]) { 412 | NSIndexPath * indexPath = [NSIndexPath indexPathForRow:i inSection:0]; 413 | UITableView * tableView = self.tableViews[1]; 414 | [tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle]; 415 | [self tableView:tableView didSelectRowAtIndexPath:indexPath]; 416 | break; 417 | } 418 | } 419 | 420 | for (int i = 0; i 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ChooseLocation/Tool/CitiesDataTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // CitiesDataTool.h 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/10/25. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CitiesDataTool : NSObject 12 | + (instancetype)sharedManager; 13 | - (void)requestGetData; 14 | //查询出所有的省 15 | - (NSMutableArray *)queryAllProvince; 16 | //根据areaLevel级别,省ID 查询出该省下属的市 17 | //- (NSMutableArray *)queryAllRecordWithSheng:(NSString *) sheng; 18 | //根据areaLevel级别,省ID(sheng) ,查询 市 19 | - (NSMutableArray *)queryAllRecordWithShengID:(NSString *) sheng; 20 | //根据areaLevel级别,省ID(sheng) , 市ID(di) ,查询 县 21 | - (NSMutableArray *)queryAllRecordWithShengID:(NSString *) sheng cityID:(NSString *)di; 22 | //根据areaCode, 查询地址 23 | - (NSString *)queryAllRecordWithAreaCode:(NSString *) areaCode; 24 | @end 25 | -------------------------------------------------------------------------------- /ChooseLocation/Tool/CitiesDataTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // CitiesDataTool.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/10/25. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import "CitiesDataTool.h" 10 | #import "FMDB.h" 11 | #import "AddressItem.h" 12 | 13 | static NSString * const dbName = @"location.db"; 14 | static NSString * const locationTabbleName = @"locationTabble"; 15 | 16 | @interface CitiesDataTool () 17 | @property (nonatomic,strong) NSMutableArray * dataArray; 18 | @property (nonatomic, strong) FMDatabase *fmdb; 19 | @end 20 | 21 | @implementation CitiesDataTool 22 | 23 | static CitiesDataTool *shareInstance = nil; 24 | 25 | #pragma mark - Singleton 26 | + (CitiesDataTool *)sharedManager 27 | { 28 | @synchronized (self) { 29 | if (shareInstance == nil) { 30 | shareInstance = [[self alloc] init]; 31 | } 32 | } 33 | return shareInstance; 34 | } 35 | 36 | + (instancetype)allocWithZone:(struct _NSZone *)zone 37 | { 38 | @synchronized (self) { 39 | if (shareInstance == nil) { 40 | shareInstance = [super allocWithZone:zone]; 41 | } 42 | } 43 | return shareInstance; 44 | } 45 | 46 | - (id)copy 47 | { 48 | return shareInstance; 49 | } 50 | 51 | - (id)init 52 | { 53 | self = [super init]; 54 | if (self) { 55 | [self creatDB]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)creatDB 61 | { 62 | 63 | NSString *dbPath = [self pathForName:dbName]; 64 | self.fmdb = [FMDatabase databaseWithPath:dbPath]; 65 | } 66 | - (void)deleteDB 67 | { 68 | NSString *dbPath = [self pathForName:dbName]; 69 | [[NSFileManager defaultManager] removeItemAtPath:dbPath error:nil]; 70 | } 71 | 72 | //获得指定名字的文件的全路径 73 | - (NSString *)pathForName:(NSString *)name 74 | { 75 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 76 | NSString *documentDirectory = [paths lastObject]; 77 | NSString *dbPath = [documentDirectory stringByAppendingPathComponent:name]; 78 | return dbPath; 79 | } 80 | 81 | // 判断是否存在表 82 | - (BOOL) isTableOK 83 | { 84 | BOOL openSuccess = [self.fmdb open]; 85 | if (!openSuccess) { 86 | NSLog(@"地址数据库打开失败"); 87 | } else { 88 | NSLog(@"地址数据库打开成功"); 89 | FMResultSet *rs = [self.fmdb executeQuery:@"SELECT count(*) as 'count' FROM sqlite_master WHERE type ='table' and name = ?", locationTabbleName]; 90 | while ([rs next]) 91 | { 92 | // just print out what we've got in a number of formats. 93 | NSInteger count = [rs intForColumn:@"count"]; 94 | if (0 == count) 95 | { 96 | [self.fmdb close]; 97 | return NO; 98 | } 99 | else 100 | { 101 | [self.fmdb close]; 102 | return YES; 103 | } 104 | } 105 | } 106 | [self.fmdb close]; 107 | return NO; 108 | } 109 | 110 | 111 | //发送网络请求,获取省市区数据,这里用的是本地json数据 112 | - (void)requestGetData{ 113 | 114 | if ([self isTableOK]) { 115 | return; 116 | } 117 | 118 | NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"Cities" ofType:@"json"]; 119 | NSData *data=[NSData dataWithContentsOfFile:jsonPath]; 120 | NSError *error; 121 | NSArray * jsonObjectArray =[NSJSONSerialization JSONObjectWithData:data 122 | options:kNilOptions 123 | error:&error]; 124 | 125 | for (NSDictionary * dict in jsonObjectArray) { 126 | AddressItem * item = [[AddressItem alloc] initWithDict:dict]; 127 | [self.dataArray addObject:item]; 128 | } 129 | if(self.dataArray.count > 0 && [self createTable]){ 130 | 131 | [self insertRecords]; 132 | } 133 | } 134 | 135 | //往表插入数据 136 | - (void)insertRecords{ 137 | 138 | NSDate *startTime = [NSDate date]; 139 | // 开启事务 140 | if ([self.fmdb open] && [self.fmdb beginTransaction]) { 141 | 142 | BOOL isRollBack = NO; 143 | @try 144 | { 145 | for (AddressItem * item in self.dataArray) { 146 | 147 | if (item.level.intValue == 3 && [item.name isEqualToString:@"市辖区"]) { 148 | continue; 149 | } 150 | 151 | NSString *insertSql= [NSString stringWithFormat: 152 | @"INSERT INTO %@ ('code','sheng','di','xian','name', 'level') VALUES ('%@','%@','%@','%@','%@','%@')", 153 | locationTabbleName,item.code, item.sheng,item.di,item.xian ,item.name, item.level]; 154 | BOOL a = [self.fmdb executeUpdate:insertSql]; 155 | if (!a) 156 | { 157 | NSLog(@"插入地址信息数据失败"); 158 | } 159 | else 160 | { 161 | NSLog(@"批量插入地址信息数据成功!"); 162 | 163 | } 164 | } 165 | NSDate *endTime = [NSDate date]; 166 | NSTimeInterval a = [endTime timeIntervalSince1970] - [startTime timeIntervalSince1970]; 167 | NSLog(@"使用事务地址信息用时%.3f秒",a); 168 | 169 | } 170 | @catch (NSException *exception) 171 | { 172 | isRollBack = YES; 173 | [self.fmdb rollback]; 174 | } 175 | @finally 176 | { 177 | if (!isRollBack) 178 | { 179 | [self.fmdb commit]; 180 | } 181 | } 182 | [self.fmdb close]; 183 | 184 | } else { 185 | [self insertRecords]; 186 | } 187 | } 188 | 189 | 190 | // 删除表 191 | - (BOOL)deleteTable 192 | 193 | { 194 | // if (![self isTableOK]) { 195 | // return YES; 196 | // } 197 | 198 | BOOL openSuccess = [self.fmdb open]; 199 | if (!openSuccess) { 200 | NSLog(@"地址数据库打开失败"); 201 | } else { 202 | NSLog(@"地址数据库打开成功"); 203 | NSString *sqlstr = [NSString stringWithFormat:@"DROP TABLE %@", locationTabbleName]; 204 | 205 | if (![self.fmdb executeUpdate:sqlstr]) 206 | { 207 | [self.fmdb close]; 208 | return NO; 209 | } 210 | } 211 | [self.fmdb close]; 212 | return YES; 213 | } 214 | 215 | //创建表 216 | - (BOOL)createTable{ 217 | 218 | BOOL result = NO; 219 | BOOL openSuccess = [self.fmdb open]; 220 | if (!openSuccess) { 221 | NSLog(@"地址数据库打开失败"); 222 | } else { 223 | NSLog(@"地址数据库打开成功"); 224 | //'code','sheng','di','xian','name', 'level' 225 | NSString *sql = [NSString stringWithFormat:@"create table if not exists %@ (code text primary key,sheng text,di text,xian text,name text,level text);",locationTabbleName]; 226 | result = [self.fmdb executeUpdate:sql]; 227 | if (!result) { 228 | NSLog(@"创建地址表失败"); 229 | 230 | } else { 231 | NSLog(@"创建地址表成功"); 232 | } 233 | } 234 | [self.fmdb close]; 235 | return result; 236 | } 237 | 238 | //根据areaLevel 查询 239 | - (NSMutableArray *)queryAllProvince 240 | { 241 | if ([self.fmdb open]) { 242 | NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE `level` = 1", locationTabbleName]; 243 | FMResultSet *result = [self.fmdb executeQuery:sql]; 244 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0]; 245 | //'code','sheng','di','xian','name', 'level' 246 | while ([result next]) { 247 | AddressItem *model = [[AddressItem alloc] init]; 248 | model.code = [result stringForColumn:@"code"]; 249 | model.sheng = [result stringForColumn:@"sheng"]; 250 | model.di = [result stringForColumn:@"di"]; 251 | model.xian = [result stringForColumn:@"xian"]; 252 | model.name = [result stringForColumn:@"name"]; 253 | model.level = [result stringForColumn:@"level"]; 254 | [array addObject:model]; 255 | } 256 | [self.fmdb close]; 257 | return array; 258 | } 259 | return nil; 260 | } 261 | 262 | //根据areaCode, 查询地址 263 | - (NSString *)queryAllRecordWithAreaCode:(NSString *) areaCode 264 | 265 | { 266 | if ([self.fmdb open]) { 267 | NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE `code` = %@" , locationTabbleName,areaCode]; 268 | FMResultSet *result = [self.fmdb executeQuery:sql]; 269 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0]; 270 | //'code','sheng','di','xian','name', 'level' 271 | while ([result next]) { 272 | AddressItem *model = [[AddressItem alloc] init]; 273 | model.code = [result stringForColumn:@"code"]; 274 | model.sheng = [result stringForColumn:@"sheng"]; 275 | model.di = [result stringForColumn:@"di"]; 276 | model.xian = [result stringForColumn:@"xian"]; 277 | model.name = [result stringForColumn:@"name"]; 278 | model.level = [result stringForColumn:@"level"]; 279 | [array addObject:model]; 280 | } 281 | [self.fmdb close]; 282 | if (array.count > 0) { 283 | AddressItem * model = array.firstObject; 284 | return model.name; 285 | } 286 | } 287 | return nil; 288 | } 289 | 290 | //根据areaLevel级别,省ID 查询 市 291 | - (NSMutableArray *)queryAllRecordWithSheng:(NSString *) sheng 292 | 293 | { 294 | if ([self.fmdb open]) { 295 | NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE `level` = 2 AND `sheng` = %@" , locationTabbleName,sheng]; 296 | FMResultSet *result = [self.fmdb executeQuery:sql]; 297 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0]; 298 | //'code','sheng','di','xian','name', 'level' 299 | while ([result next]) { 300 | AddressItem *model = [[AddressItem alloc] init]; 301 | model.code = [result stringForColumn:@"code"]; 302 | model.sheng = [result stringForColumn:@"sheng"]; 303 | model.di = [result stringForColumn:@"di"]; 304 | model.xian = [result stringForColumn:@"xian"]; 305 | model.name = [result stringForColumn:@"name"]; 306 | model.level = [result stringForColumn:@"level"]; 307 | [array addObject:model]; 308 | } 309 | [self.fmdb close]; 310 | return array; 311 | } 312 | return nil; 313 | } 314 | 315 | //根据areaLevel级别,省ID(sheng) ,查询 市 316 | - (NSMutableArray *)queryAllRecordWithShengID:(NSString *) sheng{ 317 | 318 | if ([self.fmdb open]) { 319 | NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE `level` = 2 AND `sheng` = %@ " , locationTabbleName,sheng]; 320 | FMResultSet *result = [self.fmdb executeQuery:sql]; 321 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0]; 322 | while ([result next]) { 323 | AddressItem *model = [[AddressItem alloc] init]; 324 | model.code = [result stringForColumn:@"code"]; 325 | model.sheng = [result stringForColumn:@"sheng"]; 326 | model.di = [result stringForColumn:@"di"]; 327 | model.xian = [result stringForColumn:@"xian"]; 328 | model.name = [result stringForColumn:@"name"]; 329 | model.level = [result stringForColumn:@"level"]; 330 | [array addObject:model]; 331 | } 332 | [self.fmdb close]; 333 | return array; 334 | } 335 | return nil; 336 | 337 | } 338 | 339 | 340 | //根据areaLevel级别,省ID(sheng) , 市ID(di) ,查询 县 341 | - (NSMutableArray *)queryAllRecordWithShengID:(NSString *) sheng cityID:(NSString *)di{ 342 | 343 | if ([self.fmdb open]) { 344 | NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE `level` = 3 AND `sheng` = %@ AND `di` = '%@'" , locationTabbleName,sheng,di]; 345 | FMResultSet *result = [self.fmdb executeQuery:sql]; 346 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0]; 347 | while ([result next]) { 348 | AddressItem *model = [[AddressItem alloc] init]; 349 | model.code = [result stringForColumn:@"code"]; 350 | model.sheng = [result stringForColumn:@"sheng"]; 351 | model.di = [result stringForColumn:@"di"]; 352 | model.xian = [result stringForColumn:@"xian"]; 353 | model.name = [result stringForColumn:@"name"]; 354 | model.level = [result stringForColumn:@"level"]; 355 | [array addObject:model]; 356 | } 357 | [self.fmdb close]; 358 | return array; 359 | } 360 | return nil; 361 | 362 | } 363 | 364 | - (NSMutableArray *)dataArray{ 365 | 366 | if (_dataArray == nil) { 367 | _dataArray = [[NSMutableArray alloc]init]; 368 | } 369 | return _dataArray; 370 | } 371 | 372 | @end 373 | -------------------------------------------------------------------------------- /ChooseLocation/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ChooseLocation/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ChooseLocationView.h" 11 | #import "CitiesDataTool.h" 12 | @interface ViewController () 13 | @property (nonatomic,strong) ChooseLocationView *chooseLocationView; 14 | @property (nonatomic,strong) UIView *cover; 15 | @property (weak, nonatomic) IBOutlet UILabel *addresslabel; 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | [[CitiesDataTool sharedManager] requestGetData]; 23 | [self.view addSubview:self.cover]; 24 | self.chooseLocationView.address = @"广东省 广州市 越秀区"; 25 | self.chooseLocationView.areaCode = @"440104"; 26 | self.addresslabel.text = @"广东省 广州市 越秀区"; 27 | } 28 | 29 | - (IBAction)chooseLocation:(UIButton *)sender { 30 | 31 | [UIView animateWithDuration:0.25 animations:^{ 32 | self.view.transform =CGAffineTransformMakeScale(0.95, 0.95); 33 | }]; 34 | self.cover.hidden = !self.cover.hidden; 35 | self.chooseLocationView.hidden = self.cover.hidden; 36 | } 37 | 38 | 39 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ 40 | 41 | CGPoint point = [gestureRecognizer locationInView:gestureRecognizer.view]; 42 | if (CGRectContainsPoint(_chooseLocationView.frame, point)){ 43 | return NO; 44 | } 45 | return YES; 46 | } 47 | 48 | 49 | - (void)tapCover:(UITapGestureRecognizer *)tap{ 50 | 51 | if (_chooseLocationView.chooseFinish) { 52 | _chooseLocationView.chooseFinish(); 53 | } 54 | } 55 | 56 | - (ChooseLocationView *)chooseLocationView{ 57 | 58 | if (!_chooseLocationView) { 59 | _chooseLocationView = [[ChooseLocationView alloc]initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 350, [UIScreen mainScreen].bounds.size.width, 350)]; 60 | 61 | } 62 | return _chooseLocationView; 63 | } 64 | 65 | - (UIView *)cover{ 66 | 67 | if (!_cover) { 68 | _cover = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds]; 69 | _cover.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2]; 70 | [_cover addSubview:self.chooseLocationView]; 71 | __weak typeof (self) weakSelf = self; 72 | _chooseLocationView.chooseFinish = ^{ 73 | [UIView animateWithDuration:0.25 animations:^{ 74 | weakSelf.addresslabel.text = weakSelf.chooseLocationView.address; 75 | weakSelf.view.transform = CGAffineTransformIdentity; 76 | weakSelf.cover.hidden = YES; 77 | }]; 78 | }; 79 | UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapCover:)]; 80 | [_cover addGestureRecognizer:tap]; 81 | tap.delegate = self; 82 | _cover.hidden = YES; 83 | } 84 | return _cover; 85 | } 86 | @end 87 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMDB.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | FOUNDATION_EXPORT double FMDBVersionNumber; 4 | FOUNDATION_EXPORT const unsigned char FMDBVersionString[]; 5 | 6 | #import "FMDatabase.h" 7 | #import "FMResultSet.h" 8 | #import "FMDatabaseAdditions.h" 9 | #import "FMDatabaseQueue.h" 10 | #import "FMDatabasePool.h" 11 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMDatabaseAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FMDatabase.h" 11 | 12 | 13 | /** Category of additions for `` class. 14 | 15 | ### See also 16 | 17 | - `` 18 | */ 19 | 20 | @interface FMDatabase (FMDatabaseAdditions) 21 | 22 | ///---------------------------------------- 23 | /// @name Return results of SQL to variable 24 | ///---------------------------------------- 25 | 26 | /** Return `int` value for query 27 | 28 | @param query The SQL query to be performed. 29 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 30 | 31 | @return `int` value. 32 | 33 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 34 | */ 35 | 36 | - (int)intForQuery:(NSString*)query, ...; 37 | 38 | /** Return `long` value for query 39 | 40 | @param query The SQL query to be performed. 41 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 42 | 43 | @return `long` value. 44 | 45 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 46 | */ 47 | 48 | - (long)longForQuery:(NSString*)query, ...; 49 | 50 | /** Return `BOOL` value for query 51 | 52 | @param query The SQL query to be performed. 53 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 54 | 55 | @return `BOOL` value. 56 | 57 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 58 | */ 59 | 60 | - (BOOL)boolForQuery:(NSString*)query, ...; 61 | 62 | /** Return `double` value for query 63 | 64 | @param query The SQL query to be performed. 65 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 66 | 67 | @return `double` value. 68 | 69 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 70 | */ 71 | 72 | - (double)doubleForQuery:(NSString*)query, ...; 73 | 74 | /** Return `NSString` value for query 75 | 76 | @param query The SQL query to be performed. 77 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 78 | 79 | @return `NSString` value. 80 | 81 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 82 | */ 83 | 84 | - (NSString*)stringForQuery:(NSString*)query, ...; 85 | 86 | /** Return `NSData` value for query 87 | 88 | @param query The SQL query to be performed. 89 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 90 | 91 | @return `NSData` value. 92 | 93 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 94 | */ 95 | 96 | - (NSData*)dataForQuery:(NSString*)query, ...; 97 | 98 | /** Return `NSDate` value for query 99 | 100 | @param query The SQL query to be performed. 101 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 102 | 103 | @return `NSDate` value. 104 | 105 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 106 | */ 107 | 108 | - (NSDate*)dateForQuery:(NSString*)query, ...; 109 | 110 | 111 | // Notice that there's no dataNoCopyForQuery:. 112 | // That would be a bad idea, because we close out the result set, and then what 113 | // happens to the data that we just didn't copy? Who knows, not I. 114 | 115 | 116 | ///-------------------------------- 117 | /// @name Schema related operations 118 | ///-------------------------------- 119 | 120 | /** Does table exist in database? 121 | 122 | @param tableName The name of the table being looked for. 123 | 124 | @return `YES` if table found; `NO` if not found. 125 | */ 126 | 127 | - (BOOL)tableExists:(NSString*)tableName; 128 | 129 | /** The schema of the database. 130 | 131 | This will be the schema for the entire database. For each entity, each row of the result set will include the following fields: 132 | 133 | - `type` - The type of entity (e.g. table, index, view, or trigger) 134 | - `name` - The name of the object 135 | - `tbl_name` - The name of the table to which the object references 136 | - `rootpage` - The page number of the root b-tree page for tables and indices 137 | - `sql` - The SQL that created the entity 138 | 139 | @return `FMResultSet` of schema; `nil` on error. 140 | 141 | @see [SQLite File Format](http://www.sqlite.org/fileformat.html) 142 | */ 143 | 144 | - (FMResultSet*)getSchema; 145 | 146 | /** The schema of the database. 147 | 148 | This will be the schema for a particular table as report by SQLite `PRAGMA`, for example: 149 | 150 | PRAGMA table_info('employees') 151 | 152 | This will report: 153 | 154 | - `cid` - The column ID number 155 | - `name` - The name of the column 156 | - `type` - The data type specified for the column 157 | - `notnull` - whether the field is defined as NOT NULL (i.e. values required) 158 | - `dflt_value` - The default value for the column 159 | - `pk` - Whether the field is part of the primary key of the table 160 | 161 | @param tableName The name of the table for whom the schema will be returned. 162 | 163 | @return `FMResultSet` of schema; `nil` on error. 164 | 165 | @see [table_info](http://www.sqlite.org/pragma.html#pragma_table_info) 166 | */ 167 | 168 | - (FMResultSet*)getTableSchema:(NSString*)tableName; 169 | 170 | /** Test to see if particular column exists for particular table in database 171 | 172 | @param columnName The name of the column. 173 | 174 | @param tableName The name of the table. 175 | 176 | @return `YES` if column exists in table in question; `NO` otherwise. 177 | */ 178 | 179 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName; 180 | 181 | /** Test to see if particular column exists for particular table in database 182 | 183 | @param columnName The name of the column. 184 | 185 | @param tableName The name of the table. 186 | 187 | @return `YES` if column exists in table in question; `NO` otherwise. 188 | 189 | @see columnExists:inTableWithName: 190 | 191 | @warning Deprecated - use `` instead. 192 | */ 193 | 194 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)); 195 | 196 | 197 | /** Validate SQL statement 198 | 199 | This validates SQL statement by performing `sqlite3_prepare_v2`, but not returning the results, but instead immediately calling `sqlite3_finalize`. 200 | 201 | @param sql The SQL statement being validated. 202 | 203 | @param error This is a pointer to a `NSError` object that will receive the autoreleased `NSError` object if there was any error. If this is `nil`, no `NSError` result will be returned. 204 | 205 | @return `YES` if validation succeeded without incident; `NO` otherwise. 206 | 207 | */ 208 | 209 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error; 210 | 211 | 212 | ///----------------------------------- 213 | /// @name Application identifier tasks 214 | ///----------------------------------- 215 | 216 | /** Retrieve application ID 217 | 218 | @return The `uint32_t` numeric value of the application ID. 219 | 220 | @see setApplicationID: 221 | */ 222 | 223 | - (uint32_t)applicationID; 224 | 225 | /** Set the application ID 226 | 227 | @param appID The `uint32_t` numeric value of the application ID. 228 | 229 | @see applicationID 230 | */ 231 | 232 | - (void)setApplicationID:(uint32_t)appID; 233 | 234 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 235 | /** Retrieve application ID string 236 | 237 | @return The `NSString` value of the application ID. 238 | 239 | @see setApplicationIDString: 240 | */ 241 | 242 | 243 | - (NSString*)applicationIDString; 244 | 245 | /** Set the application ID string 246 | 247 | @param string The `NSString` value of the application ID. 248 | 249 | @see applicationIDString 250 | */ 251 | 252 | - (void)setApplicationIDString:(NSString*)string; 253 | 254 | #endif 255 | 256 | ///----------------------------------- 257 | /// @name user version identifier tasks 258 | ///----------------------------------- 259 | 260 | /** Retrieve user version 261 | 262 | @return The `uint32_t` numeric value of the user version. 263 | 264 | @see setUserVersion: 265 | */ 266 | 267 | - (uint32_t)userVersion; 268 | 269 | /** Set the user-version 270 | 271 | @param version The `uint32_t` numeric value of the user version. 272 | 273 | @see userVersion 274 | */ 275 | 276 | - (void)setUserVersion:(uint32_t)version; 277 | 278 | @end 279 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMDatabaseAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import "FMDatabase.h" 10 | #import "FMDatabaseAdditions.h" 11 | #import "TargetConditionals.h" 12 | 13 | #if FMDB_SQLITE_STANDALONE 14 | #import 15 | #else 16 | #import 17 | #endif 18 | 19 | @interface FMDatabase (PrivateStuff) 20 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; 21 | @end 22 | 23 | @implementation FMDatabase (FMDatabaseAdditions) 24 | 25 | #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ 26 | va_list args; \ 27 | va_start(args, query); \ 28 | FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \ 29 | va_end(args); \ 30 | if (![resultSet next]) { return (type)0; } \ 31 | type ret = [resultSet sel:0]; \ 32 | [resultSet close]; \ 33 | [resultSet setParentDB:nil]; \ 34 | return ret; 35 | 36 | 37 | - (NSString*)stringForQuery:(NSString*)query, ... { 38 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); 39 | } 40 | 41 | - (int)intForQuery:(NSString*)query, ... { 42 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); 43 | } 44 | 45 | - (long)longForQuery:(NSString*)query, ... { 46 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); 47 | } 48 | 49 | - (BOOL)boolForQuery:(NSString*)query, ... { 50 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); 51 | } 52 | 53 | - (double)doubleForQuery:(NSString*)query, ... { 54 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); 55 | } 56 | 57 | - (NSData*)dataForQuery:(NSString*)query, ... { 58 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); 59 | } 60 | 61 | - (NSDate*)dateForQuery:(NSString*)query, ... { 62 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); 63 | } 64 | 65 | 66 | - (BOOL)tableExists:(NSString*)tableName { 67 | 68 | tableName = [tableName lowercaseString]; 69 | 70 | FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; 71 | 72 | //if at least one next exists, table exists 73 | BOOL returnBool = [rs next]; 74 | 75 | //close and free object 76 | [rs close]; 77 | 78 | return returnBool; 79 | } 80 | 81 | /* 82 | get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 83 | check if table exist in database (patch from OZLB) 84 | */ 85 | - (FMResultSet*)getSchema { 86 | 87 | //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 88 | FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"]; 89 | 90 | return rs; 91 | } 92 | 93 | /* 94 | get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 95 | */ 96 | - (FMResultSet*)getTableSchema:(NSString*)tableName { 97 | 98 | //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 99 | FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"pragma table_info('%@')", tableName]]; 100 | 101 | return rs; 102 | } 103 | 104 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName { 105 | 106 | BOOL returnBool = NO; 107 | 108 | tableName = [tableName lowercaseString]; 109 | columnName = [columnName lowercaseString]; 110 | 111 | FMResultSet *rs = [self getTableSchema:tableName]; 112 | 113 | //check if column is present in table schema 114 | while ([rs next]) { 115 | if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) { 116 | returnBool = YES; 117 | break; 118 | } 119 | } 120 | 121 | //If this is not done FMDatabase instance stays out of pool 122 | [rs close]; 123 | 124 | return returnBool; 125 | } 126 | 127 | 128 | 129 | - (uint32_t)applicationID { 130 | #if SQLITE_VERSION_NUMBER >= 3007017 131 | uint32_t r = 0; 132 | 133 | FMResultSet *rs = [self executeQuery:@"pragma application_id"]; 134 | 135 | if ([rs next]) { 136 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 137 | } 138 | 139 | [rs close]; 140 | 141 | return r; 142 | #else 143 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); 144 | if (self.logsErrors) NSLog(@"%@", errorMessage); 145 | return 0; 146 | #endif 147 | } 148 | 149 | - (void)setApplicationID:(uint32_t)appID { 150 | #if SQLITE_VERSION_NUMBER >= 3007017 151 | NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID]; 152 | FMResultSet *rs = [self executeQuery:query]; 153 | [rs next]; 154 | [rs close]; 155 | #else 156 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); 157 | if (self.logsErrors) NSLog(@"%@", errorMessage); 158 | #endif 159 | } 160 | 161 | 162 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 163 | 164 | - (NSString*)applicationIDString { 165 | #if SQLITE_VERSION_NUMBER >= 3007017 166 | NSString *s = NSFileTypeForHFSTypeCode([self applicationID]); 167 | 168 | assert([s length] == 6); 169 | 170 | s = [s substringWithRange:NSMakeRange(1, 4)]; 171 | 172 | 173 | return s; 174 | #else 175 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); 176 | if (self.logsErrors) NSLog(@"%@", errorMessage); 177 | return nil; 178 | #endif 179 | } 180 | 181 | - (void)setApplicationIDString:(NSString*)s { 182 | #if SQLITE_VERSION_NUMBER >= 3007017 183 | if ([s length] != 4) { 184 | NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]); 185 | } 186 | 187 | [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])]; 188 | #else 189 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); 190 | if (self.logsErrors) NSLog(@"%@", errorMessage); 191 | #endif 192 | } 193 | 194 | #endif 195 | 196 | - (uint32_t)userVersion { 197 | uint32_t r = 0; 198 | 199 | FMResultSet *rs = [self executeQuery:@"pragma user_version"]; 200 | 201 | if ([rs next]) { 202 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 203 | } 204 | 205 | [rs close]; 206 | return r; 207 | } 208 | 209 | - (void)setUserVersion:(uint32_t)version { 210 | NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version]; 211 | FMResultSet *rs = [self executeQuery:query]; 212 | [rs next]; 213 | [rs close]; 214 | } 215 | 216 | #pragma clang diagnostic push 217 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 218 | 219 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) { 220 | return [self columnExists:columnName inTableWithName:tableName]; 221 | } 222 | 223 | #pragma clang diagnostic pop 224 | 225 | 226 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error { 227 | sqlite3_stmt *pStmt = NULL; 228 | BOOL validationSucceeded = YES; 229 | 230 | int rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); 231 | if (rc != SQLITE_OK) { 232 | validationSucceeded = NO; 233 | if (error) { 234 | *error = [NSError errorWithDomain:NSCocoaErrorDomain 235 | code:[self lastErrorCode] 236 | userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage] 237 | forKey:NSLocalizedDescriptionKey]]; 238 | } 239 | } 240 | 241 | sqlite3_finalize(pStmt); 242 | 243 | return validationSucceeded; 244 | } 245 | 246 | @end 247 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMDatabasePool.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class FMDatabase; 12 | 13 | /** Pool of `` objects. 14 | 15 | ### See also 16 | 17 | - `` 18 | - `` 19 | 20 | @warning Before using `FMDatabasePool`, please consider using `` instead. 21 | 22 | If you really really really know what you're doing and `FMDatabasePool` is what 23 | you really really need (ie, you're using a read only database), OK you can use 24 | it. But just be careful not to deadlock! 25 | 26 | For an example on deadlocking, search for: 27 | `ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD` 28 | in the main.m file. 29 | */ 30 | 31 | @interface FMDatabasePool : NSObject { 32 | NSString *_path; 33 | 34 | dispatch_queue_t _lockQueue; 35 | 36 | NSMutableArray *_databaseInPool; 37 | NSMutableArray *_databaseOutPool; 38 | 39 | __unsafe_unretained id _delegate; 40 | 41 | NSUInteger _maximumNumberOfDatabasesToCreate; 42 | int _openFlags; 43 | NSString *_vfsName; 44 | } 45 | 46 | /** Database path */ 47 | 48 | @property (atomic, retain) NSString *path; 49 | 50 | /** Delegate object */ 51 | 52 | @property (atomic, assign) id delegate; 53 | 54 | /** Maximum number of databases to create */ 55 | 56 | @property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate; 57 | 58 | /** Open flags */ 59 | 60 | @property (atomic, readonly) int openFlags; 61 | 62 | /** Custom virtual file system name */ 63 | 64 | @property (atomic, copy) NSString *vfsName; 65 | 66 | 67 | ///--------------------- 68 | /// @name Initialization 69 | ///--------------------- 70 | 71 | /** Create pool using path. 72 | 73 | @param aPath The file path of the database. 74 | 75 | @return The `FMDatabasePool` object. `nil` on error. 76 | */ 77 | 78 | + (instancetype)databasePoolWithPath:(NSString*)aPath; 79 | 80 | /** Create pool using path and specified flags 81 | 82 | @param aPath The file path of the database. 83 | @param openFlags Flags passed to the openWithFlags method of the database 84 | 85 | @return The `FMDatabasePool` object. `nil` on error. 86 | */ 87 | 88 | + (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags; 89 | 90 | /** Create pool using path. 91 | 92 | @param aPath The file path of the database. 93 | 94 | @return The `FMDatabasePool` object. `nil` on error. 95 | */ 96 | 97 | - (instancetype)initWithPath:(NSString*)aPath; 98 | 99 | /** Create pool using path and specified flags. 100 | 101 | @param aPath The file path of the database. 102 | @param openFlags Flags passed to the openWithFlags method of the database 103 | 104 | @return The `FMDatabasePool` object. `nil` on error. 105 | */ 106 | 107 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; 108 | 109 | /** Create pool using path and specified flags. 110 | 111 | @param aPath The file path of the database. 112 | @param openFlags Flags passed to the openWithFlags method of the database 113 | @param vfsName The name of a custom virtual file system 114 | 115 | @return The `FMDatabasePool` object. `nil` on error. 116 | */ 117 | 118 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName; 119 | 120 | /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. 121 | 122 | Subclasses can override this method to return specified Class of 'FMDatabase' subclass. 123 | 124 | @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object. 125 | */ 126 | 127 | + (Class)databaseClass; 128 | 129 | ///------------------------------------------------ 130 | /// @name Keeping track of checked in/out databases 131 | ///------------------------------------------------ 132 | 133 | /** Number of checked-in databases in pool 134 | 135 | @returns Number of databases 136 | */ 137 | 138 | - (NSUInteger)countOfCheckedInDatabases; 139 | 140 | /** Number of checked-out databases in pool 141 | 142 | @returns Number of databases 143 | */ 144 | 145 | - (NSUInteger)countOfCheckedOutDatabases; 146 | 147 | /** Total number of databases in pool 148 | 149 | @returns Number of databases 150 | */ 151 | 152 | - (NSUInteger)countOfOpenDatabases; 153 | 154 | /** Release all databases in pool */ 155 | 156 | - (void)releaseAllDatabases; 157 | 158 | ///------------------------------------------ 159 | /// @name Perform database operations in pool 160 | ///------------------------------------------ 161 | 162 | /** Synchronously perform database operations in pool. 163 | 164 | @param block The code to be run on the `FMDatabasePool` pool. 165 | */ 166 | 167 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 168 | 169 | /** Synchronously perform database operations in pool using transaction. 170 | 171 | @param block The code to be run on the `FMDatabasePool` pool. 172 | */ 173 | 174 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 175 | 176 | /** Synchronously perform database operations in pool using deferred transaction. 177 | 178 | @param block The code to be run on the `FMDatabasePool` pool. 179 | */ 180 | 181 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 182 | 183 | /** Synchronously perform database operations in pool using save point. 184 | 185 | @param block The code to be run on the `FMDatabasePool` pool. 186 | 187 | @return `NSError` object if error; `nil` if successful. 188 | 189 | @warning You can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. If you need to nest, use `<[FMDatabase startSavePointWithName:error:]>` instead. 190 | */ 191 | 192 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 193 | 194 | @end 195 | 196 | 197 | /** FMDatabasePool delegate category 198 | 199 | This is a category that defines the protocol for the FMDatabasePool delegate 200 | */ 201 | 202 | @interface NSObject (FMDatabasePoolDelegate) 203 | 204 | /** Asks the delegate whether database should be added to the pool. 205 | 206 | @param pool The `FMDatabasePool` object. 207 | @param database The `FMDatabase` object. 208 | 209 | @return `YES` if it should add database to pool; `NO` if not. 210 | 211 | */ 212 | 213 | - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database; 214 | 215 | /** Tells the delegate that database was added to the pool. 216 | 217 | @param pool The `FMDatabasePool` object. 218 | @param database The `FMDatabase` object. 219 | 220 | */ 221 | 222 | - (void)databasePool:(FMDatabasePool*)pool didAddDatabase:(FMDatabase*)database; 223 | 224 | @end 225 | 226 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMDatabasePool.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #if FMDB_SQLITE_STANDALONE 10 | #import 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "FMDatabasePool.h" 16 | #import "FMDatabase.h" 17 | 18 | @interface FMDatabasePool() 19 | 20 | - (void)pushDatabaseBackInPool:(FMDatabase*)db; 21 | - (FMDatabase*)db; 22 | 23 | @end 24 | 25 | 26 | @implementation FMDatabasePool 27 | @synthesize path=_path; 28 | @synthesize delegate=_delegate; 29 | @synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate; 30 | @synthesize openFlags=_openFlags; 31 | 32 | 33 | + (instancetype)databasePoolWithPath:(NSString*)aPath { 34 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 35 | } 36 | 37 | + (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags { 38 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]); 39 | } 40 | 41 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName { 42 | 43 | self = [super init]; 44 | 45 | if (self != nil) { 46 | _path = [aPath copy]; 47 | _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 48 | _databaseInPool = FMDBReturnRetained([NSMutableArray array]); 49 | _databaseOutPool = FMDBReturnRetained([NSMutableArray array]); 50 | _openFlags = openFlags; 51 | _vfsName = [vfsName copy]; 52 | } 53 | 54 | return self; 55 | } 56 | 57 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { 58 | return [self initWithPath:aPath flags:openFlags vfs:nil]; 59 | } 60 | 61 | - (instancetype)initWithPath:(NSString*)aPath 62 | { 63 | // default flags for sqlite3_open 64 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; 65 | } 66 | 67 | - (instancetype)init { 68 | return [self initWithPath:nil]; 69 | } 70 | 71 | + (Class)databaseClass { 72 | return [FMDatabase class]; 73 | } 74 | 75 | - (void)dealloc { 76 | 77 | _delegate = 0x00; 78 | FMDBRelease(_path); 79 | FMDBRelease(_databaseInPool); 80 | FMDBRelease(_databaseOutPool); 81 | 82 | if (_lockQueue) { 83 | FMDBDispatchQueueRelease(_lockQueue); 84 | _lockQueue = 0x00; 85 | } 86 | #if ! __has_feature(objc_arc) 87 | [super dealloc]; 88 | #endif 89 | } 90 | 91 | 92 | - (void)executeLocked:(void (^)(void))aBlock { 93 | dispatch_sync(_lockQueue, aBlock); 94 | } 95 | 96 | - (void)pushDatabaseBackInPool:(FMDatabase*)db { 97 | 98 | if (!db) { // db can be null if we set an upper bound on the # of databases to create. 99 | return; 100 | } 101 | 102 | [self executeLocked:^() { 103 | 104 | if ([self->_databaseInPool containsObject:db]) { 105 | [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise]; 106 | } 107 | 108 | [self->_databaseInPool addObject:db]; 109 | [self->_databaseOutPool removeObject:db]; 110 | 111 | }]; 112 | } 113 | 114 | - (FMDatabase*)db { 115 | 116 | __block FMDatabase *db; 117 | 118 | 119 | [self executeLocked:^() { 120 | db = [self->_databaseInPool lastObject]; 121 | 122 | BOOL shouldNotifyDelegate = NO; 123 | 124 | if (db) { 125 | [self->_databaseOutPool addObject:db]; 126 | [self->_databaseInPool removeLastObject]; 127 | } 128 | else { 129 | 130 | if (self->_maximumNumberOfDatabasesToCreate) { 131 | NSUInteger currentCount = [self->_databaseOutPool count] + [self->_databaseInPool count]; 132 | 133 | if (currentCount >= self->_maximumNumberOfDatabasesToCreate) { 134 | NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount); 135 | return; 136 | } 137 | } 138 | 139 | db = [[[self class] databaseClass] databaseWithPath:self->_path]; 140 | shouldNotifyDelegate = YES; 141 | } 142 | 143 | //This ensures that the db is opened before returning 144 | #if SQLITE_VERSION_NUMBER >= 3005000 145 | BOOL success = [db openWithFlags:self->_openFlags vfs:self->_vfsName]; 146 | #else 147 | BOOL success = [db open]; 148 | #endif 149 | if (success) { 150 | if ([self->_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![self->_delegate databasePool:self shouldAddDatabaseToPool:db]) { 151 | [db close]; 152 | db = 0x00; 153 | } 154 | else { 155 | //It should not get added in the pool twice if lastObject was found 156 | if (![self->_databaseOutPool containsObject:db]) { 157 | [self->_databaseOutPool addObject:db]; 158 | 159 | if (shouldNotifyDelegate && [self->_delegate respondsToSelector:@selector(databasePool:didAddDatabase:)]) { 160 | [self->_delegate databasePool:self didAddDatabase:db]; 161 | } 162 | } 163 | } 164 | } 165 | else { 166 | NSLog(@"Could not open up the database at path %@", self->_path); 167 | db = 0x00; 168 | } 169 | }]; 170 | 171 | return db; 172 | } 173 | 174 | - (NSUInteger)countOfCheckedInDatabases { 175 | 176 | __block NSUInteger count; 177 | 178 | [self executeLocked:^() { 179 | count = [self->_databaseInPool count]; 180 | }]; 181 | 182 | return count; 183 | } 184 | 185 | - (NSUInteger)countOfCheckedOutDatabases { 186 | 187 | __block NSUInteger count; 188 | 189 | [self executeLocked:^() { 190 | count = [self->_databaseOutPool count]; 191 | }]; 192 | 193 | return count; 194 | } 195 | 196 | - (NSUInteger)countOfOpenDatabases { 197 | __block NSUInteger count; 198 | 199 | [self executeLocked:^() { 200 | count = [self->_databaseOutPool count] + [self->_databaseInPool count]; 201 | }]; 202 | 203 | return count; 204 | } 205 | 206 | - (void)releaseAllDatabases { 207 | [self executeLocked:^() { 208 | [self->_databaseOutPool removeAllObjects]; 209 | [self->_databaseInPool removeAllObjects]; 210 | }]; 211 | } 212 | 213 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 214 | 215 | FMDatabase *db = [self db]; 216 | 217 | block(db); 218 | 219 | [self pushDatabaseBackInPool:db]; 220 | } 221 | 222 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 223 | 224 | BOOL shouldRollback = NO; 225 | 226 | FMDatabase *db = [self db]; 227 | 228 | if (useDeferred) { 229 | [db beginDeferredTransaction]; 230 | } 231 | else { 232 | [db beginTransaction]; 233 | } 234 | 235 | 236 | block(db, &shouldRollback); 237 | 238 | if (shouldRollback) { 239 | [db rollback]; 240 | } 241 | else { 242 | [db commit]; 243 | } 244 | 245 | [self pushDatabaseBackInPool:db]; 246 | } 247 | 248 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 249 | [self beginTransaction:YES withBlock:block]; 250 | } 251 | 252 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 253 | [self beginTransaction:NO withBlock:block]; 254 | } 255 | 256 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 257 | #if SQLITE_VERSION_NUMBER >= 3007000 258 | static unsigned long savePointIdx = 0; 259 | 260 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 261 | 262 | BOOL shouldRollback = NO; 263 | 264 | FMDatabase *db = [self db]; 265 | 266 | NSError *err = 0x00; 267 | 268 | if (![db startSavePointWithName:name error:&err]) { 269 | [self pushDatabaseBackInPool:db]; 270 | return err; 271 | } 272 | 273 | block(db, &shouldRollback); 274 | 275 | if (shouldRollback) { 276 | // We need to rollback and release this savepoint to remove it 277 | [db rollbackToSavePointWithName:name error:&err]; 278 | } 279 | [db releaseSavePointWithName:name error:&err]; 280 | 281 | [self pushDatabaseBackInPool:db]; 282 | 283 | return err; 284 | #else 285 | NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); 286 | if (self.logsErrors) NSLog(@"%@", errorMessage); 287 | return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; 288 | #endif 289 | } 290 | 291 | @end 292 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMDatabaseQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class FMDatabase; 12 | 13 | /** To perform queries and updates on multiple threads, you'll want to use `FMDatabaseQueue`. 14 | 15 | Using a single instance of `` from multiple threads at once is a bad idea. It has always been OK to make a `` object *per thread*. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. 16 | 17 | Instead, use `FMDatabaseQueue`. Here's how to use it: 18 | 19 | First, make your queue. 20 | 21 | FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; 22 | 23 | Then use it like so: 24 | 25 | [queue inDatabase:^(FMDatabase *db) { 26 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; 27 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; 28 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; 29 | 30 | FMResultSet *rs = [db executeQuery:@"select * from foo"]; 31 | while ([rs next]) { 32 | //… 33 | } 34 | }]; 35 | 36 | An easy way to wrap things up in a transaction can be done like this: 37 | 38 | [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { 39 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; 40 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; 41 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; 42 | 43 | if (whoopsSomethingWrongHappened) { 44 | *rollback = YES; 45 | return; 46 | } 47 | // etc… 48 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; 49 | }]; 50 | 51 | `FMDatabaseQueue` will run the blocks on a serialized queue (hence the name of the class). So if you call `FMDatabaseQueue`'s methods from multiple threads at the same time, they will be executed in the order they are received. This way queries and updates won't step on each other's toes, and every one is happy. 52 | 53 | ### See also 54 | 55 | - `` 56 | 57 | @warning Do not instantiate a single `` object and use it across multiple threads. Use `FMDatabaseQueue` instead. 58 | 59 | @warning The calls to `FMDatabaseQueue`'s methods are blocking. So even though you are passing along blocks, they will **not** be run on another thread. 60 | 61 | */ 62 | 63 | @interface FMDatabaseQueue : NSObject { 64 | NSString *_path; 65 | dispatch_queue_t _queue; 66 | FMDatabase *_db; 67 | int _openFlags; 68 | NSString *_vfsName; 69 | } 70 | 71 | /** Path of database */ 72 | 73 | @property (atomic, retain) NSString *path; 74 | 75 | /** Open flags */ 76 | 77 | @property (atomic, readonly) int openFlags; 78 | 79 | /** Custom virtual file system name */ 80 | 81 | @property (atomic, copy) NSString *vfsName; 82 | 83 | ///---------------------------------------------------- 84 | /// @name Initialization, opening, and closing of queue 85 | ///---------------------------------------------------- 86 | 87 | /** Create queue using path. 88 | 89 | @param aPath The file path of the database. 90 | 91 | @return The `FMDatabaseQueue` object. `nil` on error. 92 | */ 93 | 94 | + (instancetype)databaseQueueWithPath:(NSString*)aPath; 95 | 96 | /** Create queue using path and specified flags. 97 | 98 | @param aPath The file path of the database. 99 | @param openFlags Flags passed to the openWithFlags method of the database 100 | 101 | @return The `FMDatabaseQueue` object. `nil` on error. 102 | */ 103 | + (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags; 104 | 105 | /** Create queue using path. 106 | 107 | @param aPath The file path of the database. 108 | 109 | @return The `FMDatabaseQueue` object. `nil` on error. 110 | */ 111 | 112 | - (instancetype)initWithPath:(NSString*)aPath; 113 | 114 | /** Create queue using path and specified flags. 115 | 116 | @param aPath The file path of the database. 117 | @param openFlags Flags passed to the openWithFlags method of the database 118 | 119 | @return The `FMDatabaseQueue` object. `nil` on error. 120 | */ 121 | 122 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; 123 | 124 | /** Create queue using path and specified flags. 125 | 126 | @param aPath The file path of the database. 127 | @param openFlags Flags passed to the openWithFlags method of the database 128 | @param vfsName The name of a custom virtual file system 129 | 130 | @return The `FMDatabaseQueue` object. `nil` on error. 131 | */ 132 | 133 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName; 134 | 135 | /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. 136 | 137 | Subclasses can override this method to return specified Class of 'FMDatabase' subclass. 138 | 139 | @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object. 140 | */ 141 | 142 | + (Class)databaseClass; 143 | 144 | /** Close database used by queue. */ 145 | 146 | - (void)close; 147 | 148 | /** Interupt pending database operation. */ 149 | 150 | - (void)interrupt; 151 | 152 | ///----------------------------------------------- 153 | /// @name Dispatching database operations to queue 154 | ///----------------------------------------------- 155 | 156 | /** Synchronously perform database operations on queue. 157 | 158 | @param block The code to be run on the queue of `FMDatabaseQueue` 159 | */ 160 | 161 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 162 | 163 | /** Synchronously perform database operations on queue, using transactions. 164 | 165 | @param block The code to be run on the queue of `FMDatabaseQueue` 166 | */ 167 | 168 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 169 | 170 | /** Synchronously perform database operations on queue, using deferred transactions. 171 | 172 | @param block The code to be run on the queue of `FMDatabaseQueue` 173 | */ 174 | 175 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 176 | 177 | ///----------------------------------------------- 178 | /// @name Dispatching database operations to queue 179 | ///----------------------------------------------- 180 | 181 | /** Synchronously perform database operations using save point. 182 | 183 | @param block The code to be run on the queue of `FMDatabaseQueue` 184 | */ 185 | 186 | // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. 187 | // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. 188 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 189 | 190 | @end 191 | 192 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMDatabaseQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabaseQueue.h" 10 | #import "FMDatabase.h" 11 | 12 | #if FMDB_SQLITE_STANDALONE 13 | #import 14 | #else 15 | #import 16 | #endif 17 | 18 | /* 19 | 20 | Note: we call [self retain]; before using dispatch_sync, just incase 21 | FMDatabaseQueue is released on another thread and we're in the middle of doing 22 | something in dispatch_sync 23 | 24 | */ 25 | 26 | /* 27 | * A key used to associate the FMDatabaseQueue object with the dispatch_queue_t it uses. 28 | * This in turn is used for deadlock detection by seeing if inDatabase: is called on 29 | * the queue's dispatch queue, which should not happen and causes a deadlock. 30 | */ 31 | static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey; 32 | 33 | @implementation FMDatabaseQueue 34 | 35 | @synthesize path = _path; 36 | @synthesize openFlags = _openFlags; 37 | @synthesize vfsName = _vfsName; 38 | 39 | + (instancetype)databaseQueueWithPath:(NSString*)aPath { 40 | 41 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath]; 42 | 43 | FMDBAutorelease(q); 44 | 45 | return q; 46 | } 47 | 48 | + (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags { 49 | 50 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath flags:openFlags]; 51 | 52 | FMDBAutorelease(q); 53 | 54 | return q; 55 | } 56 | 57 | + (Class)databaseClass { 58 | return [FMDatabase class]; 59 | } 60 | 61 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName { 62 | 63 | self = [super init]; 64 | 65 | if (self != nil) { 66 | 67 | _db = [[[self class] databaseClass] databaseWithPath:aPath]; 68 | FMDBRetain(_db); 69 | 70 | #if SQLITE_VERSION_NUMBER >= 3005000 71 | BOOL success = [_db openWithFlags:openFlags vfs:vfsName]; 72 | #else 73 | BOOL success = [_db open]; 74 | #endif 75 | if (!success) { 76 | NSLog(@"Could not create database queue for path %@", aPath); 77 | FMDBRelease(self); 78 | return 0x00; 79 | } 80 | 81 | _path = FMDBReturnRetained(aPath); 82 | 83 | _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 84 | dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL); 85 | _openFlags = openFlags; 86 | _vfsName = [vfsName copy]; 87 | } 88 | 89 | return self; 90 | } 91 | 92 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { 93 | return [self initWithPath:aPath flags:openFlags vfs:nil]; 94 | } 95 | 96 | - (instancetype)initWithPath:(NSString*)aPath { 97 | 98 | // default flags for sqlite3_open 99 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil]; 100 | } 101 | 102 | - (instancetype)init { 103 | return [self initWithPath:nil]; 104 | } 105 | 106 | 107 | - (void)dealloc { 108 | 109 | FMDBRelease(_db); 110 | FMDBRelease(_path); 111 | 112 | if (_queue) { 113 | FMDBDispatchQueueRelease(_queue); 114 | _queue = 0x00; 115 | } 116 | #if ! __has_feature(objc_arc) 117 | [super dealloc]; 118 | #endif 119 | } 120 | 121 | - (void)close { 122 | FMDBRetain(self); 123 | dispatch_sync(_queue, ^() { 124 | [self->_db close]; 125 | FMDBRelease(_db); 126 | self->_db = 0x00; 127 | }); 128 | FMDBRelease(self); 129 | } 130 | 131 | - (void)interrupt 132 | { 133 | [[self database] interrupt]; 134 | } 135 | 136 | - (FMDatabase*)database { 137 | if (!_db) { 138 | _db = FMDBReturnRetained([[[self class] databaseClass] databaseWithPath:_path]); 139 | 140 | #if SQLITE_VERSION_NUMBER >= 3005000 141 | BOOL success = [_db openWithFlags:_openFlags vfs:_vfsName]; 142 | #else 143 | BOOL success = [_db open]; 144 | #endif 145 | if (!success) { 146 | NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path); 147 | FMDBRelease(_db); 148 | _db = 0x00; 149 | return 0x00; 150 | } 151 | } 152 | 153 | return _db; 154 | } 155 | 156 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 157 | /* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue 158 | * and then check it against self to make sure we're not about to deadlock. */ 159 | FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey); 160 | assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"); 161 | 162 | FMDBRetain(self); 163 | 164 | dispatch_sync(_queue, ^() { 165 | 166 | FMDatabase *db = [self database]; 167 | block(db); 168 | 169 | if ([db hasOpenResultSets]) { 170 | NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]"); 171 | 172 | #if defined(DEBUG) && DEBUG 173 | NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]); 174 | for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { 175 | FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; 176 | NSLog(@"query: '%@'", [rs query]); 177 | } 178 | #endif 179 | } 180 | }); 181 | 182 | FMDBRelease(self); 183 | } 184 | 185 | 186 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 187 | FMDBRetain(self); 188 | dispatch_sync(_queue, ^() { 189 | 190 | BOOL shouldRollback = NO; 191 | 192 | if (useDeferred) { 193 | [[self database] beginDeferredTransaction]; 194 | } 195 | else { 196 | [[self database] beginTransaction]; 197 | } 198 | 199 | block([self database], &shouldRollback); 200 | 201 | if (shouldRollback) { 202 | [[self database] rollback]; 203 | } 204 | else { 205 | [[self database] commit]; 206 | } 207 | }); 208 | 209 | FMDBRelease(self); 210 | } 211 | 212 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 213 | [self beginTransaction:YES withBlock:block]; 214 | } 215 | 216 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 217 | [self beginTransaction:NO withBlock:block]; 218 | } 219 | 220 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 221 | #if SQLITE_VERSION_NUMBER >= 3007000 222 | static unsigned long savePointIdx = 0; 223 | __block NSError *err = 0x00; 224 | FMDBRetain(self); 225 | dispatch_sync(_queue, ^() { 226 | 227 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 228 | 229 | BOOL shouldRollback = NO; 230 | 231 | if ([[self database] startSavePointWithName:name error:&err]) { 232 | 233 | block([self database], &shouldRollback); 234 | 235 | if (shouldRollback) { 236 | // We need to rollback and release this savepoint to remove it 237 | [[self database] rollbackToSavePointWithName:name error:&err]; 238 | } 239 | [[self database] releaseSavePointWithName:name error:&err]; 240 | 241 | } 242 | }); 243 | FMDBRelease(self); 244 | return err; 245 | #else 246 | NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); 247 | if (self.logsErrors) NSLog(@"%@", errorMessage); 248 | return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; 249 | #endif 250 | } 251 | 252 | @end 253 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMResultSet.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #ifndef __has_feature // Optional. 4 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 5 | #endif 6 | 7 | #ifndef NS_RETURNS_NOT_RETAINED 8 | #if __has_feature(attribute_ns_returns_not_retained) 9 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 10 | #else 11 | #define NS_RETURNS_NOT_RETAINED 12 | #endif 13 | #endif 14 | 15 | @class FMDatabase; 16 | @class FMStatement; 17 | 18 | /** Represents the results of executing a query on an ``. 19 | 20 | ### See also 21 | 22 | - `` 23 | */ 24 | 25 | @interface FMResultSet : NSObject { 26 | FMDatabase *_parentDB; 27 | FMStatement *_statement; 28 | 29 | NSString *_query; 30 | NSMutableDictionary *_columnNameToIndexMap; 31 | } 32 | 33 | ///----------------- 34 | /// @name Properties 35 | ///----------------- 36 | 37 | /** Executed query */ 38 | 39 | @property (atomic, retain) NSString *query; 40 | 41 | /** `NSMutableDictionary` mapping column names to numeric index */ 42 | 43 | @property (readonly) NSMutableDictionary *columnNameToIndexMap; 44 | 45 | /** `FMStatement` used by result set. */ 46 | 47 | @property (atomic, retain) FMStatement *statement; 48 | 49 | ///------------------------------------ 50 | /// @name Creating and closing database 51 | ///------------------------------------ 52 | 53 | /** Create result set from `` 54 | 55 | @param statement A `` to be performed 56 | 57 | @param aDB A `` to be used 58 | 59 | @return A `FMResultSet` on success; `nil` on failure 60 | */ 61 | 62 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; 63 | 64 | /** Close result set */ 65 | 66 | - (void)close; 67 | 68 | - (void)setParentDB:(FMDatabase *)newDb; 69 | 70 | ///--------------------------------------- 71 | /// @name Iterating through the result set 72 | ///--------------------------------------- 73 | 74 | /** Retrieve next row for result set. 75 | 76 | You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. 77 | 78 | @return `YES` if row successfully retrieved; `NO` if end of result set reached 79 | 80 | @see hasAnotherRow 81 | */ 82 | 83 | - (BOOL)next; 84 | 85 | /** Retrieve next row for result set. 86 | 87 | You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. 88 | 89 | @param outErr A 'NSError' object to receive any error object (if any). 90 | 91 | @return 'YES' if row successfully retrieved; 'NO' if end of result set reached 92 | 93 | @see hasAnotherRow 94 | */ 95 | 96 | - (BOOL)nextWithError:(NSError **)outErr; 97 | 98 | /** Did the last call to `` succeed in retrieving another row? 99 | 100 | @return `YES` if the last call to `` succeeded in retrieving another record; `NO` if not. 101 | 102 | @see next 103 | 104 | @warning The `hasAnotherRow` method must follow a call to ``. If the previous database interaction was something other than a call to `next`, then this method may return `NO`, whether there is another row of data or not. 105 | */ 106 | 107 | - (BOOL)hasAnotherRow; 108 | 109 | ///--------------------------------------------- 110 | /// @name Retrieving information from result set 111 | ///--------------------------------------------- 112 | 113 | /** How many columns in result set 114 | 115 | @return Integer value of the number of columns. 116 | */ 117 | 118 | - (int)columnCount; 119 | 120 | /** Column index for column name 121 | 122 | @param columnName `NSString` value of the name of the column. 123 | 124 | @return Zero-based index for column. 125 | */ 126 | 127 | - (int)columnIndexForName:(NSString*)columnName; 128 | 129 | /** Column name for column index 130 | 131 | @param columnIdx Zero-based index for column. 132 | 133 | @return columnName `NSString` value of the name of the column. 134 | */ 135 | 136 | - (NSString*)columnNameForIndex:(int)columnIdx; 137 | 138 | /** Result set integer value for column. 139 | 140 | @param columnName `NSString` value of the name of the column. 141 | 142 | @return `int` value of the result set's column. 143 | */ 144 | 145 | - (int)intForColumn:(NSString*)columnName; 146 | 147 | /** Result set integer value for column. 148 | 149 | @param columnIdx Zero-based index for column. 150 | 151 | @return `int` value of the result set's column. 152 | */ 153 | 154 | - (int)intForColumnIndex:(int)columnIdx; 155 | 156 | /** Result set `long` value for column. 157 | 158 | @param columnName `NSString` value of the name of the column. 159 | 160 | @return `long` value of the result set's column. 161 | */ 162 | 163 | - (long)longForColumn:(NSString*)columnName; 164 | 165 | /** Result set long value for column. 166 | 167 | @param columnIdx Zero-based index for column. 168 | 169 | @return `long` value of the result set's column. 170 | */ 171 | 172 | - (long)longForColumnIndex:(int)columnIdx; 173 | 174 | /** Result set `long long int` value for column. 175 | 176 | @param columnName `NSString` value of the name of the column. 177 | 178 | @return `long long int` value of the result set's column. 179 | */ 180 | 181 | - (long long int)longLongIntForColumn:(NSString*)columnName; 182 | 183 | /** Result set `long long int` value for column. 184 | 185 | @param columnIdx Zero-based index for column. 186 | 187 | @return `long long int` value of the result set's column. 188 | */ 189 | 190 | - (long long int)longLongIntForColumnIndex:(int)columnIdx; 191 | 192 | /** Result set `unsigned long long int` value for column. 193 | 194 | @param columnName `NSString` value of the name of the column. 195 | 196 | @return `unsigned long long int` value of the result set's column. 197 | */ 198 | 199 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName; 200 | 201 | /** Result set `unsigned long long int` value for column. 202 | 203 | @param columnIdx Zero-based index for column. 204 | 205 | @return `unsigned long long int` value of the result set's column. 206 | */ 207 | 208 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx; 209 | 210 | /** Result set `BOOL` value for column. 211 | 212 | @param columnName `NSString` value of the name of the column. 213 | 214 | @return `BOOL` value of the result set's column. 215 | */ 216 | 217 | - (BOOL)boolForColumn:(NSString*)columnName; 218 | 219 | /** Result set `BOOL` value for column. 220 | 221 | @param columnIdx Zero-based index for column. 222 | 223 | @return `BOOL` value of the result set's column. 224 | */ 225 | 226 | - (BOOL)boolForColumnIndex:(int)columnIdx; 227 | 228 | /** Result set `double` value for column. 229 | 230 | @param columnName `NSString` value of the name of the column. 231 | 232 | @return `double` value of the result set's column. 233 | 234 | */ 235 | 236 | - (double)doubleForColumn:(NSString*)columnName; 237 | 238 | /** Result set `double` value for column. 239 | 240 | @param columnIdx Zero-based index for column. 241 | 242 | @return `double` value of the result set's column. 243 | 244 | */ 245 | 246 | - (double)doubleForColumnIndex:(int)columnIdx; 247 | 248 | /** Result set `NSString` value for column. 249 | 250 | @param columnName `NSString` value of the name of the column. 251 | 252 | @return `NSString` value of the result set's column. 253 | 254 | */ 255 | 256 | - (NSString*)stringForColumn:(NSString*)columnName; 257 | 258 | /** Result set `NSString` value for column. 259 | 260 | @param columnIdx Zero-based index for column. 261 | 262 | @return `NSString` value of the result set's column. 263 | */ 264 | 265 | - (NSString*)stringForColumnIndex:(int)columnIdx; 266 | 267 | /** Result set `NSDate` value for column. 268 | 269 | @param columnName `NSString` value of the name of the column. 270 | 271 | @return `NSDate` value of the result set's column. 272 | */ 273 | 274 | - (NSDate*)dateForColumn:(NSString*)columnName; 275 | 276 | /** Result set `NSDate` value for column. 277 | 278 | @param columnIdx Zero-based index for column. 279 | 280 | @return `NSDate` value of the result set's column. 281 | 282 | */ 283 | 284 | - (NSDate*)dateForColumnIndex:(int)columnIdx; 285 | 286 | /** Result set `NSData` value for column. 287 | 288 | This is useful when storing binary data in table (such as image or the like). 289 | 290 | @param columnName `NSString` value of the name of the column. 291 | 292 | @return `NSData` value of the result set's column. 293 | 294 | */ 295 | 296 | - (NSData*)dataForColumn:(NSString*)columnName; 297 | 298 | /** Result set `NSData` value for column. 299 | 300 | @param columnIdx Zero-based index for column. 301 | 302 | @return `NSData` value of the result set's column. 303 | */ 304 | 305 | - (NSData*)dataForColumnIndex:(int)columnIdx; 306 | 307 | /** Result set `(const unsigned char *)` value for column. 308 | 309 | @param columnName `NSString` value of the name of the column. 310 | 311 | @return `(const unsigned char *)` value of the result set's column. 312 | */ 313 | 314 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName; 315 | 316 | /** Result set `(const unsigned char *)` value for column. 317 | 318 | @param columnIdx Zero-based index for column. 319 | 320 | @return `(const unsigned char *)` value of the result set's column. 321 | */ 322 | 323 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx; 324 | 325 | /** Result set object for column. 326 | 327 | @param columnName `NSString` value of the name of the column. 328 | 329 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 330 | 331 | @see objectForKeyedSubscript: 332 | */ 333 | 334 | - (id)objectForColumnName:(NSString*)columnName; 335 | 336 | /** Result set object for column. 337 | 338 | @param columnIdx Zero-based index for column. 339 | 340 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 341 | 342 | @see objectAtIndexedSubscript: 343 | */ 344 | 345 | - (id)objectForColumnIndex:(int)columnIdx; 346 | 347 | /** Result set object for column. 348 | 349 | This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: 350 | 351 | id result = rs[@"employee_name"]; 352 | 353 | This simplified syntax is equivalent to calling: 354 | 355 | id result = [rs objectForKeyedSubscript:@"employee_name"]; 356 | 357 | which is, it turns out, equivalent to calling: 358 | 359 | id result = [rs objectForColumnName:@"employee_name"]; 360 | 361 | @param columnName `NSString` value of the name of the column. 362 | 363 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 364 | */ 365 | 366 | - (id)objectForKeyedSubscript:(NSString *)columnName; 367 | 368 | /** Result set object for column. 369 | 370 | This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: 371 | 372 | id result = rs[0]; 373 | 374 | This simplified syntax is equivalent to calling: 375 | 376 | id result = [rs objectForKeyedSubscript:0]; 377 | 378 | which is, it turns out, equivalent to calling: 379 | 380 | id result = [rs objectForColumnName:0]; 381 | 382 | @param columnIdx Zero-based index for column. 383 | 384 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 385 | */ 386 | 387 | - (id)objectAtIndexedSubscript:(int)columnIdx; 388 | 389 | /** Result set `NSData` value for column. 390 | 391 | @param columnName `NSString` value of the name of the column. 392 | 393 | @return `NSData` value of the result set's column. 394 | 395 | @warning If you are going to use this data after you iterate over the next row, or after you close the 396 | result set, make sure to make a copy of the data first (or just use ``/``) 397 | If you don't, you're going to be in a world of hurt when you try and use the data. 398 | 399 | */ 400 | 401 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED; 402 | 403 | /** Result set `NSData` value for column. 404 | 405 | @param columnIdx Zero-based index for column. 406 | 407 | @return `NSData` value of the result set's column. 408 | 409 | @warning If you are going to use this data after you iterate over the next row, or after you close the 410 | result set, make sure to make a copy of the data first (or just use ``/``) 411 | If you don't, you're going to be in a world of hurt when you try and use the data. 412 | 413 | */ 414 | 415 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; 416 | 417 | /** Is the column `NULL`? 418 | 419 | @param columnIdx Zero-based index for column. 420 | 421 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 422 | */ 423 | 424 | - (BOOL)columnIndexIsNull:(int)columnIdx; 425 | 426 | /** Is the column `NULL`? 427 | 428 | @param columnName `NSString` value of the name of the column. 429 | 430 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 431 | */ 432 | 433 | - (BOOL)columnIsNull:(NSString*)columnName; 434 | 435 | 436 | /** Returns a dictionary of the row results mapped to case sensitive keys of the column names. 437 | 438 | @returns `NSDictionary` of the row results. 439 | 440 | @warning The keys to the dictionary are case sensitive of the column names. 441 | */ 442 | 443 | - (NSDictionary*)resultDictionary; 444 | 445 | /** Returns a dictionary of the row results 446 | 447 | @see resultDictionary 448 | 449 | @warning **Deprecated**: Please use `` instead. Also, beware that `` is case sensitive! 450 | */ 451 | 452 | - (NSDictionary*)resultDict __attribute__ ((deprecated)); 453 | 454 | ///----------------------------- 455 | /// @name Key value coding magic 456 | ///----------------------------- 457 | 458 | /** Performs `setValue` to yield support for key value observing. 459 | 460 | @param object The object for which the values will be set. This is the key-value-coding compliant object that you might, for example, observe. 461 | 462 | */ 463 | 464 | - (void)kvcMagic:(id)object; 465 | 466 | 467 | @end 468 | 469 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/FMResultSet.m: -------------------------------------------------------------------------------- 1 | #import "FMResultSet.h" 2 | #import "FMDatabase.h" 3 | #import "unistd.h" 4 | 5 | #if FMDB_SQLITE_STANDALONE 6 | #import 7 | #else 8 | #import 9 | #endif 10 | 11 | @interface FMDatabase () 12 | - (void)resultSetDidClose:(FMResultSet *)resultSet; 13 | @end 14 | 15 | 16 | @implementation FMResultSet 17 | @synthesize query=_query; 18 | @synthesize statement=_statement; 19 | 20 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { 21 | 22 | FMResultSet *rs = [[FMResultSet alloc] init]; 23 | 24 | [rs setStatement:statement]; 25 | [rs setParentDB:aDB]; 26 | 27 | NSParameterAssert(![statement inUse]); 28 | [statement setInUse:YES]; // weak reference 29 | 30 | return FMDBReturnAutoreleased(rs); 31 | } 32 | 33 | - (void)finalize { 34 | [self close]; 35 | [super finalize]; 36 | } 37 | 38 | - (void)dealloc { 39 | [self close]; 40 | 41 | FMDBRelease(_query); 42 | _query = nil; 43 | 44 | FMDBRelease(_columnNameToIndexMap); 45 | _columnNameToIndexMap = nil; 46 | 47 | #if ! __has_feature(objc_arc) 48 | [super dealloc]; 49 | #endif 50 | } 51 | 52 | - (void)close { 53 | [_statement reset]; 54 | FMDBRelease(_statement); 55 | _statement = nil; 56 | 57 | // we don't need this anymore... (i think) 58 | //[_parentDB setInUse:NO]; 59 | [_parentDB resultSetDidClose:self]; 60 | _parentDB = nil; 61 | } 62 | 63 | - (int)columnCount { 64 | return sqlite3_column_count([_statement statement]); 65 | } 66 | 67 | - (NSMutableDictionary *)columnNameToIndexMap { 68 | if (!_columnNameToIndexMap) { 69 | int columnCount = sqlite3_column_count([_statement statement]); 70 | _columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount]; 71 | int columnIdx = 0; 72 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 73 | [_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] 74 | forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]]; 75 | } 76 | } 77 | return _columnNameToIndexMap; 78 | } 79 | 80 | - (void)kvcMagic:(id)object { 81 | 82 | int columnCount = sqlite3_column_count([_statement statement]); 83 | 84 | int columnIdx = 0; 85 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 86 | 87 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 88 | 89 | // check for a null row 90 | if (c) { 91 | NSString *s = [NSString stringWithUTF8String:c]; 92 | 93 | [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]]; 94 | } 95 | } 96 | } 97 | 98 | #pragma clang diagnostic push 99 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 100 | 101 | - (NSDictionary*)resultDict { 102 | 103 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 104 | 105 | if (num_cols > 0) { 106 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 107 | 108 | NSEnumerator *columnNames = [[self columnNameToIndexMap] keyEnumerator]; 109 | NSString *columnName = nil; 110 | while ((columnName = [columnNames nextObject])) { 111 | id objectValue = [self objectForColumnName:columnName]; 112 | [dict setObject:objectValue forKey:columnName]; 113 | } 114 | 115 | return FMDBReturnAutoreleased([dict copy]); 116 | } 117 | else { 118 | NSLog(@"Warning: There seem to be no columns in this set."); 119 | } 120 | 121 | return nil; 122 | } 123 | 124 | #pragma clang diagnostic pop 125 | 126 | - (NSDictionary*)resultDictionary { 127 | 128 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 129 | 130 | if (num_cols > 0) { 131 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 132 | 133 | int columnCount = sqlite3_column_count([_statement statement]); 134 | 135 | int columnIdx = 0; 136 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 137 | 138 | NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]; 139 | id objectValue = [self objectForColumnIndex:columnIdx]; 140 | [dict setObject:objectValue forKey:columnName]; 141 | } 142 | 143 | return dict; 144 | } 145 | else { 146 | NSLog(@"Warning: There seem to be no columns in this set."); 147 | } 148 | 149 | return nil; 150 | } 151 | 152 | 153 | 154 | 155 | - (BOOL)next { 156 | return [self nextWithError:nil]; 157 | } 158 | 159 | - (BOOL)nextWithError:(NSError **)outErr { 160 | 161 | int rc = sqlite3_step([_statement statement]); 162 | 163 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 164 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]); 165 | NSLog(@"Database busy"); 166 | if (outErr) { 167 | *outErr = [_parentDB lastError]; 168 | } 169 | } 170 | else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { 171 | // all is well, let's return. 172 | } 173 | else if (SQLITE_ERROR == rc) { 174 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 175 | if (outErr) { 176 | *outErr = [_parentDB lastError]; 177 | } 178 | } 179 | else if (SQLITE_MISUSE == rc) { 180 | // uh oh. 181 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 182 | if (outErr) { 183 | if (_parentDB) { 184 | *outErr = [_parentDB lastError]; 185 | } 186 | else { 187 | // If 'next' or 'nextWithError' is called after the result set is closed, 188 | // we need to return the appropriate error. 189 | NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:@"parentDB does not exist" forKey:NSLocalizedDescriptionKey]; 190 | *outErr = [NSError errorWithDomain:@"FMDatabase" code:SQLITE_MISUSE userInfo:errorMessage]; 191 | } 192 | 193 | } 194 | } 195 | else { 196 | // wtf? 197 | NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 198 | if (outErr) { 199 | *outErr = [_parentDB lastError]; 200 | } 201 | } 202 | 203 | 204 | if (rc != SQLITE_ROW) { 205 | [self close]; 206 | } 207 | 208 | return (rc == SQLITE_ROW); 209 | } 210 | 211 | - (BOOL)hasAnotherRow { 212 | return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW; 213 | } 214 | 215 | - (int)columnIndexForName:(NSString*)columnName { 216 | columnName = [columnName lowercaseString]; 217 | 218 | NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName]; 219 | 220 | if (n) { 221 | return [n intValue]; 222 | } 223 | 224 | NSLog(@"Warning: I could not find the column named '%@'.", columnName); 225 | 226 | return -1; 227 | } 228 | 229 | 230 | 231 | - (int)intForColumn:(NSString*)columnName { 232 | return [self intForColumnIndex:[self columnIndexForName:columnName]]; 233 | } 234 | 235 | - (int)intForColumnIndex:(int)columnIdx { 236 | return sqlite3_column_int([_statement statement], columnIdx); 237 | } 238 | 239 | - (long)longForColumn:(NSString*)columnName { 240 | return [self longForColumnIndex:[self columnIndexForName:columnName]]; 241 | } 242 | 243 | - (long)longForColumnIndex:(int)columnIdx { 244 | return (long)sqlite3_column_int64([_statement statement], columnIdx); 245 | } 246 | 247 | - (long long int)longLongIntForColumn:(NSString*)columnName { 248 | return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; 249 | } 250 | 251 | - (long long int)longLongIntForColumnIndex:(int)columnIdx { 252 | return sqlite3_column_int64([_statement statement], columnIdx); 253 | } 254 | 255 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName { 256 | return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]]; 257 | } 258 | 259 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx { 260 | return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx]; 261 | } 262 | 263 | - (BOOL)boolForColumn:(NSString*)columnName { 264 | return [self boolForColumnIndex:[self columnIndexForName:columnName]]; 265 | } 266 | 267 | - (BOOL)boolForColumnIndex:(int)columnIdx { 268 | return ([self intForColumnIndex:columnIdx] != 0); 269 | } 270 | 271 | - (double)doubleForColumn:(NSString*)columnName { 272 | return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; 273 | } 274 | 275 | - (double)doubleForColumnIndex:(int)columnIdx { 276 | return sqlite3_column_double([_statement statement], columnIdx); 277 | } 278 | 279 | - (NSString*)stringForColumnIndex:(int)columnIdx { 280 | 281 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 282 | return nil; 283 | } 284 | 285 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 286 | 287 | if (!c) { 288 | // null row. 289 | return nil; 290 | } 291 | 292 | return [NSString stringWithUTF8String:c]; 293 | } 294 | 295 | - (NSString*)stringForColumn:(NSString*)columnName { 296 | return [self stringForColumnIndex:[self columnIndexForName:columnName]]; 297 | } 298 | 299 | - (NSDate*)dateForColumn:(NSString*)columnName { 300 | return [self dateForColumnIndex:[self columnIndexForName:columnName]]; 301 | } 302 | 303 | - (NSDate*)dateForColumnIndex:(int)columnIdx { 304 | 305 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 306 | return nil; 307 | } 308 | 309 | return [_parentDB hasDateFormatter] ? [_parentDB dateFromString:[self stringForColumnIndex:columnIdx]] : [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; 310 | } 311 | 312 | 313 | - (NSData*)dataForColumn:(NSString*)columnName { 314 | return [self dataForColumnIndex:[self columnIndexForName:columnName]]; 315 | } 316 | 317 | - (NSData*)dataForColumnIndex:(int)columnIdx { 318 | 319 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 320 | return nil; 321 | } 322 | 323 | const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); 324 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 325 | 326 | if (dataBuffer == NULL) { 327 | return nil; 328 | } 329 | 330 | return [NSData dataWithBytes:(const void *)dataBuffer length:(NSUInteger)dataSize]; 331 | } 332 | 333 | 334 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName { 335 | return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; 336 | } 337 | 338 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { 339 | 340 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 341 | return nil; 342 | } 343 | 344 | const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); 345 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 346 | 347 | NSData *data = [NSData dataWithBytesNoCopy:(void *)dataBuffer length:(NSUInteger)dataSize freeWhenDone:NO]; 348 | 349 | return data; 350 | } 351 | 352 | 353 | - (BOOL)columnIndexIsNull:(int)columnIdx { 354 | return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL; 355 | } 356 | 357 | - (BOOL)columnIsNull:(NSString*)columnName { 358 | return [self columnIndexIsNull:[self columnIndexForName:columnName]]; 359 | } 360 | 361 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { 362 | 363 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 364 | return nil; 365 | } 366 | 367 | return sqlite3_column_text([_statement statement], columnIdx); 368 | } 369 | 370 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { 371 | return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; 372 | } 373 | 374 | - (id)objectForColumnIndex:(int)columnIdx { 375 | int columnType = sqlite3_column_type([_statement statement], columnIdx); 376 | 377 | id returnValue = nil; 378 | 379 | if (columnType == SQLITE_INTEGER) { 380 | returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]]; 381 | } 382 | else if (columnType == SQLITE_FLOAT) { 383 | returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]]; 384 | } 385 | else if (columnType == SQLITE_BLOB) { 386 | returnValue = [self dataForColumnIndex:columnIdx]; 387 | } 388 | else { 389 | //default to a string for everything else 390 | returnValue = [self stringForColumnIndex:columnIdx]; 391 | } 392 | 393 | if (returnValue == nil) { 394 | returnValue = [NSNull null]; 395 | } 396 | 397 | return returnValue; 398 | } 399 | 400 | - (id)objectForColumnName:(NSString*)columnName { 401 | return [self objectForColumnIndex:[self columnIndexForName:columnName]]; 402 | } 403 | 404 | // returns autoreleased NSString containing the name of the column in the result set 405 | - (NSString*)columnNameForIndex:(int)columnIdx { 406 | return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)]; 407 | } 408 | 409 | - (void)setParentDB:(FMDatabase *)newDb { 410 | _parentDB = newDb; 411 | } 412 | 413 | - (id)objectAtIndexedSubscript:(int)columnIdx { 414 | return [self objectForColumnIndex:columnIdx]; 415 | } 416 | 417 | - (id)objectForKeyedSubscript:(NSString *)columnName { 418 | return [self objectForColumnName:columnName]; 419 | } 420 | 421 | 422 | @end 423 | -------------------------------------------------------------------------------- /ChooseLocation/fmdb/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.6.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ChooseLocation/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ChooseLocation 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. 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 | -------------------------------------------------------------------------------- /ChooseLocationTests/ChooseLocationTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChooseLocationTests.m 3 | // ChooseLocationTests 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChooseLocationTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ChooseLocationTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ChooseLocationTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ChooseLocationUITests/ChooseLocationUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChooseLocationUITests.m 3 | // ChooseLocationUITests 4 | // 5 | // Created by Sekorm on 16/8/22. 6 | // Copyright © 2016年 HY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChooseLocationUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ChooseLocationUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ChooseLocationUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChooseLocation 2 | 京东选择收货地址 3 | 4 | 5 | ###导读 6 | 目前大多数APP的地址选择是用系统的picker View,也不乏用tableview自定义的. 7 | 这里分享一个高仿京东的地址选择给大家. 8 | 源码地址:https://github.com/HelloYeah/ChooseLocation 9 | 欢迎大家checkout,Star... 10 | 11 | #####下面是京东收货地址的一些交互以及代码思路分析 12 | 13 | >1.刚打开选择地址视图时,底部ScrollView的滚动范围只有一屏宽. 14 | 15 | >2.点击某个省时,增加对应的市级列表,底部ScrollView横向滚动区域增加一屏宽. 16 | 17 | ![1.gif](http://upload-images.jianshu.io/upload_images/1338042-16ffa01913c5ccf6.gif?imageMogr2/auto-orient/strip) 18 | 19 | >1.当重新选择省的时候,移除后面的市级别列表,区级别列表 20 | 21 | >2.移除顶部的市按钮,区按钮. 22 | 23 | >3.并且底部ScrollView的滚动范围减少至两屏宽. 24 | 25 | ![2.gif](http://upload-images.jianshu.io/upload_images/1338042-7bc0307bf43ebf45.gif?imageMogr2/auto-orient/strip) 26 | 27 | 28 | 29 | >1.当重新选择省市的时候,对应顶部按钮的宽度跟着改变,对应下级的按钮的x值要相应调整 30 | >2.按钮底部的指示条的长度和位置跟着相应变化 31 | 32 | ![tmp5deefbb7.png](http://upload-images.jianshu.io/upload_images/1338042-78137181ccaaad4e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 33 | 34 | 35 | #####其他注意点 36 | >1.点击灰色区域,取消地址选择,回到主界面 37 | 38 | >2.京东用的是网络请求获取省市区信息,每点击一个cell,向服务器发送请求,获取下级信息.这里用的是本地json数据 39 | 40 | 41 | 42 | 43 | 44 | 源码地址:https://github.com/HelloYeah/ChooseLocation 45 | 欢迎大家checkout,Star... 46 | --------------------------------------------------------------------------------