├── ConvenientPickerView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── anran.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── macbook.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── anran.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── ConvenientPickerView.xcscheme │ │ └── xcschememanagement.plist │ └── macbook.xcuserdatad │ └── xcschemes │ ├── ConvenientPickerView.xcscheme │ └── xcschememanagement.plist ├── ConvenientPickerView ├── ARAButtonViewController.swift ├── ARATextFieldViewController.swift ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── PickerView │ ├── Area.plist │ ├── City.plist │ ├── ConvenientPickerView.swift │ ├── Picker.swift │ ├── PickerTextField.swift │ ├── PickerView.swift │ ├── Province.plist │ └── ToolBarView.swift ├── ViewController.swift ├── anran.gif ├── anran1.gif └── anran2.gif ├── LICENSE └── README.md /ConvenientPickerView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 35102DBF1E80D09C003337E9 /* PickerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35102DBE1E80D09C003337E9 /* PickerTextField.swift */; }; 11 | 35102DC11E80D849003337E9 /* anran.gif in Resources */ = {isa = PBXBuildFile; fileRef = 35102DC01E80D849003337E9 /* anran.gif */; }; 12 | 35102DC41E80E1BA003337E9 /* anran2.gif in Resources */ = {isa = PBXBuildFile; fileRef = 35102DC21E80E1BA003337E9 /* anran2.gif */; }; 13 | 35102DC51E80E1BA003337E9 /* anran1.gif in Resources */ = {isa = PBXBuildFile; fileRef = 35102DC31E80E1BA003337E9 /* anran1.gif */; }; 14 | 356F5AE61E7F689900BCB6D0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356F5AE51E7F689900BCB6D0 /* AppDelegate.swift */; }; 15 | 356F5AE81E7F689900BCB6D0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356F5AE71E7F689900BCB6D0 /* ViewController.swift */; }; 16 | 356F5AEB1E7F689900BCB6D0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 356F5AE91E7F689900BCB6D0 /* Main.storyboard */; }; 17 | 356F5AED1E7F689900BCB6D0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 356F5AEC1E7F689900BCB6D0 /* Assets.xcassets */; }; 18 | 356F5AF01E7F689900BCB6D0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 356F5AEE1E7F689900BCB6D0 /* LaunchScreen.storyboard */; }; 19 | 356F5AFB1E7F693500BCB6D0 /* Area.plist in Resources */ = {isa = PBXBuildFile; fileRef = 356F5AF81E7F693500BCB6D0 /* Area.plist */; }; 20 | 356F5AFC1E7F693500BCB6D0 /* City.plist in Resources */ = {isa = PBXBuildFile; fileRef = 356F5AF91E7F693500BCB6D0 /* City.plist */; }; 21 | 356F5AFD1E7F693500BCB6D0 /* Province.plist in Resources */ = {isa = PBXBuildFile; fileRef = 356F5AFA1E7F693500BCB6D0 /* Province.plist */; }; 22 | 356F5AFF1E7F69EB00BCB6D0 /* ToolBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356F5AFE1E7F69EB00BCB6D0 /* ToolBarView.swift */; }; 23 | 356F5B011E7F877A00BCB6D0 /* Picker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356F5B001E7F877A00BCB6D0 /* Picker.swift */; }; 24 | 356F5B031E7FDB7C00BCB6D0 /* ConvenientPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356F5B021E7FDB7C00BCB6D0 /* ConvenientPickerView.swift */; }; 25 | 356F5B051E7FDBEA00BCB6D0 /* PickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 356F5B041E7FDBEA00BCB6D0 /* PickerView.swift */; }; 26 | 359A6E0D1E80B4D600F12009 /* ARAButtonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359A6E0C1E80B4D600F12009 /* ARAButtonViewController.swift */; }; 27 | 359A6E0F1E80B4EF00F12009 /* ARATextFieldViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359A6E0E1E80B4EF00F12009 /* ARATextFieldViewController.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 35102DBE1E80D09C003337E9 /* PickerTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PickerTextField.swift; sourceTree = ""; }; 32 | 35102DC01E80D849003337E9 /* anran.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = anran.gif; sourceTree = ""; }; 33 | 35102DC21E80E1BA003337E9 /* anran2.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = anran2.gif; sourceTree = ""; }; 34 | 35102DC31E80E1BA003337E9 /* anran1.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = anran1.gif; sourceTree = ""; }; 35 | 356F5AE21E7F689900BCB6D0 /* ConvenientPickerView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ConvenientPickerView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 356F5AE51E7F689900BCB6D0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 356F5AE71E7F689900BCB6D0 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 38 | 356F5AEA1E7F689900BCB6D0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 39 | 356F5AEC1E7F689900BCB6D0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 40 | 356F5AEF1E7F689900BCB6D0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 41 | 356F5AF11E7F689900BCB6D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 356F5AF81E7F693500BCB6D0 /* Area.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Area.plist; sourceTree = ""; }; 43 | 356F5AF91E7F693500BCB6D0 /* City.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = City.plist; sourceTree = ""; }; 44 | 356F5AFA1E7F693500BCB6D0 /* Province.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Province.plist; sourceTree = ""; }; 45 | 356F5AFE1E7F69EB00BCB6D0 /* ToolBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolBarView.swift; sourceTree = ""; }; 46 | 356F5B001E7F877A00BCB6D0 /* Picker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Picker.swift; sourceTree = ""; }; 47 | 356F5B021E7FDB7C00BCB6D0 /* ConvenientPickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvenientPickerView.swift; sourceTree = ""; }; 48 | 356F5B041E7FDBEA00BCB6D0 /* PickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PickerView.swift; sourceTree = ""; }; 49 | 359A6E0C1E80B4D600F12009 /* ARAButtonViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ARAButtonViewController.swift; sourceTree = ""; }; 50 | 359A6E0E1E80B4EF00F12009 /* ARATextFieldViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ARATextFieldViewController.swift; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 356F5ADF1E7F689900BCB6D0 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 356F5AD91E7F689800BCB6D0 = { 65 | isa = PBXGroup; 66 | children = ( 67 | 356F5AE41E7F689900BCB6D0 /* ConvenientPickerView */, 68 | 356F5AE31E7F689900BCB6D0 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 356F5AE31E7F689900BCB6D0 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 356F5AE21E7F689900BCB6D0 /* ConvenientPickerView.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 356F5AE41E7F689900BCB6D0 /* ConvenientPickerView */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 356F5AF71E7F693500BCB6D0 /* PickerView */, 84 | 356F5AE51E7F689900BCB6D0 /* AppDelegate.swift */, 85 | 356F5AE71E7F689900BCB6D0 /* ViewController.swift */, 86 | 356F5AE91E7F689900BCB6D0 /* Main.storyboard */, 87 | 356F5AEC1E7F689900BCB6D0 /* Assets.xcassets */, 88 | 356F5AEE1E7F689900BCB6D0 /* LaunchScreen.storyboard */, 89 | 356F5AF11E7F689900BCB6D0 /* Info.plist */, 90 | 35102DC01E80D849003337E9 /* anran.gif */, 91 | 35102DC21E80E1BA003337E9 /* anran2.gif */, 92 | 35102DC31E80E1BA003337E9 /* anran1.gif */, 93 | 359A6E0C1E80B4D600F12009 /* ARAButtonViewController.swift */, 94 | 359A6E0E1E80B4EF00F12009 /* ARATextFieldViewController.swift */, 95 | ); 96 | path = ConvenientPickerView; 97 | sourceTree = ""; 98 | }; 99 | 356F5AF71E7F693500BCB6D0 /* PickerView */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 356F5AF81E7F693500BCB6D0 /* Area.plist */, 103 | 356F5AF91E7F693500BCB6D0 /* City.plist */, 104 | 356F5AFA1E7F693500BCB6D0 /* Province.plist */, 105 | 356F5AFE1E7F69EB00BCB6D0 /* ToolBarView.swift */, 106 | 356F5B001E7F877A00BCB6D0 /* Picker.swift */, 107 | 356F5B021E7FDB7C00BCB6D0 /* ConvenientPickerView.swift */, 108 | 356F5B041E7FDBEA00BCB6D0 /* PickerView.swift */, 109 | 35102DBE1E80D09C003337E9 /* PickerTextField.swift */, 110 | ); 111 | path = PickerView; 112 | sourceTree = ""; 113 | }; 114 | /* End PBXGroup section */ 115 | 116 | /* Begin PBXNativeTarget section */ 117 | 356F5AE11E7F689900BCB6D0 /* ConvenientPickerView */ = { 118 | isa = PBXNativeTarget; 119 | buildConfigurationList = 356F5AF41E7F689900BCB6D0 /* Build configuration list for PBXNativeTarget "ConvenientPickerView" */; 120 | buildPhases = ( 121 | 356F5ADE1E7F689900BCB6D0 /* Sources */, 122 | 356F5ADF1E7F689900BCB6D0 /* Frameworks */, 123 | 356F5AE01E7F689900BCB6D0 /* Resources */, 124 | ); 125 | buildRules = ( 126 | ); 127 | dependencies = ( 128 | ); 129 | name = ConvenientPickerView; 130 | productName = ConvenientPickerView; 131 | productReference = 356F5AE21E7F689900BCB6D0 /* ConvenientPickerView.app */; 132 | productType = "com.apple.product-type.application"; 133 | }; 134 | /* End PBXNativeTarget section */ 135 | 136 | /* Begin PBXProject section */ 137 | 356F5ADA1E7F689800BCB6D0 /* Project object */ = { 138 | isa = PBXProject; 139 | attributes = { 140 | LastSwiftUpdateCheck = 0820; 141 | LastUpgradeCheck = 0900; 142 | ORGANIZATIONNAME = "安然"; 143 | TargetAttributes = { 144 | 356F5AE11E7F689900BCB6D0 = { 145 | CreatedOnToolsVersion = 8.2.1; 146 | DevelopmentTeam = E8ZU3Y58CG; 147 | LastSwiftMigration = 0900; 148 | ProvisioningStyle = Automatic; 149 | }; 150 | }; 151 | }; 152 | buildConfigurationList = 356F5ADD1E7F689800BCB6D0 /* Build configuration list for PBXProject "ConvenientPickerView" */; 153 | compatibilityVersion = "Xcode 3.2"; 154 | developmentRegion = English; 155 | hasScannedForEncodings = 0; 156 | knownRegions = ( 157 | en, 158 | Base, 159 | ); 160 | mainGroup = 356F5AD91E7F689800BCB6D0; 161 | productRefGroup = 356F5AE31E7F689900BCB6D0 /* Products */; 162 | projectDirPath = ""; 163 | projectRoot = ""; 164 | targets = ( 165 | 356F5AE11E7F689900BCB6D0 /* ConvenientPickerView */, 166 | ); 167 | }; 168 | /* End PBXProject section */ 169 | 170 | /* Begin PBXResourcesBuildPhase section */ 171 | 356F5AE01E7F689900BCB6D0 /* Resources */ = { 172 | isa = PBXResourcesBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 35102DC51E80E1BA003337E9 /* anran1.gif in Resources */, 176 | 35102DC41E80E1BA003337E9 /* anran2.gif in Resources */, 177 | 356F5AF01E7F689900BCB6D0 /* LaunchScreen.storyboard in Resources */, 178 | 356F5AFC1E7F693500BCB6D0 /* City.plist in Resources */, 179 | 356F5AFD1E7F693500BCB6D0 /* Province.plist in Resources */, 180 | 356F5AFB1E7F693500BCB6D0 /* Area.plist in Resources */, 181 | 356F5AED1E7F689900BCB6D0 /* Assets.xcassets in Resources */, 182 | 356F5AEB1E7F689900BCB6D0 /* Main.storyboard in Resources */, 183 | 35102DC11E80D849003337E9 /* anran.gif in Resources */, 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | }; 187 | /* End PBXResourcesBuildPhase section */ 188 | 189 | /* Begin PBXSourcesBuildPhase section */ 190 | 356F5ADE1E7F689900BCB6D0 /* Sources */ = { 191 | isa = PBXSourcesBuildPhase; 192 | buildActionMask = 2147483647; 193 | files = ( 194 | 356F5AFF1E7F69EB00BCB6D0 /* ToolBarView.swift in Sources */, 195 | 356F5B031E7FDB7C00BCB6D0 /* ConvenientPickerView.swift in Sources */, 196 | 359A6E0D1E80B4D600F12009 /* ARAButtonViewController.swift in Sources */, 197 | 356F5B051E7FDBEA00BCB6D0 /* PickerView.swift in Sources */, 198 | 356F5B011E7F877A00BCB6D0 /* Picker.swift in Sources */, 199 | 356F5AE81E7F689900BCB6D0 /* ViewController.swift in Sources */, 200 | 359A6E0F1E80B4EF00F12009 /* ARATextFieldViewController.swift in Sources */, 201 | 356F5AE61E7F689900BCB6D0 /* AppDelegate.swift in Sources */, 202 | 35102DBF1E80D09C003337E9 /* PickerTextField.swift in Sources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXSourcesBuildPhase section */ 207 | 208 | /* Begin PBXVariantGroup section */ 209 | 356F5AE91E7F689900BCB6D0 /* Main.storyboard */ = { 210 | isa = PBXVariantGroup; 211 | children = ( 212 | 356F5AEA1E7F689900BCB6D0 /* Base */, 213 | ); 214 | name = Main.storyboard; 215 | sourceTree = ""; 216 | }; 217 | 356F5AEE1E7F689900BCB6D0 /* LaunchScreen.storyboard */ = { 218 | isa = PBXVariantGroup; 219 | children = ( 220 | 356F5AEF1E7F689900BCB6D0 /* Base */, 221 | ); 222 | name = LaunchScreen.storyboard; 223 | sourceTree = ""; 224 | }; 225 | /* End PBXVariantGroup section */ 226 | 227 | /* Begin XCBuildConfiguration section */ 228 | 356F5AF21E7F689900BCB6D0 /* Debug */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | ALWAYS_SEARCH_USER_PATHS = NO; 232 | CLANG_ANALYZER_NONNULL = YES; 233 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 234 | CLANG_CXX_LIBRARY = "libc++"; 235 | CLANG_ENABLE_MODULES = YES; 236 | CLANG_ENABLE_OBJC_ARC = YES; 237 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_COMMA = YES; 240 | CLANG_WARN_CONSTANT_CONVERSION = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 243 | CLANG_WARN_EMPTY_BODY = YES; 244 | CLANG_WARN_ENUM_CONVERSION = YES; 245 | CLANG_WARN_INFINITE_RECURSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 248 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 249 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 250 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 251 | CLANG_WARN_STRICT_PROTOTYPES = YES; 252 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 253 | CLANG_WARN_UNREACHABLE_CODE = YES; 254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 255 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 256 | COPY_PHASE_STRIP = NO; 257 | DEBUG_INFORMATION_FORMAT = dwarf; 258 | ENABLE_STRICT_OBJC_MSGSEND = YES; 259 | ENABLE_TESTABILITY = YES; 260 | GCC_C_LANGUAGE_STANDARD = gnu99; 261 | GCC_DYNAMIC_NO_PIC = NO; 262 | GCC_NO_COMMON_BLOCKS = YES; 263 | GCC_OPTIMIZATION_LEVEL = 0; 264 | GCC_PREPROCESSOR_DEFINITIONS = ( 265 | "DEBUG=1", 266 | "$(inherited)", 267 | ); 268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 270 | GCC_WARN_UNDECLARED_SELECTOR = YES; 271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 272 | GCC_WARN_UNUSED_FUNCTION = YES; 273 | GCC_WARN_UNUSED_VARIABLE = YES; 274 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 275 | MTL_ENABLE_DEBUG_INFO = YES; 276 | ONLY_ACTIVE_ARCH = YES; 277 | SDKROOT = iphoneos; 278 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 279 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 280 | TARGETED_DEVICE_FAMILY = "1,2"; 281 | }; 282 | name = Debug; 283 | }; 284 | 356F5AF31E7F689900BCB6D0 /* Release */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | CLANG_ANALYZER_NONNULL = YES; 289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 290 | CLANG_CXX_LIBRARY = "libc++"; 291 | CLANG_ENABLE_MODULES = YES; 292 | CLANG_ENABLE_OBJC_ARC = YES; 293 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 294 | CLANG_WARN_BOOL_CONVERSION = YES; 295 | CLANG_WARN_COMMA = YES; 296 | CLANG_WARN_CONSTANT_CONVERSION = YES; 297 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 298 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 299 | CLANG_WARN_EMPTY_BODY = YES; 300 | CLANG_WARN_ENUM_CONVERSION = YES; 301 | CLANG_WARN_INFINITE_RECURSION = YES; 302 | CLANG_WARN_INT_CONVERSION = YES; 303 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 306 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 307 | CLANG_WARN_STRICT_PROTOTYPES = YES; 308 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 309 | CLANG_WARN_UNREACHABLE_CODE = YES; 310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 311 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 312 | COPY_PHASE_STRIP = NO; 313 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 314 | ENABLE_NS_ASSERTIONS = NO; 315 | ENABLE_STRICT_OBJC_MSGSEND = YES; 316 | GCC_C_LANGUAGE_STANDARD = gnu99; 317 | GCC_NO_COMMON_BLOCKS = YES; 318 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 319 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 320 | GCC_WARN_UNDECLARED_SELECTOR = YES; 321 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 322 | GCC_WARN_UNUSED_FUNCTION = YES; 323 | GCC_WARN_UNUSED_VARIABLE = YES; 324 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 325 | MTL_ENABLE_DEBUG_INFO = NO; 326 | SDKROOT = iphoneos; 327 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 328 | TARGETED_DEVICE_FAMILY = "1,2"; 329 | VALIDATE_PRODUCT = YES; 330 | }; 331 | name = Release; 332 | }; 333 | 356F5AF51E7F689900BCB6D0 /* Debug */ = { 334 | isa = XCBuildConfiguration; 335 | buildSettings = { 336 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 337 | DEVELOPMENT_TEAM = E8ZU3Y58CG; 338 | INFOPLIST_FILE = ConvenientPickerView/Info.plist; 339 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 340 | PRODUCT_BUNDLE_IDENTIFIER = cn.smm.anRan.ConvenientPickerView; 341 | PRODUCT_NAME = "$(TARGET_NAME)"; 342 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 343 | SWIFT_VERSION = 4.0; 344 | }; 345 | name = Debug; 346 | }; 347 | 356F5AF61E7F689900BCB6D0 /* Release */ = { 348 | isa = XCBuildConfiguration; 349 | buildSettings = { 350 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 351 | DEVELOPMENT_TEAM = E8ZU3Y58CG; 352 | INFOPLIST_FILE = ConvenientPickerView/Info.plist; 353 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 354 | PRODUCT_BUNDLE_IDENTIFIER = cn.smm.anRan.ConvenientPickerView; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 357 | SWIFT_VERSION = 4.0; 358 | }; 359 | name = Release; 360 | }; 361 | /* End XCBuildConfiguration section */ 362 | 363 | /* Begin XCConfigurationList section */ 364 | 356F5ADD1E7F689800BCB6D0 /* Build configuration list for PBXProject "ConvenientPickerView" */ = { 365 | isa = XCConfigurationList; 366 | buildConfigurations = ( 367 | 356F5AF21E7F689900BCB6D0 /* Debug */, 368 | 356F5AF31E7F689900BCB6D0 /* Release */, 369 | ); 370 | defaultConfigurationIsVisible = 0; 371 | defaultConfigurationName = Release; 372 | }; 373 | 356F5AF41E7F689900BCB6D0 /* Build configuration list for PBXNativeTarget "ConvenientPickerView" */ = { 374 | isa = XCConfigurationList; 375 | buildConfigurations = ( 376 | 356F5AF51E7F689900BCB6D0 /* Debug */, 377 | 356F5AF61E7F689900BCB6D0 /* Release */, 378 | ); 379 | defaultConfigurationIsVisible = 0; 380 | defaultConfigurationName = Release; 381 | }; 382 | /* End XCConfigurationList section */ 383 | }; 384 | rootObject = 356F5ADA1E7F689800BCB6D0 /* Project object */; 385 | } 386 | -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/project.xcworkspace/xcuserdata/anran.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnRanScheme/ConvenientPickerView/dcde8beb5f418c1b84045794c31627533b2fee97/ConvenientPickerView.xcodeproj/project.xcworkspace/xcuserdata/anran.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/project.xcworkspace/xcuserdata/macbook.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnRanScheme/ConvenientPickerView/dcde8beb5f418c1b84045794c31627533b2fee97/ConvenientPickerView.xcodeproj/project.xcworkspace/xcuserdata/macbook.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/xcuserdata/anran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/xcuserdata/anran.xcuserdatad/xcschemes/ConvenientPickerView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/xcuserdata/anran.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ConvenientPickerView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 356F5AE11E7F689900BCB6D0 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/xcuserdata/macbook.xcuserdatad/xcschemes/ConvenientPickerView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ConvenientPickerView.xcodeproj/xcuserdata/macbook.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ConvenientPickerView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 356F5AE11E7F689900BCB6D0 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ConvenientPickerView/ARAButtonViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ARAButtonViewController.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/21. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let singleData = ["swift", "ObjecTive-C", "C", "C++", "java", "php", "python", "ruby", "js"] 12 | // 每一列为数组 13 | let multipleData = [["1天", "2天", "3天", "4天", "5天", "6天", "7天"],["1小时", "2小时", "3小时", "4小时", "5小时"], ["1分钟","2分钟","3分钟","4分钟","5分钟","6分钟","7分钟","8分钟","9分钟","10分钟"]] 14 | 15 | // 注意这个数据的格式!!!!!! 16 | let multipleAssociatedData: [[[String: [String]?]]] = [// 数组 17 | 18 | [ ["交通工具": ["陆地", "空中", "水上"]],//字典 19 | ["食品": ["健康食品", "垃圾食品"]], 20 | ["游戏": ["益智游戏", "角色游戏"]] 21 | 22 | ],// 数组 23 | 24 | [ ["陆地": ["公交车", "小轿车", "自行车"]], 25 | ["空中": ["飞机"]], 26 | ["水上": ["轮船"]], 27 | ["健康食品": ["蔬菜", "水果"]], 28 | ["垃圾食品": ["辣条", "不健康小吃"]], 29 | ["益智游戏": ["消消乐", "消灭星星"]], 30 | ["角色游戏": ["lol", "cf"]] 31 | 32 | ] 33 | ] 34 | 35 | class ARAButtonViewController: UIViewController { 36 | 37 | 38 | @IBOutlet weak var selectedLabel: UILabel! 39 | 40 | @IBAction func singleBtnOnClick(_ sender: UIButton) { 41 | ConvenientPickerView.showSingleColPicker("单列数据", 42 | data: singleData, 43 | defaultSelectedIndex: 2) { [unowned self] 44 | (selectIndex, selectValue) in 45 | self.selectedLabel.text = "选中了第\(selectIndex)行----选中的数据为\(selectValue)" 46 | } 47 | } 48 | 49 | @IBAction func multipleBtnOnClick(_ sender: UIButton) { 50 | ConvenientPickerView.showMultipleColsPicker("多列不关联数据", 51 | data: multipleData, 52 | defaultSelectedIndexs: [0,1,2]) {[unowned self] (selectedIndexs, selectedValues) in 53 | self.selectedLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 54 | } 55 | } 56 | 57 | 58 | @IBAction func multipleAssociatedBtnOnClick(_ sender: UIButton) { 59 | // 注意这里设置的是默认的选中值, 而不是选中的下标,省得去数关联数组里的下标 60 | ConvenientPickerView.showMultipleAssociatedColsPicker("多列关联数据", data: multipleAssociatedData, defaultSelectedValues: ["交通工具","陆地","自行车"]) {[unowned self] (selectedIndexs, selectedValues) in 61 | self.selectedLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 62 | } 63 | } 64 | 65 | @IBAction func citiesBtnOnClick(_ sender: UIButton) { 66 | // 注意设置默认值得时候, 必须设置完整, 不能进行省略 ["四川", "成都", "成华区"] 比如不能设置为["四川", "成都"] 67 | // ["北京", "通州"] 不能设置为["北京"] 68 | ConvenientPickerView.showCitiesPicker("省市区选择", 69 | defaultSelectedValues: ["北京", "/", "/"], 70 | selectTopLevel: true) { [unowned self] (selectedIndexs, selectedValues) in 71 | // 处理数据 72 | let combinedString = selectedValues.reduce("", { (result, value) -> String in 73 | result + " " + value 74 | }) 75 | 76 | self.selectedLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(combinedString)" 77 | } 78 | 79 | } 80 | 81 | @IBAction func dateBtnOnClick(_ sender: UIButton) { 82 | ConvenientPickerView.showDatePicker("日期选择") {[unowned self] ( selectedDate) in 83 | let formatter = DateFormatter() 84 | formatter.dateFormat = "yyyy-MM-dd" 85 | let string = formatter.string(from: selectedDate) 86 | self.selectedLabel.text = "选中了的日期是\(string)" 87 | } 88 | } 89 | 90 | override func viewDidLoad() { 91 | super.viewDidLoad() 92 | } 93 | 94 | override func didReceiveMemoryWarning() { 95 | super.didReceiveMemoryWarning() 96 | } 97 | 98 | 99 | /* 100 | // MARK: - Navigation 101 | 102 | // In a storyboard-based application, you will often want to do a little preparation before navigation 103 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 104 | // Get the new view controller using segue.destinationViewController. 105 | // Pass the selected object to the new view controller. 106 | } 107 | */ 108 | 109 | } 110 | -------------------------------------------------------------------------------- /ConvenientPickerView/ARATextFieldViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ARATextFieldViewController.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/21. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ARATextFieldViewController: UIViewController { 12 | 13 | @IBOutlet weak var singleTextField: PickerTextField! 14 | @IBOutlet weak var multipleTextField: PickerTextField! 15 | @IBOutlet weak var multipleAssociatedTextField: PickerTextField! 16 | @IBOutlet weak var citiesTextField: PickerTextField! 17 | @IBOutlet weak var dateTextField: PickerTextField! 18 | @IBOutlet weak var timeTextField: PickerTextField! 19 | 20 | @IBOutlet weak var selectedDataLabel: UILabel! 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | // 代码生成 25 | let test = PickerTextField(frame: CGRect(x: 20, y: timeTextField.frame.maxY, width: 340, height: 28)) 26 | test.borderStyle = .roundedRect 27 | test.placeholder = "代码初始化" 28 | test.showSingleColPicker("测试代码", data: singleData, defaultSelectedIndex: 0, autoSetSelectedText: true) { [unowned self] (textField, selectedIndex, selectedValue) in 29 | print(selectedValue) 30 | self.selectedDataLabel.text = selectedValue 31 | } 32 | view.addSubview(test) 33 | 34 | singleTextField.showSingleColPicker("编程语言选择", data: singleData, defaultSelectedIndex: 2, autoSetSelectedText: true) {[unowned self] (textField, selectedIndex, selectedValue) in 35 | // 可以使用textField 也可以使用 self.singleTextField 36 | textField.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 37 | self.selectedDataLabel.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 38 | 39 | } 40 | 41 | 42 | multipleTextField.showMultipleColsPicker("持续时间选择", data: multipleData, defaultSelectedIndexs: [0,1,1], autoSetSelectedText: true) { [unowned self] (textField, selectedIndexs, selectedValues) in 43 | self.multipleTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 44 | 45 | } 46 | 47 | 48 | // 注意这里设置的是默认的选中值, 而不是选中的下标 49 | multipleAssociatedTextField.showMultipleAssociatedColsPicker("多列关联数据", data: multipleAssociatedData, defaultSelectedValues: ["交通工具","陆地","自行车"], autoSetSelectedText: true) {[unowned self] (textField, selectedIndexs, selectedValues) in 50 | self.multipleAssociatedTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 51 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 52 | 53 | } 54 | 55 | // 注意设置默认值得时候, 必须设置完整, 不能进行省略 ["四川", "成都", "成华区"] 比如不能设置为["四川", "成都"] 56 | // ["北京", "通州"] 不能设置为["北京"] 57 | citiesTextField.showCitiesPicker("省市区选择", defaultSelectedValues: ["四川", "成都", "郫县"], autoSetSelectedText: false) {[unowned self] (textField, selectedIndexs, selectedValues) in 58 | self.citiesTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 59 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 60 | 61 | } 62 | 63 | dateTextField.showDatePicker("日期选择", autoSetSelectedText: true) { (textField, selectedDate) in 64 | let formatter = DateFormatter() 65 | formatter.dateFormat = "yyyy-MM-dd" 66 | let string = formatter.string(from: selectedDate) 67 | textField.text = string 68 | } 69 | /// 70 | // style里面可以更改的和系统的DatePicker属性是一一对应的 71 | var dateStyle = DatePickerSetting() 72 | dateStyle.dateMode = .date 73 | let formatter = DateFormatter() 74 | formatter.dateFormat = "yyyy-MM-dd" 75 | let date = formatter.date(from: "2000-01-11") 76 | dateStyle.date = date! 77 | /// 78 | /// 注意使用这种方式的时候, 请设置 autoSetSelectedText = false, 否则显示的格式可能不是您需要的 79 | timeTextField.showDatePicker("时间选择", datePickerSetting: dateStyle, autoSetSelectedText: false) { (textField, selectedDate) in 80 | let formatter = DateFormatter() 81 | // H -> 24小时制 82 | formatter.dateFormat = "yyyy-MM-dd" 83 | let string = formatter.string(from: selectedDate) 84 | textField.text = string 85 | } 86 | 87 | 88 | } 89 | 90 | override func didReceiveMemoryWarning() { 91 | super.didReceiveMemoryWarning() 92 | // Dispose of any resources that can be recreated. 93 | } 94 | 95 | 96 | /* 97 | // MARK: - Navigation 98 | 99 | // In a storyboard-based application, you will often want to do a little preparation before navigation 100 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 101 | // Get the new view controller using segue.destinationViewController. 102 | // Pass the selected object to the new view controller. 103 | } 104 | */ 105 | 106 | } 107 | -------------------------------------------------------------------------------- /ConvenientPickerView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/20. 6 | // Copyright © 2017年 安然. 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 | -------------------------------------------------------------------------------- /ConvenientPickerView/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ConvenientPickerView/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 | -------------------------------------------------------------------------------- /ConvenientPickerView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Helvetica 14 | 15 | 16 | HelveticaNeue 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 42 | 52 | 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 | 101 | 111 | 118 | 125 | 131 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 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 | 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 | 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 | 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 | -------------------------------------------------------------------------------- /ConvenientPickerView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | 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 | -------------------------------------------------------------------------------- /ConvenientPickerView/PickerView/City.plist: -------------------------------------------------------------------------------- 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 | 锡林郭勒盟 50 | 兴安盟 51 | 乌兰察布 52 | 鄂尔多斯 53 | 乌海 54 | 包头 55 | 呼和浩特 56 | 呼伦贝尔 57 | 通辽 58 | 阿拉善盟 59 | 赤峰 60 | 61 | 北京 62 | 63 | 通州 64 | 房山 65 | 昌平 66 | 顺义 67 | 怀柔 68 | 大兴 69 | 密云 70 | 平谷 71 | 延庆 72 | 东城 73 | 崇文 74 | 西城 75 | 朝阳 76 | 宣武 77 | 石景山 78 | 丰台 79 | 门头沟 80 | 海淀 81 | 82 | 台湾 83 | 84 | 新竹 85 | 基隆 86 | 高雄 87 | 台北 88 | 台南 89 | 嘉义 90 | 台中 91 | 宜兰 92 | 屏东 93 | 桃园 94 | 台东 95 | 苗栗 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 | 甘孜藏族 136 | 雅安 137 | 内江 138 | 攀枝花 139 | 140 | 国外 141 | 142 | 德国 143 | 新加坡 144 | 美国 145 | 加拿大 146 | 澳大利亚 147 | 日本 148 | 英国 149 | 巴西 150 | 俄罗斯 151 | 尼日利亚 152 | 马来西亚 153 | 爱尔兰 154 | 奥地利 155 | 挪威 156 | 意大利 157 | 西班牙 158 | 泰国 159 | 芬兰 160 | 丹麦 161 | 荷兰 162 | 阿联酋 163 | 瑞典 164 | 瑞士 165 | 比利时 166 | 新西兰 167 | 法国 168 | 韩国 169 | 匈牙利 170 | 其他 171 | 越南 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 | 银川 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 | 深圳 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 | 和田地 322 | 博尔塔拉 323 | 喀什地 324 | 325 | 江苏 326 | 327 | 连云港 328 | 盐城 329 | 无锡 330 | 宿迁 331 | 扬州 332 | 镇江 333 | 南京 334 | 徐州 335 | 泰州 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 | 南沙群岛 408 | 陵水黎族 409 | 中沙群岛 410 | 屯昌 411 | 昌江黎族 412 | 乐东黎族 413 | 琼海 414 | 儋州 415 | 文昌 416 | 万宁 417 | 白沙黎族 418 | 海口 419 | 三亚 420 | 五指山 421 | 琼中 422 | 东方 423 | 定安 424 | 西沙群岛 425 | 临高 426 | 427 | 湖北 428 | 429 | 咸宁 430 | 宜昌 431 | 黄冈 432 | 荆州 433 | 孝感 434 | 荆门 435 | 十堰 436 | 鄂州 437 | 天门 438 | 潜江 439 | 恩施 440 | 武汉 441 | 仙桃 442 | 神农架林 443 | 随州 444 | 黄石 445 | 襄樊 446 | 447 | 湖南 448 | 449 | 郴州 450 | 岳阳 451 | 怀化 452 | 娄底 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 | 金昌 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 | 505 | 阿里地 506 | 拉萨 507 | 山南地 508 | 日喀则地 509 | 那曲地 510 | 昌都地 511 | 林芝地 512 | 513 | 贵州 514 | 515 | 毕节地 516 | 黔南 517 | 六盘水 518 | 黔东南 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 | 城口 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 | 深水埗 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 | -------------------------------------------------------------------------------- /ConvenientPickerView/PickerView/ConvenientPickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConvenientPickerView.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/20. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ConvenientPickerView: UIView { 12 | 13 | public typealias ButtonAction = () -> Void 14 | public typealias SingleCompleteAction = (_ selectedIndex: Int, _ selectedValue: String) -> Void 15 | public typealias MultipleCompleteAction = (_ selectedIndexs: [Int], _ selectedValues: [String]) -> Void 16 | public typealias DateCompleteAction = (_ selectedDate: Date) -> Void 17 | 18 | public typealias MultipleAssociatedDataType = [[[String: [String]?]]] 19 | 20 | fileprivate var pickerView: PickerView! = PickerView() 21 | //MARK:- 常量 22 | fileprivate let pickerViewHeight:CGFloat = 260.0 23 | 24 | fileprivate let screenWidth = UIScreen.main.bounds.size.width 25 | fileprivate let screenHeight = UIScreen.main.bounds.size.height 26 | fileprivate var hideFrame: CGRect { 27 | return CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: pickerViewHeight) 28 | } 29 | fileprivate var showFrame: CGRect { 30 | return CGRect(x: 0.0, y: screenHeight - pickerViewHeight, width: screenWidth, height: pickerViewHeight) 31 | } 32 | 33 | /// 使用NSArray 可以存任何"东西", 如果使用 [Any], 那么当 34 | /// let a = ["1", "2"] var b:[Any] = a 会报错 35 | 36 | // MARK: - 初始化 37 | // 单列 38 | convenience init(frame: CGRect, toolBarTitle: String, singleColData: [String], defaultSelectedIndex: Int?, completeAction: SingleCompleteAction?) { 39 | 40 | self.init(frame: frame) 41 | 42 | pickerView = PickerView.singleColPicker(toolBarTitle, singleColData: singleColData, defaultIndex: defaultSelectedIndex, cancelAction: {[unowned self] in 43 | // 点击取消的时候移除 44 | self.hidePicker() 45 | 46 | }, completeAction: {[unowned self] (selectedIndex, selectedValue) in 47 | completeAction?(selectedIndex, selectedValue) 48 | self.hidePicker() 49 | 50 | }) 51 | pickerView.frame = hideFrame 52 | addSubview(pickerView) 53 | 54 | // 点击背景移除self 55 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 56 | addGestureRecognizer(tap) 57 | 58 | } 59 | // 多列不关联 60 | convenience init(frame: CGRect, toolBarTitle: String, multipleColsData: [[String]], defaultSelectedIndexs: [Int]?, completeAction: MultipleCompleteAction?) { 61 | 62 | self.init(frame: frame) 63 | 64 | pickerView = PickerView.multipleCosPicker(toolBarTitle, multipleColsData: multipleColsData, defaultSelectedIndexs: defaultSelectedIndexs, cancelAction: {[unowned self] in 65 | // 点击取消的时候移除 66 | self.hidePicker() 67 | 68 | }, completeAction: {[unowned self] (selectedIndexs, selectedValues) in 69 | completeAction?(selectedIndexs, selectedValues) 70 | self.hidePicker() 71 | }) 72 | pickerView.frame = hideFrame 73 | addSubview(pickerView) 74 | 75 | // 点击背景移除self 76 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 77 | addGestureRecognizer(tap) 78 | 79 | } 80 | // 多列关联 81 | convenience init(frame: CGRect, toolBarTitle: String, multipleAssociatedColsData: MultipleAssociatedDataType, defaultSelectedValues: [String]?, completeAction: MultipleCompleteAction?) { 82 | 83 | self.init(frame: frame) 84 | 85 | pickerView = PickerView.multipleAssociatedCosPicker(toolBarTitle, multipleAssociatedColsData: multipleAssociatedColsData, defaultSelectedValues: defaultSelectedValues, cancelAction: {[unowned self] in 86 | // 点击取消的时候移除 87 | self.hidePicker() 88 | 89 | }, completeAction: {[unowned self] (selectedIndexs, selectedValues) in 90 | completeAction?(selectedIndexs, selectedValues) 91 | self.hidePicker() 92 | }) 93 | 94 | pickerView.frame = hideFrame 95 | addSubview(pickerView) 96 | 97 | // 点击背景移除self 98 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 99 | addGestureRecognizer(tap) 100 | 101 | } 102 | // 城市选择器 103 | convenience init(frame: CGRect, toolBarTitle: String, defaultSelectedValues: [String]?, selectTopLevel: Bool = false, completeAction: MultipleCompleteAction?) { 104 | 105 | self.init(frame: frame) 106 | 107 | pickerView = PickerView.citiesPicker(toolBarTitle, 108 | defaultSelectedValues: defaultSelectedValues, 109 | selectTopLevel: selectTopLevel, 110 | cancelAction: {[unowned self] in 111 | // 点击取消的时候移除 112 | self.hidePicker() 113 | 114 | }, completeAction: {[unowned self] (selectedIndexs, selectedValues) in 115 | completeAction?(selectedIndexs, selectedValues) 116 | self.hidePicker() 117 | }) 118 | 119 | pickerView.frame = hideFrame 120 | addSubview(pickerView) 121 | 122 | // 点击背景移除self 123 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 124 | addGestureRecognizer(tap) 125 | 126 | } 127 | // 日期选择器 128 | convenience init(frame: CGRect, toolBarTitle: String, datePickerSetting: DatePickerSetting, completeAction: DateCompleteAction?) { 129 | 130 | self.init(frame: frame) 131 | 132 | pickerView = PickerView.datePicker(toolBarTitle, datePickerSetting: datePickerSetting, cancelAction: {[unowned self] in 133 | // 点击取消的时候移除 134 | self.hidePicker() 135 | 136 | }, completeAction: {[unowned self] (selectedDate) in 137 | completeAction?(selectedDate) 138 | self.hidePicker() 139 | }) 140 | 141 | pickerView.frame = hideFrame 142 | addSubview(pickerView) 143 | 144 | // 点击背景移除self 145 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 146 | addGestureRecognizer(tap) 147 | 148 | } 149 | 150 | override init(frame: CGRect) { 151 | super.init(frame: frame) 152 | addOrentationObserver() 153 | } 154 | 155 | 156 | required public init?(coder aDecoder: NSCoder) { 157 | fatalError("init(coder:) has not been implemented") 158 | } 159 | 160 | deinit { 161 | NotificationCenter.default.removeObserver(self) 162 | print("\(self.debugDescription) --- 销毁") 163 | } 164 | 165 | 166 | } 167 | 168 | // MARK: - selector 169 | extension ConvenientPickerView { 170 | 171 | fileprivate func addOrentationObserver() { 172 | NotificationCenter.default.addObserver(self, selector: #selector(self.statusBarOrientationChange), name: NSNotification.Name.UIApplicationDidChangeStatusBarOrientation, object: nil) 173 | 174 | } 175 | // 屏幕旋转时移除pickerView 176 | @objc func statusBarOrientationChange() { 177 | removeFromSuperview() 178 | } 179 | @objc func tapAction(_ tap: UITapGestureRecognizer) { 180 | let location = tap.location(in: self) 181 | // 点击空白背景移除self 182 | if location.y <= screenHeight - pickerViewHeight { 183 | self.hidePicker() 184 | } 185 | } 186 | } 187 | 188 | // MARK: - 弹出和移除self 189 | extension ConvenientPickerView { 190 | 191 | fileprivate func showPicker() { 192 | // 通过window 弹出view 193 | let window = UIApplication.shared.keyWindow 194 | guard let currentWindow = window else { return } 195 | currentWindow.addSubview(self) 196 | 197 | // let pickerX = NSLayoutConstraint(item: self, attribute: .Leading, relatedBy: .Equal, toItem: currentWindow, attribute: .Leading, multiplier: 1.0, constant: 0.0) 198 | // 199 | // let pickerY = NSLayoutConstraint(item: self, attribute: .Top, relatedBy: .Equal, toItem: currentWindow, attribute: .Top, multiplier: 1.0, constant: 0.0) 200 | // let pickerW = NSLayoutConstraint(item: self, attribute: .Width, relatedBy: .Equal, toItem: currentWindow, attribute: .Width, multiplier: 1.0, constant: 0.0) 201 | // let pickerH = NSLayoutConstraint(item: self, attribute: .Height, relatedBy: .Equal, toItem: currentWindow, attribute: .Height, multiplier: 1.0, constant: 0.0) 202 | // self.translatesAutoresizingMaskIntoConstraints = false 203 | // 204 | // currentWindow.addConstraints([pickerX, pickerY, pickerW, pickerH]) 205 | 206 | UIView.animate(withDuration: 0.25, animations: {[unowned self] in 207 | self.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.1) 208 | self.pickerView.frame = self.showFrame 209 | }, completion: nil) 210 | 211 | 212 | } 213 | 214 | func hidePicker() { 215 | // 把self从window中移除 216 | UIView.animate(withDuration: 0.25, animations: { [unowned self] in 217 | self.backgroundColor = UIColor.clear 218 | self.pickerView.frame = self.hideFrame 219 | 220 | }, completion: {[unowned self] (_) in 221 | self.removeFromSuperview() 222 | }) 223 | } 224 | } 225 | 226 | // MARK: - 快速使用方法 227 | extension ConvenientPickerView { 228 | 229 | /// 单列选择器 230 | /// 231 | /// - parameter title: 标题 232 | /// - parameter data: 数据 233 | /// - parameter defaultSeletedIndex: 默认选中的行数 234 | /// - parameter CompleteAction: 响应完成的Closure 235 | /// 236 | /// - returns: 237 | public class func showSingleColPicker(_ toolBarTitle: String, data: [String], defaultSelectedIndex: Int?, completeAction: SingleCompleteAction?) { 238 | let window = UIApplication.shared.keyWindow 239 | guard let currentWindow = window else { return } 240 | 241 | let testView = ConvenientPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, singleColData: data,defaultSelectedIndex: defaultSelectedIndex ,completeAction: completeAction) 242 | 243 | testView.showPicker() 244 | 245 | } 246 | 247 | /// 多列不关联选择器 248 | /// 249 | /// - parameter title: 标题 250 | /// - parameter data: 数据 251 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 252 | /// - parameter CompleteAction: 响应完成的Closure 253 | /// 254 | /// - returns: 255 | public class func showMultipleColsPicker(_ toolBarTitle: String, data: [[String]], defaultSelectedIndexs: [Int]?,completeAction: MultipleCompleteAction?) { 256 | let window = UIApplication.shared.keyWindow 257 | guard let currentWindow = window else { return } 258 | 259 | let testView = ConvenientPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, multipleColsData: data, defaultSelectedIndexs: defaultSelectedIndexs, completeAction: completeAction) 260 | 261 | testView.showPicker() 262 | 263 | } 264 | 265 | /// 多列关联选择器 266 | /// 267 | /// - parameter title: 标题 268 | /// - parameter data: 数据, 数据的格式参照示例 269 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 270 | /// - parameter CompleteAction: 响应完成的Closure 271 | /// 272 | /// - returns: 273 | public class func showMultipleAssociatedColsPicker(_ toolBarTitle: String, data: MultipleAssociatedDataType, defaultSelectedValues: [String]?, completeAction: MultipleCompleteAction?) { 274 | let window = UIApplication.shared.keyWindow 275 | guard let currentWindow = window else { return } 276 | 277 | let testView = ConvenientPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, multipleAssociatedColsData: data, defaultSelectedValues: defaultSelectedValues, completeAction: completeAction) 278 | 279 | testView.showPicker() 280 | 281 | } 282 | 283 | 284 | /// 城市选择器 285 | /// 286 | /// - parameter title: 标题 287 | /// - parameter defaultSeletedValues: 默认选中的每一列的值, 注意不是行数 288 | /// - parameter CompleteAction: 响应完成的Closure 289 | /// 290 | /// - returns: 291 | public class func showCitiesPicker(_ toolBarTitle: String, defaultSelectedValues: [String]?, selectTopLevel: Bool=false, completeAction: MultipleCompleteAction?) { 292 | 293 | let window = UIApplication.shared.keyWindow 294 | guard let currentWindow = window else { return } 295 | 296 | let testView = ConvenientPickerView(frame: currentWindow.bounds, 297 | toolBarTitle: toolBarTitle, 298 | defaultSelectedValues: defaultSelectedValues, 299 | selectTopLevel: selectTopLevel, 300 | completeAction: completeAction) 301 | 302 | testView.showPicker() 303 | 304 | } 305 | 306 | /// 日期选择器 307 | /// 308 | /// - parameter title: 标题 309 | /// - parameter datePickerSetting: 可配置UIDatePicker的样式 310 | /// - parameter CompleteAction: 响应完成的Closure 311 | /// 312 | /// - returns: 313 | public class func showDatePicker(_ toolBarTitle: String, datePickerSetting: DatePickerSetting = DatePickerSetting(), completeAction: DateCompleteAction?) { 314 | 315 | let window = UIApplication.shared.keyWindow 316 | guard let currentWindow = window else { return } 317 | 318 | let testView = ConvenientPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, datePickerSetting: datePickerSetting, completeAction: completeAction) 319 | 320 | testView.showPicker() 321 | 322 | } 323 | 324 | } 325 | 326 | 327 | -------------------------------------------------------------------------------- /ConvenientPickerView/PickerView/Picker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Picker.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/20. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct AssociatedDataModel { 12 | var key: String 13 | var valueArray: [String]? 14 | init(key: String, valueArray: [String]? = nil) { 15 | self.key = key 16 | self.valueArray = valueArray 17 | } 18 | } 19 | 20 | class Picker: UIView { 21 | 22 | let screenWidth = UIScreen.main.bounds.size.width 23 | let screenHeight = UIScreen.main.bounds.size.height 24 | 25 | // 使用模型初始化数据示例 26 | let associatedData: [[AssociatedDataModel]] = [ 27 | // 第一列数据 (key) 28 | [ AssociatedDataModel(key: "交通工具"), 29 | AssociatedDataModel(key: "食品"), 30 | AssociatedDataModel(key: "游戏") 31 | 32 | ], 33 | // 第二列数据 (valueArray) 34 | [ AssociatedDataModel(key: "交通工具", valueArray: ["陆地", "空中", "水上"]), 35 | AssociatedDataModel(key: "食品", valueArray: ["健康食品", "垃圾食品"]), 36 | AssociatedDataModel(key: "游戏", valueArray: ["益智游戏", "角色游戏"]), 37 | 38 | ], 39 | 40 | // 第三列数据 (valueArray) 41 | [ AssociatedDataModel(key: "陆地", valueArray: ["公交车", "小轿车", "自行车"]), 42 | AssociatedDataModel(key: "空中", valueArray: ["飞机"]), 43 | AssociatedDataModel(key: "水上", valueArray: ["轮船"]), 44 | AssociatedDataModel(key: "健康食品", valueArray: ["蔬菜", "水果"]), 45 | AssociatedDataModel(key: "垃圾食品", valueArray: ["辣条", "不健康小吃"]), 46 | AssociatedDataModel(key: "益智游戏", valueArray: ["消消乐", "消灭星星"]), 47 | AssociatedDataModel(key: "角色游戏", valueArray: ["lol", "cf"]) 48 | 49 | ] 50 | 51 | ] 52 | 53 | enum PickerStyles: Int { 54 | case Single = 0 55 | case Multiple 56 | case MultipleAssociated 57 | } 58 | 59 | var pickerStyle: PickerStyles = .Single 60 | 61 | // 完成按钮的响应Closure 62 | typealias ButtonAction = () -> () 63 | typealias SingleCompleteAction = (_ selectedIndex: Int, _ selectedValue: String) -> () 64 | typealias MultipleCompleteAction = (_ selectedIndexs: [Int], _ selectedValues: [String]) -> () 65 | 66 | fileprivate var cancelAction: ButtonAction? { 67 | didSet { 68 | tool.cancelAction = cancelAction 69 | } 70 | } 71 | 72 | // MARK: - 只有一列的时候用到的属性 73 | fileprivate var singleCompleteOnClick: SingleCompleteAction? { 74 | didSet { 75 | tool.completeAction = {[unowned self] in 76 | self.singleCompleteOnClick?(self.selectedIndex, self.selectedValue) 77 | } 78 | } 79 | } 80 | 81 | fileprivate var defaultSelectedIndex: Int? { 82 | didSet { 83 | if let defaultIndex = defaultSelectedIndex, let singleData = singleColData { 84 | assert(defaultIndex >= 0 && defaultIndex < singleData.count, "设置的默认选中Index不合法") 85 | if defaultIndex >= 0 && defaultIndex < singleData.count { 86 | // 设置默认值 87 | selectedIndex = defaultIndex 88 | selectedValue = singleData[defaultIndex] 89 | // 滚动到默认位置 90 | pickerView.selectRow(defaultIndex, inComponent: 0, animated: false) 91 | } 92 | } else { 93 | // 没有默认值设置0行为默认值 94 | selectedIndex = 0 95 | selectedValue = singleColData![0] 96 | pickerView.selectRow(0, inComponent: 0, animated: false) 97 | } 98 | } 99 | } 100 | 101 | fileprivate var singleColData: [String]? 102 | 103 | fileprivate var selectedIndex: Int = 0 104 | fileprivate var selectedValue: String = "" 105 | 106 | // MARK: - 多列不关联的时候用到的属性 107 | var multipleCompleteOnClick: MultipleCompleteAction? { 108 | didSet { 109 | tool.completeAction = {[unowned self] in 110 | self.multipleCompleteOnClick?(self.selectedIndexs, self.selectedValues) 111 | } 112 | } 113 | } 114 | 115 | fileprivate var multipleColsData: [[String]]? { 116 | didSet { 117 | if let multipleData = multipleColsData { 118 | for _ in multipleData.indices { 119 | selectedIndexs.append(0) 120 | selectedValues.append(" ") 121 | } 122 | } 123 | } 124 | } 125 | 126 | fileprivate var defalultSelectedIndexs: [Int]? { 127 | didSet { 128 | if let defaultIndexs = defalultSelectedIndexs { 129 | defaultIndexs.enumerated().forEach({ (component, row) in 130 | assert(component < pickerView.numberOfComponents && row < pickerView.numberOfRows(inComponent: component), "设置的默认选中Indexs有不合法的") 131 | if component < pickerView.numberOfComponents && row < pickerView.numberOfRows(inComponent: component) { 132 | // 滚动到默认位置 133 | pickerView.selectRow(row, inComponent: component, animated: false) 134 | // 设置默认值 135 | selectedIndexs[component] = row 136 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 137 | } 138 | }) 139 | } else { 140 | multipleColsData?.indices.forEach({ (index) in 141 | // 滚动到默认位置 142 | pickerView.selectRow(0, inComponent: index, animated: false) 143 | // 设置默认选中值 144 | selectedIndexs[index] = 0 145 | selectedValues[index] = self.pickerView(pickerView, titleForRow: 0, forComponent: index) ?? " " 146 | }) 147 | } 148 | } 149 | } 150 | 151 | 152 | fileprivate var selectedIndexs: [Int] = [] 153 | fileprivate var selectedValues: [String] = [] 154 | 155 | // MARK: - 多列关联的时候用到的属性 156 | fileprivate var multipleAssociatedColsData: [[AssociatedDataModel]]? { 157 | didSet { 158 | if let multipleAssociatedData = multipleAssociatedColsData { 159 | // 初始化选中的values 160 | for _ in multipleAssociatedData.indices { 161 | selectedIndexs.append(0) 162 | selectedValues.append(" ") 163 | } 164 | } 165 | } 166 | } 167 | 168 | // 设置第一组的数据, 使用数组是因为字典无序,需要设置默认选中值的时候获取到准确的下标滚动到相应的行 169 | fileprivate var defaultSelectedValues: [String]? { 170 | didSet { 171 | if let defaultValues = defaultSelectedValues { 172 | // 设置默认值 173 | selectedValues = defaultValues 174 | defaultValues.enumerated().forEach { (component: Int, element: String) in 175 | var row: Int? = nil 176 | if component == 0 { 177 | 178 | let firstData = multipleAssociatedColsData![0] 179 | 180 | for (index, associatedModel) in firstData.enumerated() { 181 | if associatedModel.key == element { 182 | row = index 183 | } 184 | } 185 | 186 | } else { 187 | 188 | let associatedModels = multipleAssociatedColsData![component] 189 | var arr: [String]? 190 | 191 | for associatedModel in associatedModels { 192 | if associatedModel.key == selectedValues[component - 1] { 193 | arr = associatedModel.valueArray 194 | } 195 | } 196 | 197 | row = arr?.index(of: element) 198 | 199 | } 200 | 201 | assert(row != nil, "第\(component)列设置的默认值有误") 202 | 203 | if row == nil { 204 | row = 0 205 | print("第\(component)列设置的默认值有误") 206 | } 207 | 208 | if component < pickerView.numberOfComponents { 209 | // 滚动到默认的位置 210 | pickerView.selectRow(row!, inComponent: component, animated: false) 211 | // 设置选中的下标 212 | selectedIndexs[component] = row! 213 | } 214 | 215 | } 216 | 217 | } else { 218 | 219 | multipleAssociatedColsData?.indices.forEach({ (index) in 220 | // 滚动到默认的位置 0 行 221 | pickerView.selectRow(0, inComponent: index, animated: false) 222 | // 设置默认的选中值 223 | selectedValues[index] = self.pickerView(pickerView, titleForRow: 0, forComponent: index) ?? " " 224 | selectedIndexs[index] = 0 225 | }) 226 | 227 | } 228 | 229 | } 230 | 231 | } 232 | 233 | // 公用属性 234 | 235 | private lazy var pickerView: UIPickerView = { [unowned self] in 236 | let picker = UIPickerView() 237 | picker.delegate = self 238 | picker.dataSource = self 239 | picker.backgroundColor = UIColor.white 240 | return picker 241 | }() 242 | 243 | private lazy var tool: ToolBarView = ToolBarView() 244 | 245 | private let pickerViewHeight: CGFloat = 216.0 246 | private let toolBarHeight: CGFloat = 44.0 247 | 248 | let screenW = UIScreen.main.bounds.size.width 249 | 250 | init() { 251 | let frame = CGRect(x: 0.0, y: 0.0, width: screenW, height: toolBarHeight + pickerViewHeight) 252 | super.init(frame: frame) 253 | commonInit() 254 | } 255 | 256 | required init?(coder aDecoder: NSCoder) { 257 | fatalError("init(coder:) has not been implemented") 258 | } 259 | 260 | deinit { 261 | print("\(self.debugDescription) --- 销毁") 262 | } 263 | 264 | func commonInit() { 265 | addSubview(tool) 266 | addSubview(pickerView) 267 | } 268 | 269 | override func layoutSubviews() { 270 | super.layoutSubviews() 271 | tool.frame = CGRect(x: 0.0, y: 0.0, width: screenWidth, height: toolBarHeight) 272 | pickerView.frame = CGRect(x: 0.0, y: 44.0, width: screenW, height: pickerViewHeight) 273 | } 274 | } 275 | 276 | 277 | extension Picker: UIPickerViewDelegate, UIPickerViewDataSource { 278 | 279 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 280 | switch pickerStyle { 281 | case .Single: 282 | return singleColData == nil ? 0 : 1 283 | case .Multiple: 284 | return multipleColsData?.count ?? 0 285 | case .MultipleAssociated: 286 | return multipleAssociatedColsData?.count ?? 0 287 | } 288 | } 289 | 290 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 291 | switch pickerStyle { 292 | case .Single: 293 | return singleColData?.count ?? 0 294 | case .Multiple: 295 | return multipleColsData?[component].count ?? 0 296 | case .MultipleAssociated: 297 | if let multipleAssociatedData = multipleAssociatedColsData { 298 | 299 | if component == 0 { 300 | return multipleAssociatedData[0].count 301 | } else { 302 | 303 | let associatedDataModels = multipleAssociatedData[component] 304 | var arr: [String]? 305 | 306 | for associatedDataModel in associatedDataModels { 307 | if associatedDataModel.key == selectedValues[component - 1] { 308 | arr = associatedDataModel.valueArray 309 | } 310 | } 311 | 312 | return arr?.count ?? 0 313 | } 314 | } 315 | return 0 316 | } 317 | } 318 | 319 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 320 | 321 | switch pickerStyle { 322 | case .Single: 323 | selectedIndex = row 324 | selectedValue = singleColData![row] 325 | case .Multiple: 326 | selectedIndexs[component] = row 327 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 328 | case .MultipleAssociated: 329 | // 设置选中值 330 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 331 | selectedIndexs[component] = row 332 | // 更新下一列关联的值 333 | if component < multipleAssociatedColsData!.count - 1 { 334 | pickerView.reloadComponent(component + 1) 335 | // 递归 336 | self.pickerView(pickerView, didSelectRow: 0, inComponent: component+1) 337 | pickerView.selectRow(0, inComponent: component+1, animated: true) 338 | 339 | } 340 | 341 | } 342 | } 343 | 344 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 345 | 346 | switch pickerStyle { 347 | case .Single: 348 | return singleColData?[row] 349 | case .Multiple: 350 | return multipleColsData?[component][row] 351 | case .MultipleAssociated: 352 | 353 | if let multipleAssociatedData = multipleAssociatedColsData { 354 | 355 | if component == 0 { 356 | return multipleAssociatedData[0][row].key 357 | }else { 358 | let associatedDataModels = multipleAssociatedData[component] 359 | var arr: [String]? 360 | 361 | for associatedDataModel in associatedDataModels { 362 | if associatedDataModel.key == selectedValues[component - 1] { 363 | arr = associatedDataModel.valueArray 364 | } 365 | } 366 | if arr?.count == 0 { 367 | // 空数组 368 | return nil 369 | } 370 | return arr?[row] 371 | 372 | } 373 | 374 | } 375 | 376 | return nil 377 | } 378 | 379 | } 380 | 381 | } 382 | 383 | 384 | // MARK: - 快速使用方法 385 | extension Picker { 386 | 387 | /// 单列 388 | class func singleColPicker(singleColData: [String], defaultIndex: Int?, cancelAction: ButtonAction?, completeAction: SingleCompleteAction?) -> Picker { 389 | let pic = Picker() 390 | pic.pickerStyle = .Single 391 | pic.singleColData = singleColData 392 | pic.defaultSelectedIndex = defaultIndex 393 | pic.singleCompleteOnClick = completeAction 394 | pic.cancelAction = cancelAction 395 | return pic 396 | } 397 | 398 | /// 多列不关联 399 | class func multipleCosPicker(multipleColsData: [[String]], defaultSelectedIndexs: [Int]?,cancelAction: ButtonAction?, completeAction: MultipleCompleteAction?) -> Picker { 400 | 401 | let pic = Picker() 402 | 403 | pic.pickerStyle = .Multiple 404 | 405 | pic.multipleColsData = multipleColsData 406 | pic.defalultSelectedIndexs = defaultSelectedIndexs 407 | pic.cancelAction = cancelAction 408 | pic.multipleCompleteOnClick = completeAction 409 | return pic 410 | 411 | } 412 | 413 | /// 多列关联 414 | class func multipleAssociatedCosPicker(multipleAssociatedColsData: [[AssociatedDataModel]], defaultSelectedValues: [String]?, cancelAction: ButtonAction?, completeAction: MultipleCompleteAction?) -> Picker { 415 | 416 | let pic = Picker() 417 | pic.pickerStyle = .MultipleAssociated 418 | pic.multipleAssociatedColsData = multipleAssociatedColsData 419 | 420 | pic.defaultSelectedValues = defaultSelectedValues 421 | pic.cancelAction = cancelAction 422 | pic.multipleCompleteOnClick = completeAction 423 | return pic 424 | 425 | } 426 | 427 | /// 城市选择器 428 | class func citiesPicker(defaultSelectedValues: [String]?, cancelAction: ButtonAction?, completeAction: MultipleCompleteAction?) -> Picker { 429 | 430 | let provincePath = Bundle.main.path(forResource: "Province", ofType: "plist") 431 | let cityPath = Bundle.main.path(forResource: "City", ofType: "plist") 432 | let areaPath = Bundle.main.path(forResource: "Area", ofType: "plist") 433 | 434 | let proviceArr = NSArray(contentsOfFile: provincePath!) 435 | let cityArr = NSDictionary(contentsOfFile: cityPath!) 436 | let areaArr = NSDictionary(contentsOfFile: areaPath!) 437 | 438 | var provinceModelArr: [AssociatedDataModel] = [] 439 | var citiesModelArr: [AssociatedDataModel] = [] 440 | var areasModelArr: [AssociatedDataModel] = [] 441 | 442 | proviceArr?.forEach({ (element) in 443 | if let province = element as? String { 444 | provinceModelArr.append(AssociatedDataModel(key: province)) 445 | 446 | let citys = cityArr?[province] as? [String] 447 | citiesModelArr.append(AssociatedDataModel(key: province, valueArray: citys)) 448 | 449 | citys?.forEach({ (element) in 450 | let city = element 451 | let areas = areaArr?[city]as? [String] 452 | areasModelArr.append(AssociatedDataModel(key: city, valueArray: areas)) 453 | 454 | }) 455 | } 456 | }) 457 | 458 | let citiesArr = [provinceModelArr, citiesModelArr, areasModelArr] 459 | 460 | 461 | let pic = Picker.multipleAssociatedCosPicker(multipleAssociatedColsData: citiesArr, defaultSelectedValues: defaultSelectedValues, cancelAction: cancelAction, completeAction: completeAction) 462 | return pic 463 | 464 | } 465 | 466 | 467 | } 468 | 469 | -------------------------------------------------------------------------------- /ConvenientPickerView/PickerView/PickerTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PickerTextField.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/21. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class PickerTextField: UITextField { 12 | 13 | public typealias ButtonAction = () -> Void 14 | public typealias SingleCompleteAction = (_ textField: UITextField,_ selectedIndex: Int, _ selectedValue: String) -> Void 15 | public typealias MultipleCompleteAction = (_ textField: UITextField,_ selectedIndexs: [Int], _ selectedValues: [String]) -> Void 16 | public typealias DateCompleteAction = (_ textField: UITextField,_ selectedDate: Date) -> Void 17 | 18 | public typealias MultipleAssociatedDataType = [[[String: [String]?]]] 19 | 20 | /// 保存pickerView的初始化 21 | fileprivate var setUpPickerClosure:(() -> PickerView)? 22 | /// 如果设置了autoSetSelectedText为true 将自动设置text的值, 默认以空格分开多列选择, 但你仍然可以在响应完成的closure中修改text的值 23 | fileprivate var autoSetSelectedText = false 24 | 25 | //MARK: 初始化 26 | override public init(frame: CGRect) { 27 | super.init(frame: frame) 28 | commonInit() 29 | 30 | } 31 | // 从xib或storyboard中初始化时候调用 32 | required public init?(coder aDecoder: NSCoder) { 33 | super.init(coder: aDecoder) 34 | commonInit() 35 | } 36 | 37 | deinit { 38 | NotificationCenter.default.removeObserver(self) 39 | print("\(self.debugDescription) --- 销毁") 40 | } 41 | 42 | } 43 | 44 | // MARK: - 监听通知 45 | extension PickerTextField { 46 | 47 | fileprivate func commonInit() { 48 | NotificationCenter.default.addObserver(self, 49 | selector: #selector(self.didBeginEdit), 50 | name: NSNotification.Name.UITextFieldTextDidBeginEditing, 51 | object: self) 52 | NotificationCenter.default.addObserver(self, selector: #selector(self.didEndEdit), 53 | name: NSNotification.Name.UITextFieldTextDidEndEditing, 54 | object: self) 55 | } 56 | // 开始编辑添加pickerView 57 | @objc func didBeginEdit() { 58 | let pickerView = setUpPickerClosure?() 59 | pickerView?.delegate = self 60 | inputView = pickerView 61 | } 62 | // 编辑完成销毁pickerView 63 | @objc func didEndEdit() { 64 | inputView = nil 65 | } 66 | 67 | override open func caretRect(for position: UITextPosition) -> CGRect { 68 | return CGRect.zero 69 | } 70 | 71 | } 72 | 73 | extension PickerTextField: PickerViewDelegate { 74 | 75 | public func singleColDidSelected(_ selectedIndex: Int, selectedValue: String) { 76 | if autoSetSelectedText { 77 | text = " " + selectedValue 78 | } 79 | } 80 | 81 | public func multipleColsDidSelected(_ selectedIndexs: [Int], selectedValues: [String]) { 82 | 83 | 84 | if autoSetSelectedText { 85 | text = selectedValues.reduce("", { (result, selectedValue) -> String in 86 | result + " " + selectedValue 87 | }) 88 | } 89 | } 90 | 91 | public func dateDidSelected(_ selectedDate: Date) { 92 | if autoSetSelectedText { 93 | 94 | let formatter = DateFormatter() 95 | formatter.dateFormat = "yyyy-MM-dd" 96 | let string = formatter.string(from: selectedDate) 97 | text = " " + string 98 | } 99 | } 100 | 101 | } 102 | 103 | // MARK: - 使用方法 104 | extension PickerTextField { 105 | 106 | /// 单列选择器 107 | /// 108 | /// - parameter title: 标题 109 | /// - parameter data: 数据 110 | /// - parameter defaultSeletedIndex: 默认选中的行数 111 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 112 | /// - parameter completeAction: 响应完成的Closure 113 | /// 114 | /// - returns: 115 | public func showSingleColPicker(_ toolBarTitle: String, 116 | data: [String], 117 | defaultSelectedIndex: Int?, 118 | autoSetSelectedText: Bool, 119 | completeAction: SingleCompleteAction?) { 120 | 121 | self.autoSetSelectedText = autoSetSelectedText 122 | 123 | // 保存在这个closure中, 在开始编辑的时候在执行, 避免像之前在这里直接初始化pickerView, 每个SelectionTextField在调用这个方法的时候就初始化pickerView,当有多个pickerView的时候就很消耗内存 124 | setUpPickerClosure = {() -> PickerView in 125 | 126 | return PickerView.singleColPicker(toolBarTitle, singleColData: data, defaultIndex: defaultSelectedIndex, cancelAction: {[unowned self] in 127 | 128 | self.endEditing(true) 129 | 130 | }, completeAction: {[unowned self] (selectedIndex: Int, selectedValue: String) -> Void in 131 | 132 | completeAction?(self, selectedIndex, selectedValue) 133 | self.endEditing(true) 134 | 135 | }) 136 | 137 | 138 | } 139 | 140 | } 141 | 142 | /// 多列不关联选择器 143 | /// 144 | /// - parameter title: 标题 145 | /// - parameter data: 数据 146 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 147 | /// - parameter autoSetSelectedText: 设置为true的时候, 将俺默认的格式自动设置textField的值 148 | /// - parameter completeAction: 响应完成的Closure 149 | /// 150 | /// - returns: 151 | public func showMultipleColsPicker(_ toolBarTitle: String, data: [[String]], defaultSelectedIndexs: [Int]?, autoSetSelectedText: Bool, completeAction: MultipleCompleteAction?) { 152 | self.autoSetSelectedText = autoSetSelectedText 153 | 154 | setUpPickerClosure = {() -> PickerView in 155 | 156 | return PickerView.multipleCosPicker(toolBarTitle, multipleColsData: data, defaultSelectedIndexs: defaultSelectedIndexs, cancelAction: { [unowned self] in 157 | 158 | self.endEditing(true) 159 | 160 | }, completeAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 161 | 162 | completeAction?(self, selectedIndexs, selectedValues) 163 | self.endEditing(true) 164 | }) 165 | } 166 | 167 | } 168 | 169 | /// 多列关联选择器 170 | /// 171 | /// - parameter title: 标题 172 | /// - parameter data: 数据, 数据的格式参照示例 173 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 174 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 175 | /// - parameter completeAction: 响应完成的Closure 176 | /// 177 | /// - returns: 178 | public func showMultipleAssociatedColsPicker(_ toolBarTitle: String, data: MultipleAssociatedDataType, defaultSelectedValues: [String]?,autoSetSelectedText: Bool, completeAction: MultipleCompleteAction?) { 179 | self.autoSetSelectedText = autoSetSelectedText 180 | 181 | setUpPickerClosure = {() -> PickerView in 182 | 183 | return PickerView.multipleAssociatedCosPicker(toolBarTitle, multipleAssociatedColsData: data, defaultSelectedValues: defaultSelectedValues,cancelAction: { [unowned self] in 184 | 185 | self.endEditing(true) 186 | 187 | }, completeAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 188 | 189 | completeAction?(self, selectedIndexs, selectedValues) 190 | self.endEditing(true) 191 | }) 192 | } 193 | 194 | } 195 | 196 | 197 | /// 城市选择器 198 | /// 199 | /// - parameter title: 标题 200 | /// - parameter defaultSeletedValues: 默认选中的每一列的值, 注意不是行数 201 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 202 | /// - parameter completeAction: 响应完成的Closure 203 | /// 204 | /// - returns: 205 | 206 | public func showCitiesPicker(_ toolBarTitle: String, defaultSelectedValues: [String]?,autoSetSelectedText: Bool, completeAction: MultipleCompleteAction?) { 207 | self.autoSetSelectedText = autoSetSelectedText 208 | 209 | setUpPickerClosure = {() -> PickerView in 210 | return PickerView.citiesPicker(toolBarTitle, defaultSelectedValues: defaultSelectedValues, cancelAction: { [unowned self] in 211 | self.endEditing(true) 212 | 213 | }, completeAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 214 | 215 | completeAction?(self,selectedIndexs, selectedValues) 216 | self.endEditing(true) 217 | }) 218 | } 219 | 220 | } 221 | 222 | /// 日期选择器 223 | /// 224 | /// - parameter title: 标题 225 | /// - parameter datePickerSetting: 可配置UIDatePicker的样式 226 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 227 | /// - parameter completeAction: 响应完成的Closure 228 | /// 229 | /// - returns: 230 | public func showDatePicker(_ toolBarTitle: String, datePickerSetting: DatePickerSetting = DatePickerSetting(), autoSetSelectedText: Bool, completeAction: DateCompleteAction?) { 231 | self.autoSetSelectedText = autoSetSelectedText 232 | 233 | setUpPickerClosure = {() -> PickerView in 234 | return PickerView.datePicker(toolBarTitle, datePickerSetting: datePickerSetting, cancelAction: { [unowned self] in 235 | self.endEditing(true) 236 | 237 | }, completeAction: {[unowned self] (selectedDate) in 238 | completeAction?(self, selectedDate) 239 | self.endEditing(true) 240 | 241 | }) 242 | 243 | } 244 | } 245 | 246 | 247 | } 248 | -------------------------------------------------------------------------------- /ConvenientPickerView/PickerView/PickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PickerView.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/20. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - PickerViewDelegate 用于自动设置TextField的选中值 12 | public protocol PickerViewDelegate: class { 13 | func singleColDidSelected(_ selectedIndex: Int, selectedValue: String) 14 | func multipleColsDidSelected(_ selectedIndexs: [Int], selectedValues: [String]) 15 | func dateDidSelected(_ selectedDate: Date) 16 | } 17 | 18 | // MARK: - 配置UIDatePicker的样式 19 | public struct DatePickerSetting { 20 | /// 默认选中时间 21 | public var date = Date() 22 | public var dateMode = UIDatePickerMode.date 23 | // 最小时间 24 | public var minimumDate: Date? 25 | // 最大时间 26 | public var maximumDate: Date? 27 | public init() { 28 | 29 | } 30 | } 31 | 32 | 33 | // MARK: - PickerView 34 | open class PickerView: UIView { 35 | fileprivate let screenWidth = UIScreen.main.bounds.size.width 36 | fileprivate let pickerViewHeight: CGFloat = 216.0 37 | fileprivate let toolBarHeight: CGFloat = 44.0 38 | public enum PickerStyles { 39 | case single 40 | case multiple 41 | case multipleAssociated 42 | case date 43 | } 44 | open weak var delegate: PickerViewDelegate? 45 | fileprivate var toolBarTitle = "请选择" { 46 | didSet { 47 | toolBar.title = toolBarTitle 48 | } 49 | } 50 | fileprivate var pickerStyle: PickerStyles = .single 51 | // 配置UIDatePicker的样式 52 | fileprivate var datePickerSetting = DatePickerSetting() { 53 | didSet { 54 | datePicker.date = datePickerSetting.date 55 | datePicker.minimumDate = datePickerSetting.minimumDate 56 | datePicker.maximumDate = datePickerSetting.maximumDate 57 | datePicker.datePickerMode = datePickerSetting.dateMode 58 | /// set currentDate to the default 59 | selectedDate = datePickerSetting.date 60 | } 61 | } 62 | // 完成按钮的响应Closure 63 | public typealias ButtonAction = () -> Void 64 | public typealias SingleCompleteAction = (_ selectedIndex: Int, _ selectedValue: String) -> Void 65 | public typealias MultipleCompleteAction = (_ selectedIndexs: [Int], _ selectedValues: [String]) -> Void 66 | public typealias DateCompleteAction = (_ selectedDate: Date) -> Void 67 | public typealias MultipleAssociatedDataType = [[[String: [String]?]]] 68 | 69 | fileprivate var cancelAction: ButtonAction? { 70 | didSet { 71 | toolBar.cancelAction = cancelAction 72 | } 73 | } 74 | 75 | //MARK:- 只有一列的时候用到的属性 76 | fileprivate var singleCompleteOnClick:SingleCompleteAction? = nil { 77 | didSet { 78 | toolBar.completeAction = {[unowned self] in 79 | 80 | self.singleCompleteOnClick?(self.selectedIndex, self.selectedValue) 81 | } 82 | } 83 | } 84 | 85 | fileprivate var defalultSelectedIndex: Int? = nil { 86 | didSet { 87 | if let defaultIndex = defalultSelectedIndex, let singleData = singleColData { 88 | // 判断下标是否合法 89 | assert(defaultIndex >= 0 && defaultIndex < singleData.count, "设置的默认选中Index不合法") 90 | if defaultIndex >= 0 && defaultIndex < singleData.count { 91 | // 设置默认值 92 | selectedIndex = defaultIndex 93 | selectedValue = singleData[defaultIndex] 94 | // 滚动到默认位置 95 | pickerView.selectRow(defaultIndex, inComponent: 0, animated: false) 96 | 97 | } 98 | 99 | } else { 100 | // 没有默认值设置0行为默认值 101 | selectedIndex = 0 102 | selectedValue = singleColData![0] 103 | pickerView.selectRow(0, inComponent: 0, animated: false) 104 | 105 | } 106 | } 107 | } 108 | fileprivate var singleColData: [String]? = nil 109 | 110 | fileprivate var selectedIndex: Int = 0 111 | fileprivate var selectedValue: String = "" { 112 | didSet { 113 | delegate?.singleColDidSelected(selectedIndex, selectedValue: selectedValue) 114 | } 115 | } 116 | 117 | 118 | //MARK:- 有多列不关联的时候用到的属性 119 | fileprivate var multipleCompleteOnClick:MultipleCompleteAction? = nil { 120 | didSet { 121 | 122 | toolBar.completeAction = {[unowned self] in 123 | self.multipleCompleteOnClick?(self.selectedIndexs, self.selectedValues) 124 | } 125 | } 126 | } 127 | 128 | fileprivate var multipleColsData: [[String]]? = nil { 129 | didSet { 130 | if let multipleData = multipleColsData { 131 | for _ in multipleData.indices { 132 | selectedIndexs.append(0) 133 | selectedValues.append(" ") 134 | } 135 | 136 | } 137 | } 138 | } 139 | 140 | fileprivate var selectedIndexs: [Int] = [] 141 | fileprivate var selectedValues: [String] = [] { 142 | didSet { 143 | delegate?.multipleColsDidSelected(selectedIndexs, selectedValues: selectedValues) 144 | } 145 | } 146 | // 不关联的数据时直接设置默认的下标 147 | fileprivate var defalultSelectedIndexs: [Int]? = nil { 148 | didSet { 149 | if let defaultIndexs = defalultSelectedIndexs { 150 | 151 | defaultIndexs.enumerated().forEach({ (component: Int, row: Int) in 152 | 153 | assert(component < pickerView.numberOfComponents && row < pickerView.numberOfRows(inComponent: component), "设置的默认选中Indexs有不合法的") 154 | if component < pickerView.numberOfComponents && row < pickerView.numberOfRows(inComponent: component){ 155 | 156 | // 滚动到默认位置 157 | 158 | // 设置默认值 159 | selectedIndexs[component] = row 160 | selectedValues[component] = titleForRow(row, forComponent: component) ?? " " 161 | 162 | DispatchQueue.main.async(execute: { 163 | 164 | self.pickerView.selectRow(row, inComponent: component, animated: false) 165 | }) 166 | 167 | } 168 | 169 | }) 170 | 171 | } else { 172 | multipleColsData?.indices.forEach({ (index) in 173 | // 滚动到默认位置 174 | pickerView.selectRow(0, inComponent: index, animated: false) 175 | 176 | // 设置默认选中值 177 | selectedIndexs[index] = 0 178 | 179 | selectedValues[index] = titleForRow(0, forComponent: index) ?? " " 180 | 181 | }) 182 | } 183 | } 184 | } 185 | 186 | 187 | 188 | //MARK: - 有多列关联的时候用到的属性 189 | fileprivate var multipleAssociatedColsData: MultipleAssociatedDataType? = nil { 190 | didSet { 191 | 192 | if let multipleAssociatedData = multipleAssociatedColsData { 193 | // 初始化选中的values 194 | for _ in 0...multipleAssociatedData.count { 195 | selectedIndexs.append(0) 196 | selectedValues.append(" ") 197 | } 198 | } 199 | } 200 | } 201 | 202 | // 多列关联数据的时候设置默认的values而没有使用默认的index 203 | fileprivate var defaultSelectedValues: [String]? = nil { 204 | didSet { 205 | 206 | if let defaultValues = defaultSelectedValues { 207 | // this is a wrong way cause defaultValues is less than components' count 208 | // selectedValues = defaultValues 209 | defaultValues.enumerated().forEach { (component: Int, element: String) in 210 | var row: Int? = nil 211 | 212 | if component == 0 { 213 | let firstData = multipleAssociatedColsData![0] 214 | 215 | for (index,associatedModel) in firstData.enumerated() { 216 | if associatedModel.first!.0 == element { 217 | row = index 218 | break 219 | } 220 | } 221 | } else { 222 | 223 | let associatedModels = multipleAssociatedColsData![component - 1] 224 | var arr: [String]? 225 | 226 | for associatedModel in associatedModels { 227 | 228 | if associatedModel.first!.0 == defaultValues[component - 1] { 229 | arr = associatedModel.first!.1 230 | break 231 | } 232 | } 233 | row = arr?.index(of: element) 234 | 235 | } 236 | 237 | assert(row != nil, "第\(component)列设置的默认值有误") 238 | if row == nil { 239 | row = 0 240 | print("第\(component)列设置的默认值有误") 241 | } 242 | if component < pickerView.numberOfComponents { 243 | // print(" \(component) ----\(row!)") 244 | 245 | // 设置选中的下标 246 | selectedIndexs[component] = row! 247 | // 设置默认值 248 | selectedValues[component] = titleForRow(row!, forComponent: component) ?? " " 249 | // 滚动到默认的位置 250 | DispatchQueue.main.async(execute: { 251 | 252 | self.pickerView.selectRow(row!, inComponent: component, animated: false) 253 | }) 254 | 255 | } 256 | 257 | } 258 | 259 | 260 | } else { 261 | for index in 0...multipleAssociatedColsData!.count { 262 | // 滚动到默认的位置 0 行 263 | pickerView.selectRow(0, inComponent: index, animated: false) 264 | // 设置默认的选中值 265 | selectedValues[index] = titleForRow(0, forComponent: index) ?? " " 266 | 267 | selectedIndexs[index] = 0 268 | } 269 | } 270 | 271 | } 272 | 273 | } 274 | 275 | // MARK: - 日期选择器用到的属性 276 | fileprivate var selectedDate = Date() { 277 | didSet { 278 | delegate?.dateDidSelected(selectedDate) 279 | } 280 | } 281 | fileprivate var dateCompleteAction: DateCompleteAction? { 282 | didSet { 283 | toolBar.completeAction = {[unowned self] in 284 | self.dateCompleteAction?(self.selectedDate) 285 | } 286 | } 287 | } 288 | 289 | fileprivate lazy var pickerView: UIPickerView! = { [unowned self] in 290 | let picker = UIPickerView() 291 | picker.delegate = self 292 | picker.dataSource = self 293 | picker.backgroundColor = UIColor.white 294 | return picker 295 | }() 296 | 297 | fileprivate lazy var datePicker: UIDatePicker = {[unowned self] in 298 | let datePic = UIDatePicker() 299 | datePic.backgroundColor = UIColor.white 300 | // print(NSLocale.availableLocaleIdentifiers()) 301 | datePic.locale = Locale(identifier: "zh_CN") 302 | return datePic 303 | }() 304 | 305 | fileprivate lazy var toolBar: ToolBarView! = ToolBarView() 306 | 307 | // MARK: - 初始化 308 | public init(pickerStyle: PickerStyles) { 309 | let frame = CGRect(x: 0.0, y: 0.0, width: screenWidth, height: toolBarHeight + pickerViewHeight) 310 | self.pickerStyle = pickerStyle 311 | super.init(frame: frame) 312 | commonInit() 313 | } 314 | 315 | override public init(frame: CGRect) { 316 | super.init(frame: frame) 317 | commonInit() 318 | } 319 | 320 | required public init?(coder aDecoder: NSCoder) { 321 | super.init(coder: aDecoder) 322 | commonInit() 323 | } 324 | 325 | deinit { 326 | print("\(self.debugDescription) --- 销毁") 327 | } 328 | 329 | fileprivate func commonInit() { 330 | 331 | addSubview(toolBar) 332 | 333 | if pickerStyle == PickerStyles.date { 334 | datePicker.addTarget(self, 335 | action: #selector(self.dateDidChange(_:)), 336 | for: UIControlEvents.valueChanged) 337 | addSubview(datePicker) 338 | } else { 339 | addSubview(pickerView) 340 | } 341 | } 342 | 343 | @objc func dateDidChange(_ datePic: UIDatePicker) { 344 | selectedDate = datePic.date 345 | } 346 | 347 | override open func layoutSubviews() { 348 | super.layoutSubviews() 349 | 350 | let toolBarX = NSLayoutConstraint(item: toolBar, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1.0, constant: 0.0) 351 | 352 | let toolBarY = NSLayoutConstraint(item: toolBar, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0.0) 353 | let toolBarW = NSLayoutConstraint(item: toolBar, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0.0) 354 | let toolBarH = NSLayoutConstraint(item: toolBar, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: CGFloat(toolBarHeight)) 355 | toolBar.translatesAutoresizingMaskIntoConstraints = false 356 | 357 | addConstraints([toolBarX, toolBarY, toolBarW, toolBarH]) 358 | 359 | // 改用了autolayout 360 | 361 | // toolBar.frame = CGRect(x: 0.0, y: 0.0, width: Double(screenWidth), height: toolBarHeight) 362 | if pickerStyle == PickerStyles.date { 363 | // datePicker.frame = CGRect(x: 0.0, y: toolBarHeight, width: Double(screenWidth), height: pickerViewHeight) 364 | 365 | let pickerX = NSLayoutConstraint(item: datePicker, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1.0, constant: 0.0) 366 | 367 | let pickerY = NSLayoutConstraint(item: datePicker, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: CGFloat(toolBarHeight)) 368 | let pickerW = NSLayoutConstraint(item: datePicker, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0.0) 369 | let pickerH = NSLayoutConstraint(item: datePicker, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: CGFloat(pickerViewHeight)) 370 | datePicker.translatesAutoresizingMaskIntoConstraints = false 371 | addConstraints([pickerX, pickerY, pickerW, pickerH]) 372 | } else { 373 | // pickerView.frame = CGRect(x: 0.0, y: toolBarHeight, width: Double(screenWidth), height: pickerViewHeight) 374 | 375 | let pickerX = NSLayoutConstraint(item: pickerView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1.0, constant: 0.0) 376 | 377 | let pickerY = NSLayoutConstraint(item: pickerView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: CGFloat(toolBarHeight)) 378 | let pickerW = NSLayoutConstraint(item: pickerView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0.0) 379 | let pickerH = NSLayoutConstraint(item: pickerView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: CGFloat(pickerViewHeight)) 380 | pickerView.translatesAutoresizingMaskIntoConstraints = false 381 | 382 | addConstraints([pickerX, pickerY, pickerW, pickerH]) 383 | 384 | } 385 | 386 | } 387 | 388 | 389 | } 390 | 391 | // MARK: - UIPickerViewDelegate, UIPickerViewDataSource 392 | extension PickerView: UIPickerViewDelegate, UIPickerViewDataSource { 393 | 394 | final public func numberOfComponents(in pickerView: UIPickerView) -> Int { 395 | 396 | switch pickerStyle { 397 | case .single: 398 | return singleColData == nil ? 0 : 1 399 | case .multiple: 400 | return multipleColsData?.count ?? 0 401 | case .multipleAssociated: 402 | return multipleAssociatedColsData == nil ? 0 : multipleAssociatedColsData!.count+1 403 | default: 404 | return 0 405 | } 406 | 407 | } 408 | 409 | final public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 410 | 411 | switch pickerStyle { 412 | case .single: 413 | return singleColData?.count ?? 0 414 | case .multiple: 415 | return multipleColsData?[component].count ?? 0 416 | case .multipleAssociated: 417 | if let multipleAssociatedData = multipleAssociatedColsData { 418 | 419 | if component == 0 { 420 | return multipleAssociatedData[0].count 421 | }else { 422 | let associatedDataModels = multipleAssociatedData[component - 1] 423 | var arr: [String]? 424 | 425 | for associatedDataModel in associatedDataModels { 426 | if associatedDataModel.first!.0 == selectedValues[component - 1] { 427 | arr = associatedDataModel.first!.1 428 | } 429 | } 430 | 431 | return arr?.count ?? 0 432 | 433 | } 434 | 435 | } 436 | return 0 437 | default: 438 | return 0 439 | } 440 | 441 | } 442 | 443 | final public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 444 | switch pickerStyle { 445 | case .single: 446 | selectedIndex = row 447 | selectedValue = singleColData![row] 448 | case .multiple: 449 | selectedIndexs[component] = row 450 | if let title = titleForRow(row, forComponent: component) { 451 | selectedValues[component] = title 452 | } 453 | case .multipleAssociated: 454 | // 设置选中值 455 | 456 | if let title = titleForRow(row, forComponent: component) { 457 | selectedValues[component] = title 458 | selectedIndexs[component] = row 459 | // 更新下一列关联的值 460 | if component < multipleAssociatedColsData!.count { 461 | pickerView.reloadComponent(component + 1) 462 | // 递归 463 | self.pickerView(pickerView, didSelectRow: 0, inComponent: component+1) 464 | pickerView.selectRow(0, inComponent: component+1, animated: true) 465 | 466 | } 467 | 468 | } 469 | 470 | 471 | default : return 472 | } 473 | 474 | 475 | } 476 | 477 | final public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { 478 | 479 | let label = UILabel() 480 | label.textAlignment = .center 481 | label.adjustsFontSizeToFitWidth = true 482 | label.textColor = UIColor.black 483 | label.font = UIFont.systemFont(ofSize: 18.0) 484 | label.backgroundColor = UIColor.clear 485 | 486 | label.text = titleForRow(row, forComponent: component) 487 | return label 488 | } 489 | 490 | // Helper 491 | fileprivate func titleForRow(_ row: Int, forComponent component: Int) -> String? { 492 | switch pickerStyle { 493 | case .single: 494 | return singleColData?[row] 495 | case .multiple: 496 | return multipleColsData?[component][row] 497 | case .multipleAssociated: 498 | 499 | if let multipleAssociatedData = multipleAssociatedColsData { 500 | 501 | if component == 0 { 502 | return multipleAssociatedData[0][row].first!.0 503 | }else { 504 | let associatedDataModels = multipleAssociatedData[component - 1] 505 | var arr: [String]? 506 | 507 | for associatedDataModel in associatedDataModels { 508 | if associatedDataModel.first!.0 == selectedValues[component - 1] { 509 | arr = associatedDataModel.first!.1 510 | } 511 | } 512 | if arr?.count == 0 {// 空数组 513 | return nil 514 | } 515 | return arr?[row] 516 | 517 | } 518 | 519 | } 520 | return nil 521 | default: return nil 522 | 523 | } 524 | 525 | } 526 | 527 | } 528 | 529 | extension PickerView { 530 | /// 单列 531 | public class func singleColPicker(_ toolBarTitle: String, 532 | singleColData: [String], 533 | defaultIndex: Int?, 534 | cancelAction: ButtonAction?, 535 | completeAction: SingleCompleteAction?) -> PickerView { 536 | let pic = PickerView(pickerStyle: .single) 537 | 538 | pic.toolBarTitle = toolBarTitle 539 | pic.singleColData = singleColData 540 | pic.defalultSelectedIndex = defaultIndex 541 | 542 | pic.singleCompleteOnClick = completeAction 543 | pic.cancelAction = cancelAction 544 | return pic 545 | 546 | } 547 | 548 | /// 多列不关联 549 | public class func multipleCosPicker(_ toolBarTitle: String, 550 | multipleColsData: [[String]], 551 | defaultSelectedIndexs: [Int]?, 552 | cancelAction: ButtonAction?, 553 | completeAction: MultipleCompleteAction?) -> PickerView { 554 | 555 | let pic = PickerView(pickerStyle: .multiple) 556 | 557 | pic.toolBarTitle = toolBarTitle 558 | pic.multipleColsData = multipleColsData 559 | pic.defalultSelectedIndexs = defaultSelectedIndexs 560 | pic.cancelAction = cancelAction 561 | pic.multipleCompleteOnClick = completeAction 562 | return pic 563 | 564 | } 565 | 566 | /// 多列关联 567 | public class func multipleAssociatedCosPicker(_ toolBarTitle: String, 568 | multipleAssociatedColsData: MultipleAssociatedDataType, 569 | defaultSelectedValues: [String]?, 570 | cancelAction: ButtonAction?, 571 | completeAction: MultipleCompleteAction?) -> PickerView { 572 | 573 | let pic = PickerView(pickerStyle: .multipleAssociated) 574 | 575 | pic.toolBarTitle = toolBarTitle 576 | pic.multipleAssociatedColsData = multipleAssociatedColsData 577 | pic.defaultSelectedValues = defaultSelectedValues 578 | pic.cancelAction = cancelAction 579 | pic.multipleCompleteOnClick = completeAction 580 | return pic 581 | 582 | } 583 | 584 | /// 城市选择器 585 | 586 | public class func citiesPicker(_ toolBarTitle: String, 587 | defaultSelectedValues: [String]?, 588 | selectTopLevel:Bool = false, 589 | cancelAction: ButtonAction?, 590 | completeAction: MultipleCompleteAction?) -> PickerView { 591 | 592 | let provincePath = Bundle.main.path(forResource: "Province", ofType: "plist") 593 | let cityPath = Bundle.main.path(forResource: "City", ofType: "plist") 594 | let areaPath = Bundle.main.path(forResource: "Area", ofType: "plist") 595 | // 这里需要使用数组, 因为字典无序, 如果只使用 cityArr,areaArr, 那么显示将是无序的, 不能按照plist中的数组显示 596 | let proviceArr = NSArray(contentsOfFile: provincePath!) 597 | let cityArr = NSDictionary(contentsOfFile: cityPath!) 598 | let areaArr = NSDictionary(contentsOfFile: areaPath!) 599 | 600 | var citiesModelArr: [[String: [String]?]] = [] 601 | var areasModelArr: [[String: [String]?]] = [] 602 | 603 | proviceArr?.forEach({ (element) in 604 | if let provinceStr = element as? String { 605 | 606 | var cities = cityArr?[provinceStr] as? [String] 607 | if selectTopLevel { 608 | cities?.insert("/", at: 0) 609 | } 610 | citiesModelArr.append([provinceStr: cities]) 611 | 612 | cities?.forEach({ (city) in 613 | if city == "/" { 614 | areasModelArr.append([city: ["/"]]) 615 | } 616 | else { 617 | var areas = areaArr?[city]as? [String] 618 | 619 | if selectTopLevel { 620 | areas!.insert("/", at: 0) 621 | } 622 | areasModelArr.append([city: areas]) 623 | } 624 | 625 | }) 626 | } 627 | }) 628 | 629 | let citiesArr = [citiesModelArr, areasModelArr] 630 | 631 | let pic = PickerView.multipleAssociatedCosPicker(toolBarTitle, 632 | multipleAssociatedColsData: citiesArr, 633 | defaultSelectedValues: defaultSelectedValues, 634 | cancelAction: cancelAction, 635 | completeAction: completeAction) 636 | return pic 637 | 638 | } 639 | 640 | /// 时间选择器 641 | public class func datePicker(_ toolBarTitle: String, 642 | datePickerSetting: DatePickerSetting, 643 | cancelAction: ButtonAction?, 644 | completeAction: DateCompleteAction?) -> PickerView { 645 | 646 | let pic = PickerView(pickerStyle: .date) 647 | pic.datePickerSetting = datePickerSetting 648 | pic.toolBarTitle = toolBarTitle 649 | pic.cancelAction = cancelAction 650 | pic.dateCompleteAction = completeAction 651 | return pic 652 | 653 | } 654 | 655 | } 656 | -------------------------------------------------------------------------------- /ConvenientPickerView/PickerView/Province.plist: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /ConvenientPickerView/PickerView/ToolBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToolBarView.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/20. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // github: https://github.com/AnRanScheme/ConvenientPickerView.git 8 | // 简书: 9 | 10 | import UIKit 11 | 12 | class ToolBarView: UIView { 13 | 14 | typealias CustomClosures = (_ titleLabel: UILabel, _ cancelButton: UIButton, _ completeButton: UIButton) -> () 15 | 16 | public typealias ButtonAction = () -> () 17 | 18 | public var completeAction: ButtonAction? 19 | 20 | public var cancelAction: ButtonAction? 21 | 22 | public var title = "请选择" { 23 | didSet { 24 | titleLabel.text = title 25 | } 26 | } 27 | 28 | /// 主题文本框 29 | private lazy var titleLabel: UILabel = { 30 | let label = UILabel() 31 | label.textAlignment = .center 32 | label.textColor = UIColor.black 33 | return label 34 | }() 35 | 36 | /// 取消按钮 37 | private lazy var cancelButton: UIButton = { 38 | let btn = UIButton() 39 | btn.setTitle("取消", for: .normal) 40 | btn.setTitleColor(UIColor.black, for: .normal) 41 | return btn 42 | }() 43 | 44 | /// 完成按钮 45 | private lazy var completeButton: UIButton = { 46 | let btn = UIButton() 47 | btn.setTitle("完成", for: .normal) 48 | btn.setTitleColor(UIColor.black, for: .normal) 49 | return btn 50 | }() 51 | 52 | /// 菜单栏 53 | private lazy var contentView: UIView = { 54 | let view = UIView() 55 | view.backgroundColor = UIColor.white 56 | return view 57 | }() 58 | 59 | override init(frame: CGRect) { 60 | super.init(frame: frame) 61 | commonInit() 62 | } 63 | 64 | required init?(coder aDecoder: NSCoder) { 65 | fatalError("init(coder:) has not been implemented") 66 | } 67 | 68 | private func commonInit() { 69 | backgroundColor = UIColor.lightText 70 | addSubview(contentView) 71 | contentView.addSubview(completeButton) 72 | contentView.addSubview(cancelButton) 73 | contentView.addSubview(titleLabel) 74 | completeButton.addTarget(self, 75 | action: #selector(completeButtonOnClick(sender:)), 76 | for: .touchUpInside) 77 | cancelButton.addTarget(self, 78 | action: #selector(cancelButtonOnClick(sender:)), 79 | for: .touchUpInside) 80 | } 81 | 82 | @objc func completeButtonOnClick(sender: UIButton) { 83 | completeAction?() 84 | } 85 | 86 | @objc func cancelButtonOnClick(sender: UIButton) { 87 | cancelAction?() 88 | } 89 | 90 | override func layoutSubviews() { 91 | super.layoutSubviews() 92 | let margin: CGFloat = 15.0 93 | let contentHeight = bounds.size.height - 2.0 94 | contentView.frame = CGRect(x: 0.0, 95 | y: 1.0, 96 | width: bounds.size.width, 97 | height: contentHeight) 98 | 99 | let btnWidth = contentHeight 100 | cancelButton.frame = CGRect(x: margin, 101 | y: 0.0, 102 | width: btnWidth, 103 | height: btnWidth) 104 | completeButton.frame = CGRect(x: bounds.size.width - btnWidth - margin, 105 | y: 0.0, 106 | width: btnWidth, 107 | height: btnWidth) 108 | 109 | let titleX = cancelButton.frame.maxX + margin 110 | let titleW = bounds.size.width - titleX - btnWidth - margin 111 | titleLabel.frame = CGRect(x: titleX, y: 0.0, width: titleW, height: btnWidth) 112 | 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /ConvenientPickerView/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ConvenientPickerView 4 | // 5 | // Created by 安然 on 17/3/20. 6 | // Copyright © 2017年 安然. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class ViewController: UIViewController { 13 | 14 | @IBOutlet weak var contentLabel: UILabel! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | /* 19 | var headquarters = Information(phoneNum: "123", address: "123 dizhi ") 20 | var ray = Person(name: "Ray", infomation: headquarters) 21 | print("ray------------\(ray.information.phoneNum)") 22 | var brain = Person(name: "brain", infomation: headquarters) 23 | print("brain------------\(brain.information.phoneNum)") 24 | 25 | brain.information.phoneNum = "789" 26 | print("ray.information.phoneNum------------\(ray.information.phoneNum)") 27 | print("brain.information.phoneNum------------\(brain.information.phoneNum)") 28 | */ 29 | let str = "如果你喜欢这个控件希望你给个星星还有,本控件大量的参考了 jasnig 大神的demo,哈哈哈我就是不要脸" 30 | let range = (str as NSString).range(of: "本控件大量的参考了 jasnig 大神的demo") 31 | let attributeString = NSMutableAttributedString(string:str) 32 | attributeString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.red,range: range) 33 | contentLabel.attributedText = attributeString 34 | 35 | } 36 | 37 | override func didReceiveMemoryWarning() { 38 | super.didReceiveMemoryWarning() 39 | } 40 | 41 | 42 | } 43 | 44 | /// 测试类 45 | class Information { 46 | 47 | var phoneNum: String 48 | 49 | var address: String 50 | 51 | init(phoneNum: String, address: String) { 52 | self.phoneNum = phoneNum 53 | self.address = address 54 | } 55 | } 56 | 57 | 58 | /// 测试类 59 | class Person { 60 | var name: String 61 | var information: Information 62 | 63 | init(name: String, infomation: Information) { 64 | self.name = name 65 | self.information = infomation 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ConvenientPickerView/anran.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnRanScheme/ConvenientPickerView/dcde8beb5f418c1b84045794c31627533b2fee97/ConvenientPickerView/anran.gif -------------------------------------------------------------------------------- /ConvenientPickerView/anran1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnRanScheme/ConvenientPickerView/dcde8beb5f418c1b84045794c31627533b2fee97/ConvenientPickerView/anran1.gif -------------------------------------------------------------------------------- /ConvenientPickerView/anran2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnRanScheme/ConvenientPickerView/dcde8beb5f418c1b84045794c31627533b2fee97/ConvenientPickerView/anran2.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 An Ran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ConvenientPickerView 2 | 一款方便的PickerView,通过简单的代码设置可以实现UIPickerView,无需代理设置,回收之后还会移除,可以设置多种类型的UIPickerView 3 | 4 | ![这是列子](https://github.com/AnRanScheme/ConvenientPickerView/raw/master/ConvenientPickerView/anran.gif) 5 | 6 | 7 | 1. TextField支持xib和代码生成使用, 只需调用一个方法, 设置选择的数据, 和默认选中的项(可选设置),可以设置是否在滚动的时候自动填充选中的值, 然后是在closure中处理点击完成的响应 8 | 9 | // 代码生成 10 | let test = PickerTextField(frame: CGRect(x: 20, y: timeTextField.frame.maxY, width: 340, height: 28)) 11 | test.borderStyle = .roundedRect 12 | test.placeholder = "代码初始化" 13 | test.showSingleColPicker("测试代码", data: singleData, defaultSelectedIndex: 0, autoSetSelectedText: true) { [unowned self] (textField, selectedIndex, selectedValue) in 14 | print(selectedValue) 15 | self.selectedDataLabel.text = selectedValue 16 | } 17 | view.addSubview(test) 18 | 19 | TextField的    例子 20 | ![这是列子](https://github.com/AnRanScheme/ConvenientPickerView/raw/master/ConvenientPickerView/anran2.gif) 21 | 22 | 2. 按钮(点击事件)中的使用, 只需要在相应的点击事件中使用UsefulPickerView的class方法即可, 这些方法和TextField的参数和使用完全相同, 多的一个效果就是点击背景会移除选择器 23 | 24 | ConvenientPickerView.showSingleColPicker("单列数据", 25 | data: singleData, 26 | defaultSelectedIndex: 2) { [unowned self] 27 | (selectIndex, selectValue) in 28 | self.selectedLabel.text = "选中了第\(selectIndex)行----选中的数据为\(selectValue)" 29 | } 30 | 31 | Buttond的    例子 32 | ![这是列子](https://github.com/AnRanScheme/ConvenientPickerView/raw/master/ConvenientPickerView/anran1.gif) 33 | 34 | 35 | 36 | 3. UsefulPickerView. 处理弹出和移除view 37 | 38 | 39 | fileprivate func showPicker() { 40 | // 通过window 弹出view 41 | let window = UIApplication.shared.keyWindow 42 | guard let currentWindow = window else { return } 43 | currentWindow.addSubview(self) 44 | 45 | currentWindow.addConstraints([pickerX, pickerY, pickerW, pickerH]) 46 | 47 | UIView.animate(withDuration: 0.25, animations: {[unowned self] in 48 | self.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.1) 49 | self.pickerView.frame = self.showFrame 50 | }, completion: nil) 51 | 52 | 53 | } 54 | 55 | 56 | func hidePicker() { 57 | // 把self从window中移除 58 | UIView.animate(withDuration: 0.25, animations: { [unowned self] in 59 | self.backgroundColor = UIColor.clear 60 | self.pickerView.frame = self.hideFrame 61 | 62 | }, completion: {[unowned self] (_) in 63 | self.removeFromSuperview() 64 | }) 65 | } 66 | 67 | 68 | 4. 如果你觉得还不错那就不要吝啬你的星星,当然本文章应用了大量大神的思路,好吧我就是练练 69 | 70 | 5. [更详细的介绍在简书中哦](http://www.jianshu.com/p/22f9904fd7f1) 71 | --------------------------------------------------------------------------------