├── 111.gif ├── README.md ├── SoolyCitySelector.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── SoolyChristina.xcuserdatad │ └── xcschemes │ ├── SoolyCitySelector.xcscheme │ └── xcschememanagement.plist ├── SoolyCitySelector ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CitySelectorViewController.swift ├── CurrentCityTableViewCell.swift ├── Header.swift ├── HotCityTableViewCell.swift ├── Info.plist ├── RecentCitiesableViewCell.swift ├── ResultTableViewController.swift ├── String+SoolyCitySelector.swift ├── UIColor+Hex.swift ├── cities.plist └── hotCities.plist ├── SoolyCitySelectorTests ├── Info.plist └── SoolyCitySelectorTests.swift ├── SoolyCitySelectorUITests ├── Info.plist └── SoolyCitySelectorUITests.swift └── demo.png /111.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoolyChristy/SoolyCitySelector/36192a78eacd0c02422db6364a645d4cd6db0316/111.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SoolyCitySelector 2 | - 类似于美团/支付宝 城市选择器带拼音搜索 3 | 4 | ![](https://raw.githubusercontent.com/SoolyChristy/SoolyCitySelector/master/demo.png) 5 | ![](https://raw.githubusercontent.com/SoolyChristy/SoolyCitySelector/master/111.gif) 6 | -------------------------------------------------------------------------------- /SoolyCitySelector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 704842281E8E2AD20006D1B0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842271E8E2AD20006D1B0 /* AppDelegate.swift */; }; 11 | 7048422D1E8E2AD20006D1B0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7048422B1E8E2AD20006D1B0 /* Main.storyboard */; }; 12 | 7048422F1E8E2AD30006D1B0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7048422E1E8E2AD30006D1B0 /* Assets.xcassets */; }; 13 | 704842321E8E2AD30006D1B0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 704842301E8E2AD30006D1B0 /* LaunchScreen.storyboard */; }; 14 | 7048423D1E8E2AD40006D1B0 /* SoolyCitySelectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7048423C1E8E2AD40006D1B0 /* SoolyCitySelectorTests.swift */; }; 15 | 704842481E8E2AD50006D1B0 /* SoolyCitySelectorUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842471E8E2AD50006D1B0 /* SoolyCitySelectorUITests.swift */; }; 16 | 7048425C1E8E2B5C0006D1B0 /* cities.plist in Resources */ = {isa = PBXBuildFile; fileRef = 704842551E8E2B5C0006D1B0 /* cities.plist */; }; 17 | 7048425E1E8E2B5C0006D1B0 /* CitySelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842571E8E2B5C0006D1B0 /* CitySelectorViewController.swift */; }; 18 | 7048425F1E8E2B5C0006D1B0 /* CurrentCityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842581E8E2B5C0006D1B0 /* CurrentCityTableViewCell.swift */; }; 19 | 704842601E8E2B5C0006D1B0 /* HotCityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842591E8E2B5C0006D1B0 /* HotCityTableViewCell.swift */; }; 20 | 704842611E8E2B5C0006D1B0 /* RecentCitiesableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7048425A1E8E2B5C0006D1B0 /* RecentCitiesableViewCell.swift */; }; 21 | 704842621E8E2B5C0006D1B0 /* ResultTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7048425B1E8E2B5C0006D1B0 /* ResultTableViewController.swift */; }; 22 | 704842641E8E2BA70006D1B0 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842631E8E2BA70006D1B0 /* Header.swift */; }; 23 | 704842661E8E2C080006D1B0 /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842651E8E2C080006D1B0 /* UIColor+Hex.swift */; }; 24 | 704842681E8E2C8C0006D1B0 /* String+SoolyCitySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704842671E8E2C8C0006D1B0 /* String+SoolyCitySelector.swift */; }; 25 | 7048426A1E8E2F630006D1B0 /* hotCities.plist in Resources */ = {isa = PBXBuildFile; fileRef = 704842691E8E2F630006D1B0 /* hotCities.plist */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 704842391E8E2AD30006D1B0 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 7048421C1E8E2AD10006D1B0 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 704842231E8E2AD10006D1B0; 34 | remoteInfo = SoolyCitySelector; 35 | }; 36 | 704842441E8E2AD40006D1B0 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 7048421C1E8E2AD10006D1B0 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 704842231E8E2AD10006D1B0; 41 | remoteInfo = SoolyCitySelector; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 704842241E8E2AD10006D1B0 /* SoolyCitySelector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SoolyCitySelector.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 704842271E8E2AD20006D1B0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 48 | 7048422C1E8E2AD20006D1B0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | 7048422E1E8E2AD30006D1B0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | 704842311E8E2AD30006D1B0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 51 | 704842331E8E2AD30006D1B0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | 704842381E8E2AD30006D1B0 /* SoolyCitySelectorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SoolyCitySelectorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 7048423C1E8E2AD40006D1B0 /* SoolyCitySelectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoolyCitySelectorTests.swift; sourceTree = ""; }; 54 | 7048423E1E8E2AD40006D1B0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 704842431E8E2AD40006D1B0 /* SoolyCitySelectorUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SoolyCitySelectorUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 704842471E8E2AD50006D1B0 /* SoolyCitySelectorUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoolyCitySelectorUITests.swift; sourceTree = ""; }; 57 | 704842491E8E2AD60006D1B0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | 704842551E8E2B5C0006D1B0 /* cities.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = cities.plist; sourceTree = ""; }; 59 | 704842571E8E2B5C0006D1B0 /* CitySelectorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CitySelectorViewController.swift; sourceTree = ""; }; 60 | 704842581E8E2B5C0006D1B0 /* CurrentCityTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentCityTableViewCell.swift; sourceTree = ""; }; 61 | 704842591E8E2B5C0006D1B0 /* HotCityTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotCityTableViewCell.swift; sourceTree = ""; }; 62 | 7048425A1E8E2B5C0006D1B0 /* RecentCitiesableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentCitiesableViewCell.swift; sourceTree = ""; }; 63 | 7048425B1E8E2B5C0006D1B0 /* ResultTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultTableViewController.swift; sourceTree = ""; }; 64 | 704842631E8E2BA70006D1B0 /* Header.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; }; 65 | 704842651E8E2C080006D1B0 /* UIColor+Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = ""; }; 66 | 704842671E8E2C8C0006D1B0 /* String+SoolyCitySelector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SoolyCitySelector.swift"; sourceTree = ""; }; 67 | 704842691E8E2F630006D1B0 /* hotCities.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = hotCities.plist; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 704842211E8E2AD10006D1B0 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 704842351E8E2AD30006D1B0 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | 704842401E8E2AD40006D1B0 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | /* End PBXFrameworksBuildPhase section */ 93 | 94 | /* Begin PBXGroup section */ 95 | 7048421B1E8E2AD10006D1B0 = { 96 | isa = PBXGroup; 97 | children = ( 98 | 704842261E8E2AD10006D1B0 /* SoolyCitySelector */, 99 | 7048423B1E8E2AD30006D1B0 /* SoolyCitySelectorTests */, 100 | 704842461E8E2AD50006D1B0 /* SoolyCitySelectorUITests */, 101 | 704842251E8E2AD10006D1B0 /* Products */, 102 | ); 103 | sourceTree = ""; 104 | }; 105 | 704842251E8E2AD10006D1B0 /* Products */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 704842241E8E2AD10006D1B0 /* SoolyCitySelector.app */, 109 | 704842381E8E2AD30006D1B0 /* SoolyCitySelectorTests.xctest */, 110 | 704842431E8E2AD40006D1B0 /* SoolyCitySelectorUITests.xctest */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 704842261E8E2AD10006D1B0 /* SoolyCitySelector */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 704842271E8E2AD20006D1B0 /* AppDelegate.swift */, 119 | 704842571E8E2B5C0006D1B0 /* CitySelectorViewController.swift */, 120 | 7048425B1E8E2B5C0006D1B0 /* ResultTableViewController.swift */, 121 | 7048426D1E8E30E40006D1B0 /* Cell */, 122 | 7048426C1E8E30C10006D1B0 /* Extension */, 123 | 7048426B1E8E30A20006D1B0 /* Resource */, 124 | 704842301E8E2AD30006D1B0 /* LaunchScreen.storyboard */, 125 | 7048422B1E8E2AD20006D1B0 /* Main.storyboard */, 126 | ); 127 | path = SoolyCitySelector; 128 | sourceTree = ""; 129 | }; 130 | 7048423B1E8E2AD30006D1B0 /* SoolyCitySelectorTests */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 7048423C1E8E2AD40006D1B0 /* SoolyCitySelectorTests.swift */, 134 | 7048423E1E8E2AD40006D1B0 /* Info.plist */, 135 | ); 136 | path = SoolyCitySelectorTests; 137 | sourceTree = ""; 138 | }; 139 | 704842461E8E2AD50006D1B0 /* SoolyCitySelectorUITests */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 704842471E8E2AD50006D1B0 /* SoolyCitySelectorUITests.swift */, 143 | 704842491E8E2AD60006D1B0 /* Info.plist */, 144 | ); 145 | path = SoolyCitySelectorUITests; 146 | sourceTree = ""; 147 | }; 148 | 7048426B1E8E30A20006D1B0 /* Resource */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 704842631E8E2BA70006D1B0 /* Header.swift */, 152 | 704842551E8E2B5C0006D1B0 /* cities.plist */, 153 | 704842691E8E2F630006D1B0 /* hotCities.plist */, 154 | 704842331E8E2AD30006D1B0 /* Info.plist */, 155 | 7048422E1E8E2AD30006D1B0 /* Assets.xcassets */, 156 | ); 157 | name = Resource; 158 | sourceTree = ""; 159 | }; 160 | 7048426C1E8E30C10006D1B0 /* Extension */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 704842651E8E2C080006D1B0 /* UIColor+Hex.swift */, 164 | 704842671E8E2C8C0006D1B0 /* String+SoolyCitySelector.swift */, 165 | ); 166 | name = Extension; 167 | sourceTree = ""; 168 | }; 169 | 7048426D1E8E30E40006D1B0 /* Cell */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 704842591E8E2B5C0006D1B0 /* HotCityTableViewCell.swift */, 173 | 7048425A1E8E2B5C0006D1B0 /* RecentCitiesableViewCell.swift */, 174 | 704842581E8E2B5C0006D1B0 /* CurrentCityTableViewCell.swift */, 175 | ); 176 | name = Cell; 177 | sourceTree = ""; 178 | }; 179 | /* End PBXGroup section */ 180 | 181 | /* Begin PBXNativeTarget section */ 182 | 704842231E8E2AD10006D1B0 /* SoolyCitySelector */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 7048424C1E8E2AD60006D1B0 /* Build configuration list for PBXNativeTarget "SoolyCitySelector" */; 185 | buildPhases = ( 186 | 704842201E8E2AD10006D1B0 /* Sources */, 187 | 704842211E8E2AD10006D1B0 /* Frameworks */, 188 | 704842221E8E2AD10006D1B0 /* Resources */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | ); 194 | name = SoolyCitySelector; 195 | productName = SoolyCitySelector; 196 | productReference = 704842241E8E2AD10006D1B0 /* SoolyCitySelector.app */; 197 | productType = "com.apple.product-type.application"; 198 | }; 199 | 704842371E8E2AD30006D1B0 /* SoolyCitySelectorTests */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = 7048424F1E8E2AD60006D1B0 /* Build configuration list for PBXNativeTarget "SoolyCitySelectorTests" */; 202 | buildPhases = ( 203 | 704842341E8E2AD30006D1B0 /* Sources */, 204 | 704842351E8E2AD30006D1B0 /* Frameworks */, 205 | 704842361E8E2AD30006D1B0 /* Resources */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | 7048423A1E8E2AD30006D1B0 /* PBXTargetDependency */, 211 | ); 212 | name = SoolyCitySelectorTests; 213 | productName = SoolyCitySelectorTests; 214 | productReference = 704842381E8E2AD30006D1B0 /* SoolyCitySelectorTests.xctest */; 215 | productType = "com.apple.product-type.bundle.unit-test"; 216 | }; 217 | 704842421E8E2AD40006D1B0 /* SoolyCitySelectorUITests */ = { 218 | isa = PBXNativeTarget; 219 | buildConfigurationList = 704842521E8E2AD60006D1B0 /* Build configuration list for PBXNativeTarget "SoolyCitySelectorUITests" */; 220 | buildPhases = ( 221 | 7048423F1E8E2AD40006D1B0 /* Sources */, 222 | 704842401E8E2AD40006D1B0 /* Frameworks */, 223 | 704842411E8E2AD40006D1B0 /* Resources */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | 704842451E8E2AD40006D1B0 /* PBXTargetDependency */, 229 | ); 230 | name = SoolyCitySelectorUITests; 231 | productName = SoolyCitySelectorUITests; 232 | productReference = 704842431E8E2AD40006D1B0 /* SoolyCitySelectorUITests.xctest */; 233 | productType = "com.apple.product-type.bundle.ui-testing"; 234 | }; 235 | /* End PBXNativeTarget section */ 236 | 237 | /* Begin PBXProject section */ 238 | 7048421C1E8E2AD10006D1B0 /* Project object */ = { 239 | isa = PBXProject; 240 | attributes = { 241 | LastSwiftUpdateCheck = 0820; 242 | LastUpgradeCheck = 0820; 243 | ORGANIZATIONNAME = SoolyChristina; 244 | TargetAttributes = { 245 | 704842231E8E2AD10006D1B0 = { 246 | CreatedOnToolsVersion = 8.2.1; 247 | DevelopmentTeam = 7523YW79F8; 248 | ProvisioningStyle = Automatic; 249 | }; 250 | 704842371E8E2AD30006D1B0 = { 251 | CreatedOnToolsVersion = 8.2.1; 252 | ProvisioningStyle = Automatic; 253 | TestTargetID = 704842231E8E2AD10006D1B0; 254 | }; 255 | 704842421E8E2AD40006D1B0 = { 256 | CreatedOnToolsVersion = 8.2.1; 257 | ProvisioningStyle = Automatic; 258 | TestTargetID = 704842231E8E2AD10006D1B0; 259 | }; 260 | }; 261 | }; 262 | buildConfigurationList = 7048421F1E8E2AD10006D1B0 /* Build configuration list for PBXProject "SoolyCitySelector" */; 263 | compatibilityVersion = "Xcode 3.2"; 264 | developmentRegion = English; 265 | hasScannedForEncodings = 0; 266 | knownRegions = ( 267 | en, 268 | Base, 269 | ); 270 | mainGroup = 7048421B1E8E2AD10006D1B0; 271 | productRefGroup = 704842251E8E2AD10006D1B0 /* Products */; 272 | projectDirPath = ""; 273 | projectRoot = ""; 274 | targets = ( 275 | 704842231E8E2AD10006D1B0 /* SoolyCitySelector */, 276 | 704842371E8E2AD30006D1B0 /* SoolyCitySelectorTests */, 277 | 704842421E8E2AD40006D1B0 /* SoolyCitySelectorUITests */, 278 | ); 279 | }; 280 | /* End PBXProject section */ 281 | 282 | /* Begin PBXResourcesBuildPhase section */ 283 | 704842221E8E2AD10006D1B0 /* Resources */ = { 284 | isa = PBXResourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | 704842321E8E2AD30006D1B0 /* LaunchScreen.storyboard in Resources */, 288 | 7048422F1E8E2AD30006D1B0 /* Assets.xcassets in Resources */, 289 | 7048422D1E8E2AD20006D1B0 /* Main.storyboard in Resources */, 290 | 7048426A1E8E2F630006D1B0 /* hotCities.plist in Resources */, 291 | 7048425C1E8E2B5C0006D1B0 /* cities.plist in Resources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | 704842361E8E2AD30006D1B0 /* Resources */ = { 296 | isa = PBXResourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | }; 302 | 704842411E8E2AD40006D1B0 /* Resources */ = { 303 | isa = PBXResourcesBuildPhase; 304 | buildActionMask = 2147483647; 305 | files = ( 306 | ); 307 | runOnlyForDeploymentPostprocessing = 0; 308 | }; 309 | /* End PBXResourcesBuildPhase section */ 310 | 311 | /* Begin PBXSourcesBuildPhase section */ 312 | 704842201E8E2AD10006D1B0 /* Sources */ = { 313 | isa = PBXSourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | 704842621E8E2B5C0006D1B0 /* ResultTableViewController.swift in Sources */, 317 | 704842661E8E2C080006D1B0 /* UIColor+Hex.swift in Sources */, 318 | 704842611E8E2B5C0006D1B0 /* RecentCitiesableViewCell.swift in Sources */, 319 | 7048425E1E8E2B5C0006D1B0 /* CitySelectorViewController.swift in Sources */, 320 | 704842641E8E2BA70006D1B0 /* Header.swift in Sources */, 321 | 704842681E8E2C8C0006D1B0 /* String+SoolyCitySelector.swift in Sources */, 322 | 7048425F1E8E2B5C0006D1B0 /* CurrentCityTableViewCell.swift in Sources */, 323 | 704842601E8E2B5C0006D1B0 /* HotCityTableViewCell.swift in Sources */, 324 | 704842281E8E2AD20006D1B0 /* AppDelegate.swift in Sources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 704842341E8E2AD30006D1B0 /* Sources */ = { 329 | isa = PBXSourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 7048423D1E8E2AD40006D1B0 /* SoolyCitySelectorTests.swift in Sources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | 7048423F1E8E2AD40006D1B0 /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 704842481E8E2AD50006D1B0 /* SoolyCitySelectorUITests.swift in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | /* End PBXSourcesBuildPhase section */ 345 | 346 | /* Begin PBXTargetDependency section */ 347 | 7048423A1E8E2AD30006D1B0 /* PBXTargetDependency */ = { 348 | isa = PBXTargetDependency; 349 | target = 704842231E8E2AD10006D1B0 /* SoolyCitySelector */; 350 | targetProxy = 704842391E8E2AD30006D1B0 /* PBXContainerItemProxy */; 351 | }; 352 | 704842451E8E2AD40006D1B0 /* PBXTargetDependency */ = { 353 | isa = PBXTargetDependency; 354 | target = 704842231E8E2AD10006D1B0 /* SoolyCitySelector */; 355 | targetProxy = 704842441E8E2AD40006D1B0 /* PBXContainerItemProxy */; 356 | }; 357 | /* End PBXTargetDependency section */ 358 | 359 | /* Begin PBXVariantGroup section */ 360 | 7048422B1E8E2AD20006D1B0 /* Main.storyboard */ = { 361 | isa = PBXVariantGroup; 362 | children = ( 363 | 7048422C1E8E2AD20006D1B0 /* Base */, 364 | ); 365 | name = Main.storyboard; 366 | sourceTree = ""; 367 | }; 368 | 704842301E8E2AD30006D1B0 /* LaunchScreen.storyboard */ = { 369 | isa = PBXVariantGroup; 370 | children = ( 371 | 704842311E8E2AD30006D1B0 /* Base */, 372 | ); 373 | name = LaunchScreen.storyboard; 374 | sourceTree = ""; 375 | }; 376 | /* End PBXVariantGroup section */ 377 | 378 | /* Begin XCBuildConfiguration section */ 379 | 7048424A1E8E2AD60006D1B0 /* Debug */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ALWAYS_SEARCH_USER_PATHS = NO; 383 | CLANG_ANALYZER_NONNULL = YES; 384 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 385 | CLANG_CXX_LIBRARY = "libc++"; 386 | CLANG_ENABLE_MODULES = YES; 387 | CLANG_ENABLE_OBJC_ARC = YES; 388 | CLANG_WARN_BOOL_CONVERSION = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 391 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 392 | CLANG_WARN_EMPTY_BODY = YES; 393 | CLANG_WARN_ENUM_CONVERSION = YES; 394 | CLANG_WARN_INFINITE_RECURSION = YES; 395 | CLANG_WARN_INT_CONVERSION = YES; 396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 397 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 398 | CLANG_WARN_UNREACHABLE_CODE = YES; 399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 400 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 401 | COPY_PHASE_STRIP = NO; 402 | DEBUG_INFORMATION_FORMAT = dwarf; 403 | ENABLE_STRICT_OBJC_MSGSEND = YES; 404 | ENABLE_TESTABILITY = YES; 405 | GCC_C_LANGUAGE_STANDARD = gnu99; 406 | GCC_DYNAMIC_NO_PIC = NO; 407 | GCC_NO_COMMON_BLOCKS = YES; 408 | GCC_OPTIMIZATION_LEVEL = 0; 409 | GCC_PREPROCESSOR_DEFINITIONS = ( 410 | "DEBUG=1", 411 | "$(inherited)", 412 | ); 413 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 414 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 415 | GCC_WARN_UNDECLARED_SELECTOR = YES; 416 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 417 | GCC_WARN_UNUSED_FUNCTION = YES; 418 | GCC_WARN_UNUSED_VARIABLE = YES; 419 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 420 | MTL_ENABLE_DEBUG_INFO = YES; 421 | ONLY_ACTIVE_ARCH = YES; 422 | SDKROOT = iphoneos; 423 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 424 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 425 | TARGETED_DEVICE_FAMILY = "1,2"; 426 | }; 427 | name = Debug; 428 | }; 429 | 7048424B1E8E2AD60006D1B0 /* Release */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ALWAYS_SEARCH_USER_PATHS = NO; 433 | CLANG_ANALYZER_NONNULL = YES; 434 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 435 | CLANG_CXX_LIBRARY = "libc++"; 436 | CLANG_ENABLE_MODULES = YES; 437 | CLANG_ENABLE_OBJC_ARC = YES; 438 | CLANG_WARN_BOOL_CONVERSION = YES; 439 | CLANG_WARN_CONSTANT_CONVERSION = YES; 440 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 441 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 442 | CLANG_WARN_EMPTY_BODY = YES; 443 | CLANG_WARN_ENUM_CONVERSION = YES; 444 | CLANG_WARN_INFINITE_RECURSION = YES; 445 | CLANG_WARN_INT_CONVERSION = YES; 446 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 447 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 448 | CLANG_WARN_UNREACHABLE_CODE = YES; 449 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 450 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 451 | COPY_PHASE_STRIP = NO; 452 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 453 | ENABLE_NS_ASSERTIONS = NO; 454 | ENABLE_STRICT_OBJC_MSGSEND = YES; 455 | GCC_C_LANGUAGE_STANDARD = gnu99; 456 | GCC_NO_COMMON_BLOCKS = YES; 457 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 458 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 459 | GCC_WARN_UNDECLARED_SELECTOR = YES; 460 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 461 | GCC_WARN_UNUSED_FUNCTION = YES; 462 | GCC_WARN_UNUSED_VARIABLE = YES; 463 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 464 | MTL_ENABLE_DEBUG_INFO = NO; 465 | SDKROOT = iphoneos; 466 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 467 | TARGETED_DEVICE_FAMILY = "1,2"; 468 | VALIDATE_PRODUCT = YES; 469 | }; 470 | name = Release; 471 | }; 472 | 7048424D1E8E2AD60006D1B0 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 476 | DEVELOPMENT_TEAM = 7523YW79F8; 477 | INFOPLIST_FILE = SoolyCitySelector/Info.plist; 478 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 479 | PRODUCT_BUNDLE_IDENTIFIER = "-.SoolyCitySelector"; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | SWIFT_VERSION = 3.0; 482 | }; 483 | name = Debug; 484 | }; 485 | 7048424E1E8E2AD60006D1B0 /* Release */ = { 486 | isa = XCBuildConfiguration; 487 | buildSettings = { 488 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 489 | DEVELOPMENT_TEAM = 7523YW79F8; 490 | INFOPLIST_FILE = SoolyCitySelector/Info.plist; 491 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 492 | PRODUCT_BUNDLE_IDENTIFIER = "-.SoolyCitySelector"; 493 | PRODUCT_NAME = "$(TARGET_NAME)"; 494 | SWIFT_VERSION = 3.0; 495 | }; 496 | name = Release; 497 | }; 498 | 704842501E8E2AD60006D1B0 /* Debug */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 502 | BUNDLE_LOADER = "$(TEST_HOST)"; 503 | INFOPLIST_FILE = SoolyCitySelectorTests/Info.plist; 504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 505 | PRODUCT_BUNDLE_IDENTIFIER = "-.SoolyCitySelectorTests"; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | SWIFT_VERSION = 3.0; 508 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SoolyCitySelector.app/SoolyCitySelector"; 509 | }; 510 | name = Debug; 511 | }; 512 | 704842511E8E2AD60006D1B0 /* Release */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 516 | BUNDLE_LOADER = "$(TEST_HOST)"; 517 | INFOPLIST_FILE = SoolyCitySelectorTests/Info.plist; 518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 519 | PRODUCT_BUNDLE_IDENTIFIER = "-.SoolyCitySelectorTests"; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | SWIFT_VERSION = 3.0; 522 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SoolyCitySelector.app/SoolyCitySelector"; 523 | }; 524 | name = Release; 525 | }; 526 | 704842531E8E2AD60006D1B0 /* Debug */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 530 | INFOPLIST_FILE = SoolyCitySelectorUITests/Info.plist; 531 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 532 | PRODUCT_BUNDLE_IDENTIFIER = "-.SoolyCitySelectorUITests"; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | SWIFT_VERSION = 3.0; 535 | TEST_TARGET_NAME = SoolyCitySelector; 536 | }; 537 | name = Debug; 538 | }; 539 | 704842541E8E2AD60006D1B0 /* Release */ = { 540 | isa = XCBuildConfiguration; 541 | buildSettings = { 542 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 543 | INFOPLIST_FILE = SoolyCitySelectorUITests/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 545 | PRODUCT_BUNDLE_IDENTIFIER = "-.SoolyCitySelectorUITests"; 546 | PRODUCT_NAME = "$(TARGET_NAME)"; 547 | SWIFT_VERSION = 3.0; 548 | TEST_TARGET_NAME = SoolyCitySelector; 549 | }; 550 | name = Release; 551 | }; 552 | /* End XCBuildConfiguration section */ 553 | 554 | /* Begin XCConfigurationList section */ 555 | 7048421F1E8E2AD10006D1B0 /* Build configuration list for PBXProject "SoolyCitySelector" */ = { 556 | isa = XCConfigurationList; 557 | buildConfigurations = ( 558 | 7048424A1E8E2AD60006D1B0 /* Debug */, 559 | 7048424B1E8E2AD60006D1B0 /* Release */, 560 | ); 561 | defaultConfigurationIsVisible = 0; 562 | defaultConfigurationName = Release; 563 | }; 564 | 7048424C1E8E2AD60006D1B0 /* Build configuration list for PBXNativeTarget "SoolyCitySelector" */ = { 565 | isa = XCConfigurationList; 566 | buildConfigurations = ( 567 | 7048424D1E8E2AD60006D1B0 /* Debug */, 568 | 7048424E1E8E2AD60006D1B0 /* Release */, 569 | ); 570 | defaultConfigurationIsVisible = 0; 571 | }; 572 | 7048424F1E8E2AD60006D1B0 /* Build configuration list for PBXNativeTarget "SoolyCitySelectorTests" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 704842501E8E2AD60006D1B0 /* Debug */, 576 | 704842511E8E2AD60006D1B0 /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | }; 580 | 704842521E8E2AD60006D1B0 /* Build configuration list for PBXNativeTarget "SoolyCitySelectorUITests" */ = { 581 | isa = XCConfigurationList; 582 | buildConfigurations = ( 583 | 704842531E8E2AD60006D1B0 /* Debug */, 584 | 704842541E8E2AD60006D1B0 /* Release */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | }; 588 | /* End XCConfigurationList section */ 589 | }; 590 | rootObject = 7048421C1E8E2AD10006D1B0 /* Project object */; 591 | } 592 | -------------------------------------------------------------------------------- /SoolyCitySelector.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SoolyCitySelector.xcodeproj/xcuserdata/SoolyChristina.xcuserdatad/xcschemes/SoolyCitySelector.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 | -------------------------------------------------------------------------------- /SoolyCitySelector.xcodeproj/xcuserdata/SoolyChristina.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SoolyCitySelector.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 704842231E8E2AD10006D1B0 16 | 17 | primary 18 | 19 | 20 | 704842371E8E2AD30006D1B0 21 | 22 | primary 23 | 24 | 25 | 704842421E8E2AD40006D1B0 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /SoolyCitySelector/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SoolyCitySelector 4 | // 5 | // Created by SoolyChristina on 2017/3/31. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SoolyCitySelector/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /SoolyCitySelector/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 | -------------------------------------------------------------------------------- /SoolyCitySelector/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /SoolyCitySelector/CitySelectorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CitySelectorViewController.swift 3 | // SoolyWeather 4 | // 5 | // Created by SoolyChristina on 2017/3/9. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private let nomalCell = "nomalCell" 12 | private let hotCityCell = "hotCityCell" 13 | private let recentCell = "rencentCityCell" 14 | private let currentCell = "currentCityCell" 15 | 16 | class CitySelectorViewController: UIViewController { 17 | 18 | /// 表格 19 | lazy var tableView: UITableView = UITableView(frame: self.view.frame, style: .plain) 20 | /// 搜索控制器 21 | lazy var searchVC: UISearchController = { 22 | let searchVc = UISearchController(searchResultsController: self.searchResultVC) 23 | searchVc.delegate = self 24 | searchVc.searchResultsUpdater = self 25 | searchVc.hidesNavigationBarDuringPresentation = false 26 | /** 27 | * a、如果不添加下面这行代码,在设置hidesNavigationBarDuringPresentation这个属性为YES的时候,搜索框进入编辑模式会导致searchbar不可见,偏移-64; 在设置为NO的时候,进入编辑模式输入内容会导致高度为64的白条,猜测是导航栏没有渲染出来 28 | b、如果添加了下面这行代码,在设置hidesNavigationBarDuringPresentation这个属性为YES的时候,输入框进入编辑模式正常显示和使用; 在设置为NO的时候,搜索框进入编辑模式导致向下偏移64,具体原因暂时未找到 29 | */ 30 | searchVc.definesPresentationContext = true 31 | // searchVc.dimsBackgroundDuringPresentation = false 32 | searchVc.searchBar.frame = CGRect(x: 0, y: 0, width: ScreenWidth - 114, height: 44) 33 | searchVc.searchBar.placeholder = "输入城市名或拼音查询" 34 | searchVc.searchBar.backgroundImage = UIColor.creatImageWithColor(color: UIColor.clear) 35 | searchVc.searchBar.delegate = self 36 | return searchVc 37 | }() 38 | /// 搜索结果控制器 39 | lazy var searchResultVC: ResultTableViewController = ResultTableViewController() 40 | /// 懒加载 城市数据 41 | lazy var cityDic: [String: [String]] = { () -> [String : [String]] in 42 | let path = Bundle.main.path(forResource: "cities.plist", ofType: nil) 43 | let dic = NSDictionary(contentsOfFile: path ?? "") as? [String: [String]] 44 | return dic ?? [:] 45 | }() 46 | /// 懒加载 热门城市 47 | lazy var hotCities: [String] = { 48 | let path = Bundle.main.path(forResource: "hotCities.plist", ofType: nil) 49 | let array = NSArray(contentsOfFile: path ?? "") as? [String] 50 | return array ?? [] 51 | }() 52 | /// 懒加载 标题数组 53 | lazy var titleArray: [String] = { () -> [String] in 54 | var array = [String]() 55 | for str in self.cityDic.keys { 56 | array.append(str) 57 | } 58 | // 标题排序 59 | array.sort() 60 | array.insert("热门", at: 0) 61 | array.insert("最近", at: 0) 62 | array.insert("当前", at: 0) 63 | return array 64 | }() 65 | 66 | override func viewDidLoad() { 67 | super.viewDidLoad() 68 | setupUI() 69 | } 70 | 71 | private func setupUI() { 72 | 73 | // 在导航条添加searchBar 74 | let titleView = UIView(frame: searchVC.searchBar.frame) 75 | titleView.addSubview(searchVC.searchBar) 76 | self.navigationItem.titleView = titleView 77 | 78 | // 设置导航条 79 | self.title = "选择城市" 80 | self.navigationController?.navigationBar.barTintColor = mainColor 81 | self.navigationController?.navigationBar.tintColor = mainColor 82 | self.navigationController?.navigationBar.titleTextAttributes = {[ 83 | NSForegroundColorAttributeName: UIColor.white, 84 | NSFontAttributeName: UIFont.systemFont(ofSize: 18) 85 | ]}() 86 | 87 | // 设置tableView 88 | tableView.delegate = self 89 | tableView.dataSource = self 90 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: nomalCell) 91 | tableView.register(RecentCitiesTableViewCell.self, forCellReuseIdentifier: recentCell) 92 | tableView.register(CurrentCityTableViewCell.self, forCellReuseIdentifier: currentCell) 93 | tableView.register(HotCityTableViewCell.self, forCellReuseIdentifier: hotCityCell) 94 | 95 | // 右边索引 96 | tableView.sectionIndexColor = mainColor 97 | // tableView.sectionIndexTrackingBackgroundColor = UIColor.white 98 | tableView.sectionIndexBackgroundColor = UIColor.clear 99 | self.view.addSubview(tableView) 100 | } 101 | 102 | deinit { 103 | print("我走了") 104 | } 105 | 106 | } 107 | 108 | // MARK: UISearchResultsUpdating 109 | extension CitySelectorViewController: UISearchResultsUpdating, UISearchControllerDelegate { 110 | func updateSearchResults(for searchController: UISearchController) { 111 | getSearchResultArray(searchBarText: searchController.searchBar.text ?? "") 112 | } 113 | 114 | func willPresentSearchController(_ searchController: UISearchController) { 115 | searchController.searchBar.showsCancelButton = false 116 | } 117 | 118 | func presentSearchController(_ searchController: UISearchController) { 119 | searchController.searchBar.showsCancelButton = false 120 | } 121 | 122 | // 隐藏取消按钮 123 | func didPresentSearchController(_ searchController: UISearchController) { 124 | searchController.searchBar.showsCancelButton = false 125 | } 126 | 127 | } 128 | 129 | // MARK: searchBar 代理方法 130 | extension CitySelectorViewController: UISearchBarDelegate { 131 | func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { 132 | return true 133 | } 134 | } 135 | 136 | // MARK: tableView 代理方法、数据源方法 137 | extension CitySelectorViewController: UITableViewDataSource, UITableViewDelegate { 138 | func numberOfSections(in tableView: UITableView) -> Int { 139 | return titleArray.count 140 | } 141 | 142 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 143 | if section > 2 { 144 | let key = titleArray[section] 145 | return cityDic[key]!.count - 3 146 | } 147 | return 1 148 | } 149 | 150 | // MARK: 创建cell 151 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 152 | if indexPath.section == 0 { 153 | 154 | let cell = tableView.dequeueReusableCell(withIdentifier: currentCell, for: indexPath) 155 | cell.backgroundColor = cellColor 156 | return cell 157 | 158 | }else if indexPath.section == 1 { 159 | 160 | let cell = tableView.dequeueReusableCell(withIdentifier: recentCell, for: indexPath) as! RecentCitiesTableViewCell 161 | return cell 162 | }else if indexPath.section == 2 { 163 | 164 | let cell = tableView.dequeueReusableCell(withIdentifier: hotCityCell, for: indexPath) as! HotCityTableViewCell 165 | return cell 166 | }else { 167 | let cell = tableView.dequeueReusableCell(withIdentifier: nomalCell, for: indexPath) 168 | // cell.backgroundColor = cellColor 169 | let key = titleArray[indexPath.section] 170 | cell.textLabel?.text = cityDic[key]![indexPath.row] 171 | return cell 172 | } 173 | } 174 | // MARK: 点击cell 175 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 176 | 177 | tableView.deselectRow(at: indexPath, animated: false) 178 | let cell = tableView.cellForRow(at: indexPath) 179 | print("点击了 \(cell?.textLabel?.text ?? "")") 180 | } 181 | 182 | // MARK: 右边索引 183 | func sectionIndexTitles(for tableView: UITableView) -> [String]? { 184 | return titleArray 185 | } 186 | 187 | // MARK: section头视图 188 | func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 189 | let view = UIView(frame: CGRect(x: 0, y: 0, width: ScreenWidth, height: sectionMargin)) 190 | let title = UILabel(frame: CGRect(x: 15, y: 5, width: ScreenWidth - 15, height: 28)) 191 | var titleArr = titleArray 192 | titleArr[0] = "当前城市" 193 | titleArr[1] = "最近选择城市" 194 | titleArr[2] = "热门城市" 195 | title.text = titleArr[section] 196 | title.textColor = mainColor 197 | title.font = UIFont.boldSystemFont(ofSize: 18) 198 | view.addSubview(title) 199 | view.backgroundColor = UIColor.white 200 | if section > 2 { 201 | view.backgroundColor = mainColor 202 | title.textColor = UIColor.white 203 | } 204 | 205 | return view 206 | } 207 | 208 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 209 | return sectionMargin 210 | } 211 | 212 | // MARK: row高度 213 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 214 | if indexPath.section == 0 { 215 | return btnHeight + 2 * btnMargin 216 | }else if indexPath.section == 1 { 217 | return btnHeight + 2 * btnMargin 218 | }else if indexPath.section == 2 { 219 | let row = (hotCities.count - 1) / 3 220 | return (btnHeight + 2 * btnMargin) + (btnMargin + btnHeight) * CGFloat(row) 221 | }else{ 222 | return 42 223 | } 224 | } 225 | } 226 | 227 | // MARK: 搜索逻辑 228 | extension CitySelectorViewController { 229 | fileprivate func getSearchResultArray(searchBarText: String) { 230 | var resultArray:[String] = [] 231 | if searchBarText == "" { 232 | searchResultVC.resultArray = resultArray 233 | searchResultVC.tableView.reloadData() 234 | return 235 | } 236 | // 传递闭包 当点击’搜索结果‘的cell调用 237 | searchResultVC.callBack = { [weak self] in 238 | // 搜索完成 关闭resultVC 239 | self?.searchVC.isActive = false 240 | } 241 | // 中文搜索 242 | if searchBarText.isIncludeChineseIn() { 243 | // 转拼音 244 | let pinyin = searchBarText.chineseToPinyin() 245 | // 获取大写首字母 246 | let first = String(pinyin[pinyin.startIndex]).uppercased() 247 | guard let dic = cityDic[first] else { 248 | return 249 | } 250 | for str in dic { 251 | if str.hasPrefix(searchBarText) { 252 | resultArray.append(str) 253 | } 254 | } 255 | searchResultVC.resultArray = resultArray 256 | searchResultVC.tableView.reloadData() 257 | }else { 258 | // 拼音搜索 259 | // 若字符个数为1 260 | if searchBarText.characters.count == 1 { 261 | guard let dic = cityDic[searchBarText.uppercased()] else { 262 | return 263 | } 264 | resultArray = dic 265 | searchResultVC.resultArray = resultArray 266 | searchResultVC.tableView.reloadData() 267 | }else { 268 | guard let dic = cityDic[searchBarText.first().uppercased()] else { 269 | return 270 | } 271 | for str in dic { 272 | // 去空格 273 | let py = String(str.chineseToPinyin().characters.filter({ $0 != " "})) 274 | let range = py.range(of: searchBarText) 275 | if range != nil { 276 | resultArray.append(str) 277 | } 278 | } 279 | // 加入首字母判断 如 cq => 重庆 bj => 北京 280 | if resultArray.count == 0 { 281 | for str in dic { 282 | // 北京 => bei jing 283 | let pinyin = str.chineseToPinyin() 284 | // 获取空格的index 285 | let a = pinyin.characters.index(of: " ") 286 | let index = pinyin.index(a!, offsetBy: 2) 287 | // offsetBy: 2 截取 bei j 288 | // offsetBy: 1 截取 bei+空格 289 | // substring(to: index) 不包含 index最后那个下标 290 | let py = pinyin.substring(to: index) 291 | /// 获取第二个首字母 292 | /// 293 | /// py = "bei j" 294 | /// last = "j" 295 | /// 296 | let last = py.substring(from: py.index(py.endIndex, offsetBy: -1)) 297 | /// 两个首字母 298 | let pyIndex = String(pinyin[pinyin.startIndex]) + last 299 | 300 | if searchBarText.lowercased() == pyIndex { 301 | resultArray.append(str) 302 | } 303 | } 304 | } 305 | searchResultVC.resultArray = resultArray 306 | searchResultVC.tableView.reloadData() 307 | } 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /SoolyCitySelector/CurrentCityTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurrentCityTableViewCell.swift 3 | // SoolyWeather 4 | // 5 | // Created by SoolyChristina on 2017/3/10. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CurrentCityTableViewCell: UITableViewCell { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | // Initialization code 16 | } 17 | 18 | override func setSelected(_ selected: Bool, animated: Bool) { 19 | super.setSelected(selected, animated: animated) 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /SoolyCitySelector/Header.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Header.swift 3 | // SoolyCitySelector 4 | // 5 | // Created by SoolyChristina on 2017/3/31. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let ScreenWidth: CGFloat = UIScreen.main.bounds.size.width 12 | let ScreenHeight: CGFloat = UIScreen.main.bounds.size.height 13 | let ScreenBounds: CGRect = UIScreen.main.bounds 14 | 15 | /// 主配色 16 | let mainColor = UIColor.color(hex: "#707070") 17 | /// 浅灰 cell背景色 18 | let cellColor = UIColor.color(hex: "#EAEAEA") 19 | /// btn 高亮背景色 20 | let btnHighlightColor = UIColor.color(hex: "#efeff4") 21 | /// btn 高亮图片 22 | let btnHighlightImage = UIColor.creatImageWithColor(color: btnHighlightColor) 23 | 24 | /// section间距 25 | let sectionMargin: CGFloat = 38 26 | 27 | /// 热门城市btn 28 | let btnMargin: CGFloat = 15 29 | let btnWidth: CGFloat = (ScreenWidth - 90) / 3 30 | let btnHeight: CGFloat = 36 31 | -------------------------------------------------------------------------------- /SoolyCitySelector/HotCityTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HotCityTableViewCell.swift 3 | // SoolyWeather 4 | // 5 | // Created by SoolyChristina on 2017/3/9. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HotCityTableViewCell: UITableViewCell { 12 | 13 | /// 懒加载 热门城市 14 | lazy var hotCities: [String] = { 15 | let path = Bundle.main.path(forResource: "hotCities.plist", ofType: nil) 16 | let array = NSArray(contentsOfFile: path!) as? [String] 17 | return array ?? [] 18 | }() 19 | 20 | /// 使用tableView.dequeueReusableCell会自动调用这个方法 21 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 22 | super.init(style: style, reuseIdentifier: reuseIdentifier) 23 | self.setupUI() 24 | } 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | fatalError("init(coder:) has not been implemented") 28 | } 29 | 30 | private func setupUI() { 31 | 32 | self.backgroundColor = cellColor 33 | // 动态创建城市btn 34 | for i in 0.. 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SoolyCitySelector/RecentCitiesableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecentCityTableViewCell.swift 3 | // SoolyWeather 4 | // 5 | // Created by SoolyChristina on 2017/3/10. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class RecentCitiesTableViewCell: UITableViewCell { 12 | 13 | // 使用tableView.dequeueReusableCell会自动调用这个方法 14 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 15 | super.init(style: style, reuseIdentifier: reuseIdentifier) 16 | self.setupUI() 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | private func setupUI() { 24 | 25 | self.backgroundColor = cellColor 26 | 27 | let btn = UIButton(frame: CGRect(x: btnMargin, y: 15, width: btnWidth, height: btnHeight)) 28 | btn.setTitle("北京", for: .normal) 29 | btn.setTitleColor(mainColor, for: .normal) 30 | btn.titleLabel?.font = UIFont.systemFont(ofSize: 16) 31 | btn.backgroundColor = UIColor.white 32 | // btn.layer.borderColor = mainColor.cgColor 33 | // btn.layer.borderWidth = 0.5 34 | btn.layer.cornerRadius = 1 35 | btn.setBackgroundImage(btnHighlightImage, for: .highlighted) 36 | btn .addTarget(self, action: #selector(btnClick(btn:)), for: .touchUpInside) 37 | self.addSubview(btn) 38 | 39 | } 40 | 41 | @objc private func btnClick(btn: UIButton) { 42 | print(btn.titleLabel?.text!) 43 | } 44 | 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /SoolyCitySelector/ResultTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResultTableViewController.swift 3 | // SoolyWeather 4 | // 5 | // Created by SoolyChristina on 2017/3/24. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private let resultCell = "resultCell" 12 | 13 | class ResultTableViewController: UITableViewController { 14 | 15 | var resultArray:[String] = [] 16 | var isFrameChange = false 17 | /// 点击cell回调闭包 18 | var callBack: () -> () = {} 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | // 控制器根据所在界面的status bar,navigationbar,与tabbar的高度,不自动调整scrollview的 inset 23 | self.automaticallyAdjustsScrollViewInsets = false 24 | 25 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: resultCell) 26 | } 27 | 28 | override func numberOfSections(in tableView: UITableView) -> Int { 29 | 30 | return 1 31 | } 32 | 33 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 34 | 35 | return resultArray.count 36 | } 37 | 38 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 39 | let cell = tableView.dequeueReusableCell(withIdentifier: resultCell, for: indexPath) 40 | cell.textLabel?.text = resultArray[indexPath.row] 41 | return cell 42 | } 43 | 44 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 45 | let cell = tableView.cellForRow(at: indexPath) 46 | print(cell?.textLabel?.text ?? "") 47 | 48 | // 点击cell调用闭包 49 | callBack() 50 | } 51 | 52 | override func viewWillLayoutSubviews() { 53 | super.viewWillLayoutSubviews() 54 | 55 | // 设置view的frame 56 | if isFrameChange == false { 57 | view.frame = CGRect(x: 0, y: 64, width: ScreenWidth, height: ScreenHeight - 64) 58 | isFrameChange = true 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SoolyCitySelector/String+SoolyCitySelector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Pinyin.swift 3 | // SoolyWeather 4 | // 5 | // Created by SoolyChristina on 2017/3/14. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension String { 13 | // MARK: 汉字 -> 拼音 14 | func chineseToPinyin() -> String { 15 | 16 | let stringRef = NSMutableString(string: self) as CFMutableString 17 | // 转换为带音标的拼音 18 | CFStringTransform(stringRef,nil, kCFStringTransformToLatin, false) 19 | // 去掉音标 20 | CFStringTransform(stringRef, nil, kCFStringTransformStripCombiningMarks, false) 21 | let pinyin = stringRef as String 22 | 23 | return pinyin 24 | } 25 | 26 | // MARK: 判断是否含有中文 27 | func isIncludeChineseIn() -> Bool { 28 | 29 | for (_, value) in self.characters.enumerated() { 30 | 31 | if ("\u{4E00}" <= value && value <= "\u{9FA5}") { 32 | return true 33 | } 34 | } 35 | 36 | return false 37 | } 38 | 39 | // MARK: 获取第一个字符 40 | func first() -> String { 41 | let index = self.index(self.startIndex, offsetBy: 1) 42 | return self.substring(to: index) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /SoolyCitySelector/UIColor+Hex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Hex.swift 3 | // SoolyWeather 4 | // 5 | // Created by SoolyChristina on 2017/3/7. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | extension UIColor { 13 | // MARK:- 把#ffffff颜色转为UIColor 14 | class func color(hex:String) ->UIColor { 15 | 16 | var cString = hex.trimmingCharacters(in:CharacterSet.whitespacesAndNewlines).uppercased() 17 | 18 | if (cString.hasPrefix("#")) { 19 | let index = cString.index(cString.startIndex, offsetBy:1) 20 | cString = cString.substring(from: index) 21 | } 22 | 23 | if (cString.characters.count != 6) { 24 | return UIColor.red 25 | } 26 | 27 | let rIndex = cString.index(cString.startIndex, offsetBy: 2) 28 | let rString = cString.substring(to: rIndex) 29 | let otherString = cString.substring(from: rIndex) 30 | let gIndex = otherString.index(otherString.startIndex, offsetBy: 2) 31 | let gString = otherString.substring(to: gIndex) 32 | let bIndex = cString.index(cString.endIndex, offsetBy: -2) 33 | let bString = cString.substring(from: bIndex) 34 | 35 | var r:CUnsignedInt = 0, g:CUnsignedInt = 0, b:CUnsignedInt = 0; 36 | Scanner(string: rString).scanHexInt32(&r) 37 | Scanner(string: gString).scanHexInt32(&g) 38 | Scanner(string: bString).scanHexInt32(&b) 39 | 40 | return UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(1)) 41 | } 42 | 43 | // MARK:- 把#ffffff颜色转为UIImage 44 | class func creatImageWithColor(color: UIColor) -> UIImage { 45 | let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0) 46 | UIGraphicsBeginImageContext(rect.size) 47 | let context = UIGraphicsGetCurrentContext() 48 | context!.setFillColor(color.cgColor) 49 | context!.fill(rect) 50 | let image = UIGraphicsGetImageFromCurrentImageContext() 51 | UIGraphicsEndImageContext() 52 | return image! 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /SoolyCitySelector/cities.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | A 6 | 7 | 阿坝 8 | 阿克苏 9 | 阿拉善盟 10 | 阿勒泰 11 | 安吉 12 | 安康 13 | 安宁 14 | 安平 15 | 安庆 16 | 安丘 17 | 鞍山 18 | 安顺 19 | 安溪 20 | 安阳 21 | 安岳 22 | 23 | B 24 | 25 | 白城 26 | 百色 27 | 白山 28 | 白银 29 | 保定 30 | 宝丰 31 | 宝鸡 32 | 保山 33 | 包头 34 | 宝应 35 | 巴彦淖尔 36 | 巴中 37 | 巴州 38 | 北海 39 | 北京 40 | 北流 41 | 北碚 42 | 蚌埠 43 | 毕节 44 | 滨海 45 | 滨州 46 | 泌阳 47 | 博尔塔拉 48 | 博兴 49 | 潘阳 50 | 亳州 51 | 52 | C 53 | 54 | 苍南 55 | 沧州 56 | 曹妃甸 57 | 曹县 58 | 岑溪 59 | 长春 60 | 常德 61 | 长葛 62 | 昌吉 63 | 长乐 64 | 昌乐 65 | 昌黎 66 | 长沙 67 | 常熟 68 | 长兴 69 | 昌邑 70 | 长垣 71 | 长治 72 | 常州 73 | 巢湖 74 | 朝阳 75 | 潮州 76 | 成安 77 | 承德 78 | 承德县 79 | 成都 80 | 郴州 81 | 赤壁 82 | 赤峰 83 | 茌平 84 | 池州 85 | 重庆 86 | 崇州 87 | 崇左 88 | 淳安 89 | 楚雄 90 | 滁州 91 | 慈溪 92 | 磁县 93 | 从化 94 | 95 | D 96 | 97 | 大丰 98 | 达拉特旗 99 | 大理 100 | 大连 101 | 大荔 102 | 郸城 103 | 丹东 104 | 当阳 105 | 丹阳 106 | 儋州 107 | 大庆 108 | 大石桥 109 | 大同 110 | 大通 111 | 大冶 112 | 达州 113 | 德宏 114 | 登封 115 | 灯塔 116 | 邓州 117 | 德清 118 | 德阳 119 | 德州 120 | 电白 121 | 调兵山 122 | 定西 123 | 定州 124 | 迪庆 125 | 东方 126 | 东港 127 | 东莞 128 | 东海 129 | 东平 130 | 东台 131 | 东阳 132 | 东营 133 | 敦煌 134 | 135 | E 136 | 137 | 鄂尔多斯 138 | 额尔古纳 139 | 峨眉山 140 | 恩施 141 | 鄂州 142 | 143 | F 144 | 145 | 繁昌 146 | 防城港 147 | 范县 148 | 丰城 149 | 肥城 150 | 肥乡 151 | 凤城 152 | 奉化 153 | 凤凰 154 | 封丘 155 | 丰县 156 | 汾阳 157 | 佛山 158 | 扶风 159 | 阜康 160 | 涪陵 161 | 阜宁 162 | 福清 163 | 抚顺 164 | 抚松 165 | 阜新 166 | 阜阳 167 | 富阳 168 | 福州 169 | 抚州 170 | 171 | G 172 | 173 | 盖州 174 | 甘南 175 | 赣榆 176 | 赣州 177 | 甘孜 178 | 高安 179 | 高碑店 180 | 藁城 181 | 高陵 182 | 高密 183 | 高平 184 | 高阳 185 | 高邮 186 | 高州 187 | 格尔木 188 | 个旧 189 | 共青城 190 | 巩义 191 | 公主岭 192 | 广安 193 | 广德 194 | 广汉 195 | 广饶 196 | 广元 197 | 广州 198 | 灌南 199 | 灌云 200 | 贵港 201 | 桂林 202 | 桂平 203 | 贵阳 204 | 固阳 205 | 206 | H 207 | 208 | 哈尔滨 209 | 海安 210 | 海城 211 | 海东 212 | 海口 213 | 海林 214 | 海门 215 | 海宁 216 | 海西 217 | 海盐 218 | 海阳 219 | 哈密 220 | 韩城 221 | 邯郸 222 | 杭州 223 | 含山 224 | 汉阴 225 | 汉中 226 | 霸州 227 | 鹤壁 228 | 河池 229 | 合川 230 | 合肥 231 | 鹤岗 232 | 黑河 233 | 河津 234 | 河口 235 | 横店 236 | 衡水 237 | 衡阳 238 | 鹤山 239 | 和县 240 | 河源 241 | 菏泽 242 | 贺州 243 | 红河 244 | 洪江 245 | 洪洞 246 | 侯马 247 | 桦甸 248 | 花都 249 | 淮安 250 | 淮北 251 | 怀化 252 | 淮南 253 | 淮阳 254 | 黄岛 255 | 黄冈 256 | 黄骅 257 | 黄山 258 | 黄石 259 | 华容 260 | 华亭 261 | 滑县 262 | 化州 263 | 呼和浩特 264 | 惠安 265 | 惠东 266 | 辉县 267 | 惠州 268 | 葫芦岛 269 | 呼伦贝尔 270 | 霍州 271 | 户县 272 | 湖州 273 | 274 | J 275 | 276 | 佳木斯 277 | 吉安 278 | 建德 279 | 江津 280 | 江门 281 | 江山 282 | 江阴 283 | 建湖 284 | 监利 285 | 集安 286 | 简阳 287 | 建阳 288 | 胶州 289 | 焦作 290 | 嘉善 291 | 郏县 292 | 嘉兴 293 | 嘉峪关 294 | 介休 295 | 揭阳 296 | 吉林 297 | 即墨 298 | 济南 299 | 金昌 300 | 晋城 301 | 景德镇 302 | 靖江 303 | 荆门 304 | 荆州 305 | 金华 306 | 济宁 307 | 晋江 308 | 金坛 309 | 金堂 310 | 晋中 311 | 锦州 312 | 晋州 313 | 吉首 314 | 九江 315 | 酒泉 316 | 鸡西 317 | 济源 318 | 句容 319 | 巨野 320 | 321 | K 322 | 323 | 开封 324 | 凯里 325 | 开平 326 | 开县 327 | 喀什 328 | 克拉玛依 329 | 垦利 330 | 库尔勒 331 | 奎屯 332 | 昆明 333 | 昆山 334 | 335 | L 336 | 337 | 来宾 338 | 莱芜 339 | 莱西 340 | 莱阳 341 | 莱州 342 | 廊坊 343 | 阆中 344 | 蓝田 345 | 兰溪 346 | 兰州 347 | 老河口 348 | 乐陵 349 | 乐亭 350 | 拉萨 351 | 乐昌 352 | 耒阳 353 | 冷水江 354 | 乐平 355 | 乐山 356 | 凉山 357 | 廉江 358 | 连江 359 | 连云港 360 | 连州 361 | 聊城 362 | 辽阳 363 | 辽源 364 | 利州 365 | 醴陵 366 | 临安 367 | 临沧 368 | 临汾 369 | 凌海 370 | 灵山 371 | 灵石 372 | 陵水 373 | 临海 374 | 临江 375 | 临清 376 | 临朐 377 | 临潼 378 | 临夏 379 | 临沂 380 | 临漳 381 | 林州 382 | 丽水 383 | 六安 384 | 六盘水 385 | 浏阳 386 | 柳州 387 | 溧阳 388 | 隆昌 389 | 龙海 390 | 龙口 391 | 陇南 392 | 龙岩 393 | 娄底 394 | 栾城 395 | 滦南 396 | 滦县 397 | 陆丰 398 | 漯河 399 | 洛阳 400 | 鹿泉 401 | 庐山 402 | 鲁山 403 | 鹿邑 404 | 泸州 405 | 吕梁 406 | 407 | M 408 | 409 | 马鞍山 410 | 麻城 411 | 满洲里 412 | 茂名 413 | 梅河口 414 | 眉山 415 | 眉县 416 | 梅州 417 | 孟津 418 | 孟州 419 | 渑池 420 | 绵阳 421 | 汨罗 422 | 明光 423 | 民权 424 | 密山 425 | 牡丹江 426 | 牟平 427 | 428 | N 429 | 430 | 南安 431 | 南昌 432 | 南充 433 | 南京 434 | 南陵 435 | 南宁 436 | 南平 437 | 南沙 438 | 南通 439 | 南阳 440 | 讷河 441 | 内江 442 | 宁波 443 | 宁德 444 | 宁国 445 | 宁海 446 | 宁晋 447 | 宁津 448 | 宁乡 449 | 宁阳 450 | 怒江 451 | 452 | P 453 | 454 | 盘锦 455 | 磐石 456 | 盘县 457 | 攀枝花 458 | 沛县 459 | 蓬莱 460 | 彭州 461 | 平顶山 462 | 平度 463 | 平湖 464 | 平江 465 | 平凉 466 | 平潭 467 | 萍乡 468 | 平原 469 | 邳州 470 | 蒲城 471 | 普洱 472 | 浦江 473 | 普宁 474 | 莆田 475 | 濮阳 476 | 477 | Q 478 | 479 | 迁安 480 | 黔东南 481 | 潜江 482 | 黔南 483 | 黔西南 484 | 启动 485 | 齐河 486 | 青岛 487 | 清丰 488 | 庆阳 489 | 清远 490 | 青州 491 | 秦皇岛 492 | 沁阳 493 | 钦州 494 | 琼海 495 | 邛崃 496 | 齐齐哈尔 497 | 七台河 498 | 全椒 499 | 泉州 500 | 曲阜 501 | 曲靖 502 | 衢州 503 | 504 | R 505 | 506 | 仁怀 507 | 仁寿 508 | 日喀则 509 | 日照 510 | 荣成 511 | 如皋 512 | 瑞安 513 | 瑞昌 514 | 瑞金 515 | 乳山 516 | 汝州 517 | 518 | S 519 | 520 | 三河 521 | 三门 522 | 三门峡 523 | 三明 524 | 三亚 525 | 沙河 526 | 上蔡 527 | 上高 528 | 上海 529 | 商洛 530 | 商丘 531 | 上饶 532 | 上虞 533 | 尚志 534 | 汕头 535 | 汕尾 536 | 单县 537 | 邵东 538 | 韶关 539 | 韶山 540 | 邵阳 541 | 沙湾 542 | 嵊泗 543 | 嵊州 544 | 神木 545 | 神农架 546 | 沈丘 547 | 沈阳 548 | 深圳 549 | 涉县 550 | 射阳 551 | 石河子 552 | 石家庄 553 | 石狮 554 | 十堰 555 | 石嘴山 556 | 寿光 557 | 双鸭山 558 | 顺德 559 | 朔州 560 | 沭阳 561 | 泗洪 562 | 四会 563 | 四平 564 | 泗阳 565 | 松原 566 | 松滋 567 | 绥化 568 | 遂宁 569 | 睢宁 570 | 睢县 571 | 绥中 572 | 随州 573 | 宿迁 574 | 苏州 575 | 宿州 576 | 577 | T 578 | 579 | 塔城 580 | 泰安 581 | 台北 582 | 太仓 583 | 太谷 584 | 太和 585 | 台前 586 | 台山 587 | 泰兴 588 | 太原 589 | 泰州 590 | 台州 591 | 唐山 592 | 汤阴 593 | 洮南 594 | 腾冲 595 | 滕州 596 | 天长 597 | 天津 598 | 天门 599 | 天水 600 | 天台 601 | 铁岭 602 | 桐城 603 | 铜川 604 | 通化 605 | 通辽 606 | 铜陵 607 | 桐庐 608 | 铜仁 609 | 桐乡 610 | 吐鲁番 611 | 土默特右旗 612 | 613 | W 614 | 615 | 万宁 616 | 万州 617 | 潍坊 618 | 威海 619 | 渭南 620 | 文登 621 | 温岭 622 | 文山 623 | 温县 624 | 温州 625 | 乌拉特前旗 626 | 武安 627 | 吴川 628 | 舞钢 629 | 乌海 630 | 武汉 631 | 芜湖 632 | 芜湖县 633 | 吴江 634 | 乌兰察布 635 | 乌鲁木齐 636 | 乌苏 637 | 武威 638 | 无为 639 | 无锡 640 | 武穴 641 | 武夷山 642 | 婺源 643 | 乌镇 644 | 武陟 645 | 吴忠 646 | 梧州 647 | 648 | X 649 | 650 | 夏津 651 | 厦门 652 | 西安 653 | 项城 654 | 香格里拉 655 | 象山 656 | 响水 657 | 湘潭 658 | 湘西 659 | 湘乡 660 | 襄阳 661 | 湘阴 662 | 襄垣 663 | 仙居 664 | 咸宁 665 | 仙桃 666 | 咸阳 667 | 孝感 668 | 孝义 669 | 霞浦 670 | 夏邑 671 | 锡林郭勒 672 | 新安 673 | 兴安盟 674 | 兴城 675 | 兴化 676 | 兴宁 677 | 兴平 678 | 邢台 679 | 荥阳 680 | 新化 681 | 西宁 682 | 辛集 683 | 新密 684 | 新泰 685 | 新乡 686 | 新乡县 687 | 信阳 688 | 新沂 689 | 信宜 690 | 新余 691 | 新郑 692 | 忻州 693 | 西平 694 | 西双版纳 695 | 西塘 696 | 修武 697 | 西乡 698 | 宣城 699 | 宣威 700 | 许昌 701 | 浚县 702 | 徐闻 703 | 盱眙 704 | 徐州 705 | 706 | Y 707 | 708 | 雅安 709 | 延安 710 | 延边 711 | 盐城 712 | 阳城 713 | 阳江 714 | 阳泉 715 | 阳朔 716 | 扬中 717 | 扬州 718 | 阎良 719 | 偃师 720 | 烟台 721 | 兖州 722 | 叶县 723 | 宜宾 724 | 宜昌 725 | 宜城 726 | 伊川 727 | 宜春 728 | 伊春 729 | 宜都 730 | 伊犁 731 | 银川 732 | 应城 733 | 英德 734 | 营口 735 | 鹰潭 736 | 伊宁 737 | 义乌 738 | 易县 739 | 宜兴 740 | 益阳 741 | 宜阳 742 | 仪征 743 | 永城 744 | 永川 745 | 永济 746 | 永康 747 | 永年 748 | 永州 749 | 沅江 750 | 原阳 751 | 禹城 752 | 虞城 753 | 乐清 754 | 岳阳 755 | 玉环 756 | 玉林 757 | 榆林 758 | 运城 759 | 郓城 760 | 云浮 761 | 云阳 762 | 玉山 763 | 玉溪 764 | 余姚 765 | 禹州 766 | 767 | Z 768 | 769 | 枣阳 770 | 枣庄 771 | 增城 772 | 张家港 773 | 张家界 774 | 张家口 775 | 漳浦 776 | 章丘 777 | 樟树 778 | 张掖 779 | 漳州 780 | 湛江 781 | 肇庆 782 | 昭通 783 | 赵县 784 | 招远 785 | 柘城 786 | 正定 787 | 郑州 788 | 镇江 789 | 枝江 790 | 中牟 791 | 中山 792 | 中卫 793 | 钟祥 794 | 周口 795 | 舟山 796 | 庄河 797 | 诸城 798 | 珠海 799 | 诸暨 800 | 驻马店 801 | 涿州 802 | 株洲 803 | 淄博 804 | 自贡 805 | 资阳 806 | 邹城 807 | 邹平 808 | 遵化 809 | 遵义 810 | 811 | 812 | 813 | -------------------------------------------------------------------------------- /SoolyCitySelector/hotCities.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 北京 6 | 上海 7 | 广州 8 | 深圳 9 | 天津 10 | 武汉 11 | 南京 12 | 杭州 13 | 成都 14 | 重庆 15 | 16 | 17 | -------------------------------------------------------------------------------- /SoolyCitySelectorTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SoolyCitySelectorTests/SoolyCitySelectorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SoolyCitySelectorTests.swift 3 | // SoolyCitySelectorTests 4 | // 5 | // Created by SoolyChristina on 2017/3/31. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SoolyCitySelector 11 | 12 | class SoolyCitySelectorTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /SoolyCitySelectorUITests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SoolyCitySelectorUITests/SoolyCitySelectorUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SoolyCitySelectorUITests.swift 3 | // SoolyCitySelectorUITests 4 | // 5 | // Created by SoolyChristina on 2017/3/31. 6 | // Copyright © 2017年 SoolyChristina. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SoolyCitySelectorUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // 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. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoolyChristy/SoolyCitySelector/36192a78eacd0c02422db6364a645d4cd6db0316/demo.png --------------------------------------------------------------------------------