├── Demo ├── .DS_Store └── JWDatePickerView │ ├── .DS_Store │ ├── JWDatePickerView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── zhujianwei.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── zhujianwei.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── JWDatePickerView.xcscheme │ │ └── xcschememanagement.plist │ └── JWDatePickerView │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── JWDatePickerView.xcdatamodeld │ ├── .xccurrentversion │ └── JWDatePickerView.xcdatamodel │ │ └── contents │ ├── JWDatePickerView │ ├── JWDatePickerView.h │ ├── JWDatePickerView.m │ ├── KBDatePickerViewkeyBoard.h │ └── KBDatePickerViewkeyBoard.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── JWDatePickerView-Swift ├── JWDatePickerKeyBoardView.swift ├── JWDatePickerView.swift └── JWPickerKeyBoardView.swift ├── JWDatePickerView ├── JWDatePickerView.h ├── JWDatePickerView.m ├── KBDatePickerViewkeyBoard.h └── KBDatePickerViewkeyBoard.m ├── README.md └── Source ├── .DS_Store └── PickerView.gif /Demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JWDatePickerView/dec25a1cc59e871d7b8ecbcf145e59036b3e7a0d/Demo/.DS_Store -------------------------------------------------------------------------------- /Demo/JWDatePickerView/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JWDatePickerView/dec25a1cc59e871d7b8ecbcf145e59036b3e7a0d/Demo/JWDatePickerView/.DS_Store -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5BA28ADF1D3DC2F300E4AFEA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BA28ADE1D3DC2F300E4AFEA /* main.m */; }; 11 | 5BA28AE21D3DC2F300E4AFEA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BA28AE11D3DC2F300E4AFEA /* AppDelegate.m */; }; 12 | 5BA28AE51D3DC2F300E4AFEA /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BA28AE41D3DC2F300E4AFEA /* ViewController.m */; }; 13 | 5BA28AE81D3DC2F300E4AFEA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BA28AE61D3DC2F300E4AFEA /* Main.storyboard */; }; 14 | 5BA28AEB1D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 5BA28AE91D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodeld */; }; 15 | 5BA28AED1D3DC2F300E4AFEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5BA28AEC1D3DC2F300E4AFEA /* Assets.xcassets */; }; 16 | 5BA28AF01D3DC2F300E4AFEA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BA28AEE1D3DC2F300E4AFEA /* LaunchScreen.storyboard */; }; 17 | 5BA28AFA1D3DC32A00E4AFEA /* JWDatePickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BA28AF91D3DC32A00E4AFEA /* JWDatePickerView.m */; }; 18 | 5BA28AFD1D3E10EE00E4AFEA /* KBDatePickerViewkeyBoard.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BA28AFC1D3E10EE00E4AFEA /* KBDatePickerViewkeyBoard.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 5BA28ADA1D3DC2F300E4AFEA /* JWDatePickerView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JWDatePickerView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 5BA28ADE1D3DC2F300E4AFEA /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 24 | 5BA28AE01D3DC2F300E4AFEA /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 25 | 5BA28AE11D3DC2F300E4AFEA /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 26 | 5BA28AE31D3DC2F300E4AFEA /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 27 | 5BA28AE41D3DC2F300E4AFEA /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 28 | 5BA28AE71D3DC2F300E4AFEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 29 | 5BA28AEA1D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = JWDatePickerView.xcdatamodel; sourceTree = ""; }; 30 | 5BA28AEC1D3DC2F300E4AFEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 5BA28AEF1D3DC2F300E4AFEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 5BA28AF11D3DC2F300E4AFEA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 5BA28AF81D3DC32A00E4AFEA /* JWDatePickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JWDatePickerView.h; sourceTree = ""; }; 34 | 5BA28AF91D3DC32A00E4AFEA /* JWDatePickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JWDatePickerView.m; sourceTree = ""; }; 35 | 5BA28AFB1D3E10ED00E4AFEA /* KBDatePickerViewkeyBoard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KBDatePickerViewkeyBoard.h; sourceTree = ""; }; 36 | 5BA28AFC1D3E10EE00E4AFEA /* KBDatePickerViewkeyBoard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KBDatePickerViewkeyBoard.m; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 5BA28AD71D3DC2F300E4AFEA /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 5BA28AD11D3DC2F300E4AFEA = { 51 | isa = PBXGroup; 52 | children = ( 53 | 5BA28ADC1D3DC2F300E4AFEA /* JWDatePickerView */, 54 | 5BA28ADB1D3DC2F300E4AFEA /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 5BA28ADB1D3DC2F300E4AFEA /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 5BA28ADA1D3DC2F300E4AFEA /* JWDatePickerView.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 5BA28ADC1D3DC2F300E4AFEA /* JWDatePickerView */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 5BA28AF71D3DC2FA00E4AFEA /* JWDatePickerView */, 70 | 5BA28AE01D3DC2F300E4AFEA /* AppDelegate.h */, 71 | 5BA28AE11D3DC2F300E4AFEA /* AppDelegate.m */, 72 | 5BA28AE31D3DC2F300E4AFEA /* ViewController.h */, 73 | 5BA28AE41D3DC2F300E4AFEA /* ViewController.m */, 74 | 5BA28AE61D3DC2F300E4AFEA /* Main.storyboard */, 75 | 5BA28AEC1D3DC2F300E4AFEA /* Assets.xcassets */, 76 | 5BA28AEE1D3DC2F300E4AFEA /* LaunchScreen.storyboard */, 77 | 5BA28AF11D3DC2F300E4AFEA /* Info.plist */, 78 | 5BA28AE91D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodeld */, 79 | 5BA28ADD1D3DC2F300E4AFEA /* Supporting Files */, 80 | ); 81 | path = JWDatePickerView; 82 | sourceTree = ""; 83 | }; 84 | 5BA28ADD1D3DC2F300E4AFEA /* Supporting Files */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 5BA28ADE1D3DC2F300E4AFEA /* main.m */, 88 | ); 89 | name = "Supporting Files"; 90 | sourceTree = ""; 91 | }; 92 | 5BA28AF71D3DC2FA00E4AFEA /* JWDatePickerView */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 5BA28AFB1D3E10ED00E4AFEA /* KBDatePickerViewkeyBoard.h */, 96 | 5BA28AFC1D3E10EE00E4AFEA /* KBDatePickerViewkeyBoard.m */, 97 | 5BA28AF81D3DC32A00E4AFEA /* JWDatePickerView.h */, 98 | 5BA28AF91D3DC32A00E4AFEA /* JWDatePickerView.m */, 99 | ); 100 | path = JWDatePickerView; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | 5BA28AD91D3DC2F300E4AFEA /* JWDatePickerView */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = 5BA28AF41D3DC2F300E4AFEA /* Build configuration list for PBXNativeTarget "JWDatePickerView" */; 109 | buildPhases = ( 110 | 5BA28AD61D3DC2F300E4AFEA /* Sources */, 111 | 5BA28AD71D3DC2F300E4AFEA /* Frameworks */, 112 | 5BA28AD81D3DC2F300E4AFEA /* Resources */, 113 | ); 114 | buildRules = ( 115 | ); 116 | dependencies = ( 117 | ); 118 | name = JWDatePickerView; 119 | productName = JWDatePickerView; 120 | productReference = 5BA28ADA1D3DC2F300E4AFEA /* JWDatePickerView.app */; 121 | productType = "com.apple.product-type.application"; 122 | }; 123 | /* End PBXNativeTarget section */ 124 | 125 | /* Begin PBXProject section */ 126 | 5BA28AD21D3DC2F300E4AFEA /* Project object */ = { 127 | isa = PBXProject; 128 | attributes = { 129 | LastUpgradeCheck = 0730; 130 | ORGANIZATIONNAME = zhujianwei; 131 | TargetAttributes = { 132 | 5BA28AD91D3DC2F300E4AFEA = { 133 | CreatedOnToolsVersion = 7.3.1; 134 | DevelopmentTeam = 4575Y5U33P; 135 | }; 136 | }; 137 | }; 138 | buildConfigurationList = 5BA28AD51D3DC2F300E4AFEA /* Build configuration list for PBXProject "JWDatePickerView" */; 139 | compatibilityVersion = "Xcode 3.2"; 140 | developmentRegion = English; 141 | hasScannedForEncodings = 0; 142 | knownRegions = ( 143 | en, 144 | Base, 145 | ); 146 | mainGroup = 5BA28AD11D3DC2F300E4AFEA; 147 | productRefGroup = 5BA28ADB1D3DC2F300E4AFEA /* Products */; 148 | projectDirPath = ""; 149 | projectRoot = ""; 150 | targets = ( 151 | 5BA28AD91D3DC2F300E4AFEA /* JWDatePickerView */, 152 | ); 153 | }; 154 | /* End PBXProject section */ 155 | 156 | /* Begin PBXResourcesBuildPhase section */ 157 | 5BA28AD81D3DC2F300E4AFEA /* Resources */ = { 158 | isa = PBXResourcesBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | 5BA28AF01D3DC2F300E4AFEA /* LaunchScreen.storyboard in Resources */, 162 | 5BA28AED1D3DC2F300E4AFEA /* Assets.xcassets in Resources */, 163 | 5BA28AE81D3DC2F300E4AFEA /* Main.storyboard in Resources */, 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXResourcesBuildPhase section */ 168 | 169 | /* Begin PBXSourcesBuildPhase section */ 170 | 5BA28AD61D3DC2F300E4AFEA /* Sources */ = { 171 | isa = PBXSourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | 5BA28AE51D3DC2F300E4AFEA /* ViewController.m in Sources */, 175 | 5BA28AFA1D3DC32A00E4AFEA /* JWDatePickerView.m in Sources */, 176 | 5BA28AEB1D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodeld in Sources */, 177 | 5BA28AE21D3DC2F300E4AFEA /* AppDelegate.m in Sources */, 178 | 5BA28AFD1D3E10EE00E4AFEA /* KBDatePickerViewkeyBoard.m in Sources */, 179 | 5BA28ADF1D3DC2F300E4AFEA /* main.m in Sources */, 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXSourcesBuildPhase section */ 184 | 185 | /* Begin PBXVariantGroup section */ 186 | 5BA28AE61D3DC2F300E4AFEA /* Main.storyboard */ = { 187 | isa = PBXVariantGroup; 188 | children = ( 189 | 5BA28AE71D3DC2F300E4AFEA /* Base */, 190 | ); 191 | name = Main.storyboard; 192 | sourceTree = ""; 193 | }; 194 | 5BA28AEE1D3DC2F300E4AFEA /* LaunchScreen.storyboard */ = { 195 | isa = PBXVariantGroup; 196 | children = ( 197 | 5BA28AEF1D3DC2F300E4AFEA /* Base */, 198 | ); 199 | name = LaunchScreen.storyboard; 200 | sourceTree = ""; 201 | }; 202 | /* End PBXVariantGroup section */ 203 | 204 | /* Begin XCBuildConfiguration section */ 205 | 5BA28AF21D3DC2F300E4AFEA /* Debug */ = { 206 | isa = XCBuildConfiguration; 207 | buildSettings = { 208 | ALWAYS_SEARCH_USER_PATHS = NO; 209 | CLANG_ANALYZER_NONNULL = YES; 210 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 211 | CLANG_CXX_LIBRARY = "libc++"; 212 | CLANG_ENABLE_MODULES = YES; 213 | CLANG_ENABLE_OBJC_ARC = YES; 214 | CLANG_WARN_BOOL_CONVERSION = YES; 215 | CLANG_WARN_CONSTANT_CONVERSION = YES; 216 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 217 | CLANG_WARN_EMPTY_BODY = YES; 218 | CLANG_WARN_ENUM_CONVERSION = YES; 219 | CLANG_WARN_INT_CONVERSION = YES; 220 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 221 | CLANG_WARN_UNREACHABLE_CODE = YES; 222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 223 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 224 | COPY_PHASE_STRIP = NO; 225 | DEBUG_INFORMATION_FORMAT = dwarf; 226 | ENABLE_STRICT_OBJC_MSGSEND = YES; 227 | ENABLE_TESTABILITY = YES; 228 | GCC_C_LANGUAGE_STANDARD = gnu99; 229 | GCC_DYNAMIC_NO_PIC = NO; 230 | GCC_NO_COMMON_BLOCKS = YES; 231 | GCC_OPTIMIZATION_LEVEL = 0; 232 | GCC_PREPROCESSOR_DEFINITIONS = ( 233 | "DEBUG=1", 234 | "$(inherited)", 235 | ); 236 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 237 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 238 | GCC_WARN_UNDECLARED_SELECTOR = YES; 239 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 240 | GCC_WARN_UNUSED_FUNCTION = YES; 241 | GCC_WARN_UNUSED_VARIABLE = YES; 242 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 243 | MTL_ENABLE_DEBUG_INFO = YES; 244 | ONLY_ACTIVE_ARCH = YES; 245 | SDKROOT = iphoneos; 246 | TARGETED_DEVICE_FAMILY = "1,2"; 247 | }; 248 | name = Debug; 249 | }; 250 | 5BA28AF31D3DC2F300E4AFEA /* Release */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | ALWAYS_SEARCH_USER_PATHS = NO; 254 | CLANG_ANALYZER_NONNULL = YES; 255 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 256 | CLANG_CXX_LIBRARY = "libc++"; 257 | CLANG_ENABLE_MODULES = YES; 258 | CLANG_ENABLE_OBJC_ARC = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_CONSTANT_CONVERSION = YES; 261 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 262 | CLANG_WARN_EMPTY_BODY = YES; 263 | CLANG_WARN_ENUM_CONVERSION = YES; 264 | CLANG_WARN_INT_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_UNREACHABLE_CODE = YES; 267 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 268 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 269 | COPY_PHASE_STRIP = NO; 270 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 271 | ENABLE_NS_ASSERTIONS = NO; 272 | ENABLE_STRICT_OBJC_MSGSEND = YES; 273 | GCC_C_LANGUAGE_STANDARD = gnu99; 274 | GCC_NO_COMMON_BLOCKS = YES; 275 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 276 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 277 | GCC_WARN_UNDECLARED_SELECTOR = YES; 278 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 279 | GCC_WARN_UNUSED_FUNCTION = YES; 280 | GCC_WARN_UNUSED_VARIABLE = YES; 281 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 282 | MTL_ENABLE_DEBUG_INFO = NO; 283 | SDKROOT = iphoneos; 284 | TARGETED_DEVICE_FAMILY = "1,2"; 285 | VALIDATE_PRODUCT = YES; 286 | }; 287 | name = Release; 288 | }; 289 | 5BA28AF51D3DC2F300E4AFEA /* Debug */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 293 | CODE_SIGN_IDENTITY = "iPhone Developer"; 294 | INFOPLIST_FILE = JWDatePickerView/Info.plist; 295 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 296 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 297 | PRODUCT_BUNDLE_IDENTIFIER = com.zjw.otherApp; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | }; 300 | name = Debug; 301 | }; 302 | 5BA28AF61D3DC2F300E4AFEA /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 306 | CODE_SIGN_IDENTITY = "iPhone Developer"; 307 | INFOPLIST_FILE = JWDatePickerView/Info.plist; 308 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 310 | PRODUCT_BUNDLE_IDENTIFIER = com.zjw.otherApp; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | }; 313 | name = Release; 314 | }; 315 | /* End XCBuildConfiguration section */ 316 | 317 | /* Begin XCConfigurationList section */ 318 | 5BA28AD51D3DC2F300E4AFEA /* Build configuration list for PBXProject "JWDatePickerView" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | 5BA28AF21D3DC2F300E4AFEA /* Debug */, 322 | 5BA28AF31D3DC2F300E4AFEA /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | 5BA28AF41D3DC2F300E4AFEA /* Build configuration list for PBXNativeTarget "JWDatePickerView" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | 5BA28AF51D3DC2F300E4AFEA /* Debug */, 331 | 5BA28AF61D3DC2F300E4AFEA /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | /* End XCConfigurationList section */ 337 | 338 | /* Begin XCVersionGroup section */ 339 | 5BA28AE91D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodeld */ = { 340 | isa = XCVersionGroup; 341 | children = ( 342 | 5BA28AEA1D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodel */, 343 | ); 344 | currentVersion = 5BA28AEA1D3DC2F300E4AFEA /* JWDatePickerView.xcdatamodel */; 345 | path = JWDatePickerView.xcdatamodeld; 346 | sourceTree = ""; 347 | versionGroupType = wrapper.xcdatamodel; 348 | }; 349 | /* End XCVersionGroup section */ 350 | }; 351 | rootObject = 5BA28AD21D3DC2F300E4AFEA /* Project object */; 352 | } 353 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView.xcodeproj/project.xcworkspace/xcuserdata/zhujianwei.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JWDatePickerView/dec25a1cc59e871d7b8ecbcf145e59036b3e7a0d/Demo/JWDatePickerView/JWDatePickerView.xcodeproj/project.xcworkspace/xcuserdata/zhujianwei.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView.xcodeproj/xcuserdata/zhujianwei.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 18 | 30 | 31 | 32 | 34 | 46 | 47 | 48 | 50 | 56 | 57 | 58 | 60 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView.xcodeproj/xcuserdata/zhujianwei.xcuserdatad/xcschemes/JWDatePickerView.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 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView.xcodeproj/xcuserdata/zhujianwei.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | JWDatePickerView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 5BA28AD91D3DC2F300E4AFEA 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | 16 | @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; 17 | @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; 18 | @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; 19 | 20 | - (void)saveContext; 21 | - (NSURL *)applicationDocumentsDirectory; 22 | 23 | 24 | @end 25 | 26 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | // Saves changes in the application's managed object context before the application terminates. 44 | [self saveContext]; 45 | } 46 | 47 | #pragma mark - Core Data stack 48 | 49 | @synthesize managedObjectContext = _managedObjectContext; 50 | @synthesize managedObjectModel = _managedObjectModel; 51 | @synthesize persistentStoreCoordinator = _persistentStoreCoordinator; 52 | 53 | - (NSURL *)applicationDocumentsDirectory { 54 | // The directory the application uses to store the Core Data store file. This code uses a directory named "com.zjw.JWDatePickerView" in the application's documents directory. 55 | return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; 56 | } 57 | 58 | - (NSManagedObjectModel *)managedObjectModel { 59 | // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model. 60 | if (_managedObjectModel != nil) { 61 | return _managedObjectModel; 62 | } 63 | NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"JWDatePickerView" withExtension:@"momd"]; 64 | _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; 65 | return _managedObjectModel; 66 | } 67 | 68 | - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { 69 | // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. 70 | if (_persistentStoreCoordinator != nil) { 71 | return _persistentStoreCoordinator; 72 | } 73 | 74 | // Create the coordinator and store 75 | 76 | _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 77 | NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"JWDatePickerView.sqlite"]; 78 | NSError *error = nil; 79 | NSString *failureReason = @"There was an error creating or loading the application's saved data."; 80 | if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { 81 | // Report any error we got. 82 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 83 | dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data"; 84 | dict[NSLocalizedFailureReasonErrorKey] = failureReason; 85 | dict[NSUnderlyingErrorKey] = error; 86 | error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict]; 87 | // Replace this with code to handle the error appropriately. 88 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 89 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 90 | abort(); 91 | } 92 | 93 | return _persistentStoreCoordinator; 94 | } 95 | 96 | 97 | - (NSManagedObjectContext *)managedObjectContext { 98 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) 99 | if (_managedObjectContext != nil) { 100 | return _managedObjectContext; 101 | } 102 | 103 | NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 104 | if (!coordinator) { 105 | return nil; 106 | } 107 | _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 108 | [_managedObjectContext setPersistentStoreCoordinator:coordinator]; 109 | return _managedObjectContext; 110 | } 111 | 112 | #pragma mark - Core Data Saving support 113 | 114 | - (void)saveContext { 115 | NSManagedObjectContext *managedObjectContext = self.managedObjectContext; 116 | if (managedObjectContext != nil) { 117 | NSError *error = nil; 118 | if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { 119 | // Replace this implementation with code to handle the error appropriately. 120 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 121 | NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 122 | abort(); 123 | } 124 | } 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/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 | } -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | 日历控件 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/JWDatePickerView.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | JWDatePickerView.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/JWDatePickerView.xcdatamodeld/JWDatePickerView.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/JWDatePickerView/JWDatePickerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerView.h 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | typedef NS_ENUM(NSUInteger,JWDatePickerMode) 13 | { 14 | JWDatePickerMode_DateAndTime,// 年月日 时 分 秒 4 15 | JWDatePickerMode_DateAndTimeRYear,// 月日 时 分 秒 4 16 | JWDatePickerMode_Time,// 时分秒 3 17 | JWDatePickerMode_TimeRSecond,//时分 2 18 | JWDatePickerMode_Date,// 年月日 3 19 | JWDatePickerMode_DateAddHour,// 年 月 日 时 4 20 | JWDatePickerMode_DateAndTimeRSecond,// 年月日 时 分 3 21 | JWDatePickerMode_DateAndTimeRYearAndSecond//月日 时 分 3 22 | }; 23 | 24 | @class JWDatePickerView; 25 | /** 26 | * 代理方法 27 | */ 28 | @protocol JWDatePickerViewDelegate 29 | 30 | /** 31 | * 返回单位文字 比如说 参数NSCalendarUnitYear 返回 年 例如 2016年 没有则返回@""或者nil 32 | */ 33 | -(NSString*)datePickerView:(JWDatePickerView*)datePickerView unitForCalendarUnit:(NSCalendarUnit)calendarUnit; 34 | 35 | @end 36 | 37 | @interface JWDatePickerView : UIView 38 | 39 | /** 40 | * delegate 41 | */ 42 | @property(nonatomic,weak)id delegate; 43 | 44 | /** 45 | * 最小日期 如果是 不显示年的模式下 自动将最大日期设置在 一年内 只显示时分秒的同理 46 | */ 47 | @property(nonatomic,strong)NSDate* minDate; 48 | 49 | /** 50 | * 当前日期 如果比最小日期小,设置最小日期为 date 51 | */ 52 | @property(nonatomic,strong)NSDate* date; 53 | 54 | /** 55 | * 最大日起 如果是 不显示年的模式下 自动将最小日期设置在 一年内 只显示时分秒的同理 56 | */ 57 | @property(nonatomic,strong)NSDate* maxDate; 58 | 59 | /** 60 | * pickerModel 61 | */ 62 | @property(nonatomic,assign)JWDatePickerMode pickerMode; 63 | 64 | 65 | /** 66 | * font 67 | */ 68 | @property(nonatomic,strong)UIFont* font; 69 | 70 | 71 | 72 | /** 73 | * minuteSpace 分钟间隔 条件 minuteSpace<30&&60%minuteSpace==0 74 | */ 75 | @property(nonatomic,assign)NSUInteger minuteSpace; 76 | 77 | /** 78 | * secondSpace 秒间隔 条件 secondSpace<30&&60%secondSpace==0 79 | */ 80 | @property(nonatomic,assign)NSUInteger secondSpace; 81 | 82 | /** 83 | * 刷新数据 84 | */ 85 | -(void)reloadData; 86 | 87 | 88 | /** 89 | * 四舍五入 date 数据 90 | */ 91 | -(NSDate*)getRoundDateWithDate:(NSDate*)date; 92 | 93 | /** 94 | * 获取一天中的 最小时间00:00:00 最大时间 23:59:59 95 | */ 96 | -(NSDate*)getDayLastDateWithDate:(NSDate*)date max:(BOOL)isGetMax; 97 | 98 | /** 99 | * 获取日期字符串 100 | */ 101 | -(NSString*)getDateStr; 102 | /** 103 | * 获取日期字符串 104 | */ 105 | -(NSString*)getDateStrWithDateFormatterString:(NSString*)formatterStr; 106 | @end 107 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/JWDatePickerView/JWDatePickerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerView.m 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | #define KSplit @"(#拆#)"//用来拆分 9 | 10 | #define KTotalFormatter @"yyyy MM dd HH mm ss" 11 | #define KTotalFormatterStr @"%@ %@ %@ %@ %@ %@" 12 | 13 | #define KComUnit NSCalendarUnitYear|NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond 14 | 15 | 16 | #import "JWDatePickerView.h" 17 | 18 | @interface JWDatePickerView() 19 | /** 20 | * bgView 21 | */ 22 | @property(nonatomic,strong)UIView* bgView; 23 | /** 24 | * pickerView 25 | */ 26 | @property(nonatomic,strong)UIPickerView* pickerView; 27 | 28 | /** 29 | * modeDict 30 | */ 31 | @property(nonatomic,strong)NSMutableDictionary* modeDict; 32 | /** 33 | * formatterDict 34 | */ 35 | @property(nonatomic,strong)NSMutableDictionary* formatterDict; 36 | 37 | /** 38 | * unitdict 39 | */ 40 | @property(nonatomic,strong)NSMutableDictionary* unitStrDict; 41 | 42 | /** 43 | * calendar 44 | */ 45 | @property(nonatomic,strong)NSCalendar* calendar; 46 | 47 | /** 48 | * dateFormmater 49 | */ 50 | @property(nonatomic,strong)NSDateFormatter* dateF; 51 | 52 | 53 | /** 54 | * formmater 55 | */ 56 | @property(nonatomic,strong)NSDateFormatter* simpleDateF; 57 | 58 | /** 59 | * tempComps 60 | */ 61 | @property(nonatomic,strong)NSDateComponents *tempDateComps; 62 | 63 | 64 | @end 65 | 66 | @implementation JWDatePickerView 67 | 68 | /** 69 | * 初始化控件 70 | */ 71 | -(instancetype)initWithFrame:(CGRect)frame 72 | { 73 | if (self = [super initWithFrame:frame]) { 74 | self.minuteSpace = 1; 75 | self.secondSpace = 1; 76 | 77 | self.bgView = [[UIView alloc] init]; 78 | [self addSubview:self.bgView]; 79 | 80 | //模式 81 | self.pickerMode = JWDatePickerMode_DateAddHour; 82 | self.font = [UIFont boldSystemFontOfSize:16]; 83 | 84 | //pickerView 85 | self.pickerView = [[UIPickerView alloc] init]; 86 | self.pickerView.backgroundColor = [UIColor whiteColor]; 87 | self.pickerView.delegate = self; 88 | self.pickerView.dataSource = self; 89 | [self.bgView addSubview:self.pickerView]; 90 | 91 | } 92 | return self; 93 | } 94 | 95 | 96 | - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView 97 | { 98 | //返回对应模式下的compsCount 99 | // if (self.pickerMode==JWDatePickerMode_DateAndTime||self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAddHour) { 100 | // return 4; 101 | // }else if (self.pickerMode==JWDatePickerMode_TimeRSecond) 102 | // { 103 | // return 2; 104 | // } 105 | // return 3; 106 | return [self.modeDict objectForKey:@(self.pickerMode)].count; 107 | } 108 | 109 | 110 | - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component 111 | { 112 | NSArray* groupUnitArray = self.modeDict[@(self.pickerMode)]; 113 | if (component>=groupUnitArray.count) { 114 | return 0; 115 | } 116 | NSNumber* number = [groupUnitArray[component] lastObject]; 117 | 118 | switch (number.unsignedIntegerValue) { 119 | case NSCalendarUnitYear: 120 | { 121 | NSInteger year= [self.calendar component:number.unsignedIntegerValue fromDate:[self getMaxDate]]-[self.calendar component:number.unsignedIntegerValue fromDate:[self getMinDate]]+1; 122 | 123 | 124 | return year; 125 | break; 126 | } 127 | case NSCalendarUnitMonth: 128 | return 12; 129 | break; 130 | case NSCalendarUnitDay: 131 | { 132 | if (self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour) {//这个模式下 年 月 日 或者 年 月 日 时 133 | //根据年月日刷新 天数 134 | NSInteger days =[self getMonthDaysWithYear:self.tempDateComps.year andMoth:self.tempDateComps.month]; 135 | return days; 136 | }else 137 | { 138 | 139 | return [self.calendar components:number.unsignedIntegerValue fromDate:[self getMinDate] toDate:[self getMaxDate] options:NSCalendarMatchLast].day+1; 140 | } 141 | break; 142 | } 143 | case NSCalendarUnitHour: 144 | return 24; 145 | break; 146 | 147 | case NSCalendarUnitMinute: 148 | return 60/self.minuteSpace; 149 | break; 150 | 151 | case NSCalendarUnitSecond: 152 | return 60/self.secondSpace; 153 | break; 154 | default: 155 | break; 156 | } 157 | 158 | return 1; 159 | } 160 | 161 | 162 | 163 | -(NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component 164 | { 165 | 166 | 167 | NSArray* groupUnitArray = self.modeDict[@(self.pickerMode)]; 168 | 169 | if (component>=groupUnitArray.count) { 170 | return @""; 171 | } 172 | NSNumber* number = [groupUnitArray[component] lastObject]; 173 | 174 | switch (number.unsignedIntegerValue) { 175 | case NSCalendarUnitYear:// 年月日 模式下用 176 | { 177 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 178 | dateComps.year = row; 179 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 180 | 181 | NSArray*dateArray = [[self.dateF stringFromDate:date] componentsSeparatedByString:KSplit]; 182 | 183 | return dateArray[component]; 184 | break; 185 | } 186 | case NSCalendarUnitMonth://年 月 日 模式下用 187 | { 188 | return [NSString stringWithFormat:@"%02zd%@",row+1,self.unitStrDict[@(NSCalendarUnitMonth)]]; 189 | break; 190 | } 191 | case NSCalendarUnitDay: 192 | { 193 | if (self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour) {//这个模式下 单独选择日期 194 | return [NSString stringWithFormat:@"%02zd%@",row+1,self.unitStrDict[@(NSCalendarUnitDay)]]; 195 | }else 196 | { 197 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 198 | dateComps.day = row; 199 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 200 | 201 | NSArray*dateArray = [[self.dateF stringFromDate:date] componentsSeparatedByString:KSplit]; 202 | 203 | return dateArray[component]; 204 | } 205 | break; 206 | } 207 | case NSCalendarUnitHour: 208 | { 209 | return [NSString stringWithFormat:@"%02zd%@",row,self.unitStrDict[@(NSCalendarUnitHour)]]; 210 | break; 211 | } 212 | case NSCalendarUnitMinute: 213 | { 214 | return [NSString stringWithFormat:@"%02zd%@",self.minuteSpace*row,self.unitStrDict[@(NSCalendarUnitMinute)]]; 215 | break; 216 | } 217 | case NSCalendarUnitSecond: 218 | { 219 | return [NSString stringWithFormat:@"%02zd%@",self.secondSpace*row,self.unitStrDict[@(NSCalendarUnitSecond)]]; 220 | break; 221 | } 222 | default: 223 | { 224 | return @"return void"; 225 | break; 226 | } 227 | } 228 | 229 | } 230 | 231 | /** 232 | * 选中完日期之后 233 | */ 234 | -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ 235 | 236 | //用于拼接日期 237 | __block NSMutableString* m_str = [NSMutableString string]; 238 | 239 | 240 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//前面拼 年 241 | //同年可用 242 | NSInteger yearRow = [self.pickerView selectedRowInComponent:0]; 243 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 244 | dateComps.day = yearRow; 245 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 246 | NSDateComponents* comps = [self.calendar components:KComUnit fromDate:date]; 247 | 248 | [m_str appendFormat:@"%@%@",[NSString stringWithFormat:@"%02zd",comps.year],self.unitStrDict[@(NSCalendarUnitYear)]]; 249 | }else if (self.pickerMode == JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//前面拼接 年月日 250 | { 251 | //同年可用 252 | NSDateComponents* comps = [self.calendar components:KComUnit fromDate:[self getMinDate]]; 253 | //yyyy年MM月dd号 254 | [m_str appendFormat:@"%@%@%@%@%@%@%@",[NSString stringWithFormat:@"%02zd",comps.year],self.unitStrDict[@(NSCalendarUnitYear)],[NSString stringWithFormat:@"%02zd",comps.month],self.unitStrDict[@(NSCalendarUnitMonth)],[NSString stringWithFormat:@"%02zd",comps.day],self.unitStrDict[@(NSCalendarUnitDay)],KSplit]; 255 | } 256 | 257 | NSArray* groupArray = self.modeDict[@(self.pickerMode)]; 258 | 259 | 260 | [groupArray enumerateObjectsUsingBlock:^(NSArray* _Nonnull unitArray, NSUInteger idxOut, BOOL * _Nonnull stop) { 261 | //拼接拆分 262 | if (idxOut!=0) { 263 | [m_str appendString:KSplit]; 264 | } 265 | //选择项 266 | NSInteger row = [self.pickerView selectedRowInComponent:idxOut]; 267 | NSString *title = [self pickerView:pickerView titleForRow:row forComponent:idxOut]; 268 | 269 | if (self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour){ 270 | //记录 day 对应的component 271 | if ([unitArray.lastObject unsignedIntegerValue]==NSCalendarUnitYear) { 272 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 273 | dateComps.year = row; 274 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 275 | self.tempDateComps.year = [self.calendar components:KComUnit fromDate:date].year; 276 | 277 | }else if ([unitArray.lastObject unsignedIntegerValue]==NSCalendarUnitMonth) 278 | { 279 | 280 | self.tempDateComps.month = row+1; 281 | } 282 | else if ([unitArray.lastObject unsignedIntegerValue]==NSCalendarUnitDay) { 283 | //获取到当前row 和刷新后的rowCount 比较 284 | NSInteger newRowCount= [self pickerView:pickerView numberOfRowsInComponent:idxOut]; 285 | if (row>=newRowCount) { 286 | [self.pickerView selectRow:newRowCount-1 inComponent:idxOut animated:NO]; 287 | 288 | title = [NSString stringWithFormat:@"%02zd%@",newRowCount,self.unitStrDict[@(NSCalendarUnitDay)]]; 289 | } 290 | [self.pickerView reloadAllComponents]; 291 | } 292 | } 293 | [m_str appendString:title]; 294 | 295 | }]; 296 | 297 | 298 | NSDate* date = [self.simpleDateF dateFromString:m_str]; 299 | 300 | if (!date) { 301 | NSLog(@"Date is nil"); 302 | return; 303 | } 304 | 305 | if(date){[self checkWithSelectedDate:date];}else{NSLog(@"Date 为 nil");} 306 | 307 | 308 | } 309 | 310 | 311 | /** 312 | * 根据选择的时间 验证最大时间和最小时间 可以择调用回调 313 | */ 314 | -(void)checkWithSelectedDate:(NSDate*)date 315 | { 316 | if ([date compare:self.maxDate]==NSOrderedDescending){ 317 | _date = self.maxDate; 318 | [self scrollToCurrentDate:self.date animated:YES]; 319 | }else if ([date compare:self.minDate]==NSOrderedAscending) { 320 | _date = self.minDate; 321 | [self scrollToCurrentDate:self.date animated:YES]; 322 | }else 323 | { 324 | _date = date; 325 | } 326 | } 327 | 328 | 329 | /** 330 | * 四舍五入 date 数据 331 | */ 332 | -(NSDate*)getRoundDateWithDate:(NSDate*)date 333 | { 334 | NSDateComponents *com = [self.calendar components:KComUnit fromDate:date]; 335 | 336 | NSInteger day = com.day; 337 | NSInteger hour = (com.hour%KHourSpace?com.hour/KHourSpace+1:com.hour/KHourSpace)*KHourSpace; 338 | NSInteger minute = (com.minute%KMinuteSpace?com.minute/KMinuteSpace+1:com.minute/KMinuteSpace)*KMinuteSpace; 339 | 340 | 341 | NSDateComponents* dateAdd = [[NSDateComponents alloc] init]; 342 | dateAdd.minute = minute - com.minute; 343 | dateAdd.hour = hour - com.hour; 344 | dateAdd.day = day - com.day; 345 | 346 | 347 | return [self.calendar dateByAddingComponents:dateAdd toDate:date options:0]; 348 | } 349 | 350 | 351 | 352 | /** 353 | * 宽度 354 | */ 355 | - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component 356 | { 357 | NSInteger coms = [self numberOfComponentsInPickerView:self.pickerView]; 358 | CGFloat width = self.bounds.size.width-(coms-1)*10; 359 | 360 | NSString* formatterStr = [self getDateFormatterWithPickModel:self.pickerMode]; 361 | NSArray* array = [formatterStr componentsSeparatedByString:KSplit]; 362 | 363 | __block NSUInteger maxLength = 0; 364 | [array enumerateObjectsUsingBlock:^(NSString* _Nonnull str, NSUInteger idx, BOOL * _Nonnull stop) { 365 | maxLength+=str.length; 366 | }]; 367 | return width/maxLength*[array[component] length]; 368 | } 369 | 370 | /** 371 | * 高度 372 | */ 373 | - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component 374 | { 375 | return (self.bounds.size.height-20)/4; 376 | } 377 | 378 | 379 | 380 | 381 | -(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view 382 | { 383 | UILabel* label = nil; 384 | if (view&&[view isKindOfClass:[UILabel class]]) { 385 | label =(UILabel*)view; 386 | }else 387 | { 388 | label= [[UILabel alloc] init]; 389 | label.textAlignment =NSTextAlignmentCenter; 390 | label.font =self.font?self.font:[UIFont systemFontOfSize:16]; 391 | } 392 | label.text = [self pickerView:pickerView titleForRow:row forComponent:component]; 393 | return label; 394 | } 395 | 396 | 397 | 398 | /** 399 | * 布局 400 | */ 401 | -(void)layoutSubviews 402 | { 403 | [super layoutSubviews]; 404 | 405 | self.bgView.frame = self.bounds; 406 | 407 | CGFloat pickerX = 0; 408 | CGFloat pickerY = 10; 409 | CGFloat pickerW = self.bounds.size.width; 410 | CGFloat pickerH = self.bounds.size.height-20; 411 | self.pickerView.frame= CGRectMake(pickerX, pickerY, pickerW, pickerH); 412 | 413 | } 414 | 415 | 416 | 417 | /** 418 | * 设置dateFormmater 419 | */ 420 | -(NSString*)getDateFormatterWithPickModel:(JWDatePickerMode)mode 421 | { 422 | __block NSMutableString* m_str = [NSMutableString string]; 423 | 424 | NSArray* groupArray = self.modeDict[@(mode)]; 425 | 426 | 427 | [groupArray enumerateObjectsUsingBlock:^(NSArray* _Nonnull unitArray, NSUInteger idx, BOOL * _Nonnull stop) { 428 | //拼接拆分 429 | if (idx!=0) { 430 | [m_str appendString:KSplit]; 431 | } 432 | //遍历模式 433 | [unitArray enumerateObjectsUsingBlock:^(NSNumber* _Nonnull unitNumber, NSUInteger idx, BOOL * _Nonnull stop) { 434 | //拼接日历格式化字符串 435 | [m_str appendString:(self.formatterDict[unitNumber])]; 436 | 437 | //拼接单位 438 | if (self.delegate&&[self.delegate respondsToSelector:@selector(datePickerView:unitForCalendarUnit:)]) { 439 | NSString* unitStr = self.unitStrDict[unitNumber]; 440 | [m_str appendString:unitStr]; 441 | } 442 | }]; 443 | }]; 444 | return m_str; 445 | 446 | } 447 | 448 | 449 | 450 | /** 451 | * 滚动到指定的位置 452 | */ 453 | -(void)reloadDataNeedScrollTo:(BOOL)need 454 | { 455 | 456 | [self.pickerView reloadAllComponents]; 457 | 458 | 459 | [self.pickerView setNeedsLayout]; 460 | 461 | 462 | if([self getCurrentDate]&&need)//滑动到显示时间 463 | { 464 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 465 | [self scrollToCurrentDate:[self getCurrentDate] animated:YES]; 466 | }); 467 | } 468 | } 469 | 470 | /** 471 | * 滚动到指定的时间 472 | */ 473 | -(void)scrollToCurrentDate:(NSDate*)date animated:(BOOL)animated 474 | { 475 | 476 | NSArray* groupUnitArray = self.modeDict[@(self.pickerMode)]; 477 | 478 | [groupUnitArray enumerateObjectsUsingBlock:^(NSArray* _Nonnull unitArray, NSUInteger idx, BOOL * _Nonnull stop) { 479 | 480 | NSNumber *number = unitArray.lastObject; 481 | //获取的对应的组的rowCount 482 | NSInteger maxRowCount = [self pickerView:self.pickerView numberOfRowsInComponent:idx]; 483 | //分项处理 484 | switch (number.unsignedIntegerValue) { 485 | case NSCalendarUnitYear:// 年月日 模式下用 486 | { 487 | NSInteger year= [self.calendar component:number.unsignedIntegerValue fromDate:date]-[self.calendar component:number.unsignedIntegerValue fromDate:[self getMinDate]]; 488 | 489 | if (year*)modeDict 710 | { 711 | if (_modeDict==nil) { 712 | _modeDict = [NSMutableDictionary dictionary]; 713 | // JWDatePickerMode_DateAndTime,// 年月日 时分秒 714 | _modeDict[@(JWDatePickerMode_DateAndTime)]= @[@[@(NSCalendarUnitYear),@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)],@[@(NSCalendarUnitSecond)]]; 715 | 716 | // JWDatePickerMode_Time,// 时分秒 717 | _modeDict[@(JWDatePickerMode_Time)]= @[@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)],@[@(NSCalendarUnitSecond)]]; 718 | 719 | 720 | // JWDatePickerMode_TimeRSecond,// 时分 721 | _modeDict[@(JWDatePickerMode_TimeRSecond)]= @[@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)]]; 722 | 723 | // JWDatePickerMode_Date,// 年月日 724 | _modeDict[@(JWDatePickerMode_Date)]= @[@[@(NSCalendarUnitYear)],@[@(NSCalendarUnitMonth)],@[@(NSCalendarUnitDay)]]; 725 | 726 | // JWDatePickerMode_DateAndTimeRYear,// 月日 时 分 秒 727 | _modeDict[@(JWDatePickerMode_DateAndTimeRYear)]= @[@[@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)],@[@(NSCalendarUnitSecond)]]; 728 | 729 | // JWDatePickerMode_DateAddHour,// 年月日 时 分 730 | _modeDict[@(JWDatePickerMode_DateAddHour)]= @[@[@(NSCalendarUnitYear)],@[@(NSCalendarUnitMonth)],@[@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)]]; 731 | 732 | // JWDatePickerMode_DateAndTimeRSecond,// 年月日 时 分 733 | _modeDict[@(JWDatePickerMode_DateAndTimeRSecond)]= @[@[@(NSCalendarUnitYear),@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)]]; 734 | 735 | 736 | // JWDatePickerMode_DateAndTimeRYearAndSecond,//月日 时分 737 | _modeDict[@(JWDatePickerMode_DateAndTimeRYearAndSecond)]= @[@[@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)]]; 738 | 739 | } 740 | 741 | return _modeDict; 742 | } 743 | 744 | /** 745 | * 格式化字符串 746 | */ 747 | -(NSDateFormatter *)dateF 748 | { 749 | if (_dateF==nil) { 750 | _dateF = [[NSDateFormatter alloc] init]; 751 | } 752 | return _dateF; 753 | } 754 | 755 | -(NSMutableDictionary *)unitStrDict 756 | { 757 | if (_unitStrDict ==nil&&self.delegate) { 758 | _unitStrDict = [NSMutableDictionary dictionary]; 759 | 760 | [self.formatterDict enumerateKeysAndObjectsUsingBlock:^(NSNumber* _Nonnull unitNumber, id _Nonnull obj, BOOL * _Nonnull stop) { 761 | 762 | NSString* unitStr = @""; 763 | 764 | if (self.delegate&&[self.delegate respondsToSelector:@selector(datePickerView:unitForCalendarUnit:)]) { 765 | unitStr = [self.delegate datePickerView:self unitForCalendarUnit:unitNumber.unsignedIntegerValue]; 766 | //验证拆分 767 | if([unitStr rangeOfString:KSplit].location!=NSNotFound) 768 | { 769 | unitStr = @""; 770 | } 771 | } 772 | _unitStrDict[unitNumber] = unitStr; 773 | }]; 774 | 775 | } 776 | return _unitStrDict; 777 | } 778 | 779 | /** 780 | * 格式化 781 | */ 782 | -(NSMutableDictionary *)formatterDict 783 | { 784 | if (_formatterDict ==nil) { 785 | _formatterDict = [NSMutableDictionary dictionary]; 786 | _formatterDict[@(NSCalendarUnitYear)]= @"yyyy"; 787 | _formatterDict[@(NSCalendarUnitMonth)]= @"MM"; 788 | _formatterDict[@(NSCalendarUnitDay)]= @"dd"; 789 | _formatterDict[@(NSCalendarUnitHour)]= @"HH"; 790 | _formatterDict[@(NSCalendarUnitMinute)]= @"mm"; 791 | _formatterDict[@(NSCalendarUnitSecond)]= @"ss"; 792 | } 793 | return _formatterDict; 794 | } 795 | 796 | /** 797 | * 设置日期格式 798 | */ 799 | -(void)setDateFDateFormatter 800 | { 801 | //1.获取到当前模式下的 格式化字符串 802 | NSString *dateFormatterStr = [self getDateFormatterWithPickModel:self.pickerMode]; 803 | self.dateF.dateFormat = dateFormatterStr; 804 | 805 | //2.拼接 转换选中模式下的格式化字符串 806 | NSString* sourceFormatter = @""; 807 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//前面拼 年 808 | sourceFormatter = [NSString stringWithFormat:@"yyyy%@",self.unitStrDict[@(NSCalendarUnitYear)]]; 809 | }else if (self.pickerMode == JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//前面拼接 年月日 810 | { 811 | sourceFormatter =[NSString stringWithFormat:@"yyyy%@MM%@dd%@%@",self.unitStrDict[@(NSCalendarUnitYear)],self.unitStrDict[@(NSCalendarUnitMonth)],self.unitStrDict[@(NSCalendarUnitDay)],KSplit]; 812 | } 813 | 814 | //3.完整拼接 815 | NSString* lastFormatterStr = [NSString stringWithFormat:@"%@%@",sourceFormatter,dateFormatterStr]; 816 | 817 | //4.设置格式化字符串 818 | self.simpleDateF.dateFormat= lastFormatterStr; 819 | 820 | } 821 | 822 | 823 | 824 | /** 825 | * 获取最大时间 826 | */ 827 | -(NSDate*)getMinDate 828 | { 829 | if (self.minDate==nil) { 830 | 831 | if (self.maxDate) {//设置 最小日期的时候 832 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//同一年 833 | NSDateComponents *addComps = [[NSDateComponents alloc] init]; 834 | addComps.year = -1; 835 | addComps.day = 1; 836 | self.minDate = [self.calendar dateByAddingComponents:addComps toDate:self.maxDate options:NSCalendarMatchLast]; 837 | }else if (self.pickerMode==JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//同一天 838 | { 839 | self.minDate = [self getDayLastDateWithDate:self.maxDate max:NO]; 840 | } 841 | else{ 842 | NSDateComponents* comps = [[NSDateComponents alloc] init]; 843 | comps.year = -1; 844 | self.minDate = [self.calendar dateByAddingComponents:comps toDate:self.maxDate options:NSCalendarMatchLast]; 845 | } 846 | }else 847 | { 848 | self.minDate = [NSDate date]; 849 | } 850 | } 851 | return self.minDate; 852 | } 853 | 854 | /** 855 | * 获取最小时间 856 | */ 857 | -(NSDate*)getMaxDate 858 | { 859 | if (self.maxDate ==nil) { 860 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//同一年 861 | NSDateComponents *addComps = [[NSDateComponents alloc] init]; 862 | addComps.year = 1; 863 | addComps.day = -1; 864 | _maxDate = [self.calendar dateByAddingComponents:addComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 865 | }else if (self.pickerMode==JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//同一天 866 | { 867 | _maxDate = [self getDayLastDateWithDate:self.minDate max:YES]; 868 | } 869 | else{ 870 | NSDateComponents* comps = [[NSDateComponents alloc] init]; 871 | comps.year = 1; 872 | _maxDate = [self.calendar dateByAddingComponents:comps toDate:[self getMinDate] options:NSCalendarMatchLast]; 873 | } 874 | } 875 | return self.maxDate; 876 | } 877 | 878 | /** 879 | * 获取当前时间 880 | */ 881 | -(NSDate*)getCurrentDate 882 | { 883 | if (self.date ==nil) { 884 | self.date = [self getMinDate]; 885 | } 886 | return self.date; 887 | } 888 | 889 | 890 | 891 | /** 892 | * 切换模式时刷新 893 | */ 894 | -(void)setPickerMode:(JWDatePickerMode)pickerMode 895 | { 896 | 897 | _pickerMode = pickerMode; 898 | 899 | 900 | [self setDateFDateFormatter]; 901 | 902 | //年 月 日 单选 903 | if(self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour){ 904 | if (!self.date) { 905 | self.date = [self getMinDate]; 906 | } 907 | NSDateComponents *comps= [self.calendar components:KComUnit fromDate:self.date]; 908 | self.tempDateComps.year=comps.year; 909 | self.tempDateComps.month = comps.month; 910 | } 911 | 912 | if (self.minDate&&self.maxDate) { 913 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//同一年 914 | // 最小时间1年后的时间 compare 最大时间 915 | NSDateComponents *addComps = [[NSDateComponents alloc] init]; 916 | addComps.year = 1; 917 | addComps.day = -1; 918 | NSDate* lastMaxDate = [self.calendar dateByAddingComponents:addComps toDate:self.minDate options:NSCalendarMatchLast]; 919 | if ([lastMaxDate compare:self.maxDate]==NSOrderedAscending) { 920 | 921 | self.maxDate = lastMaxDate; 922 | [self.pickerView reloadAllComponents]; 923 | } 924 | }else if (self.pickerMode==JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//同一天 925 | { 926 | NSDate* lastMaxDate = [self getDayLastDateWithDate:self.minDate max:YES]; 927 | 928 | if ([lastMaxDate compare:self.maxDate]==NSOrderedAscending) { 929 | self.maxDate = lastMaxDate; 930 | [self.pickerView reloadAllComponents]; 931 | } 932 | } 933 | } 934 | 935 | [self reloadDataNeedScrollTo:YES]; 936 | 937 | } 938 | 939 | /** 940 | * 设置代理是刷新 941 | */ 942 | -(void)setDelegate:(id)delegate 943 | { 944 | if (delegate) { 945 | _delegate = delegate; 946 | 947 | [self setDateFDateFormatter]; 948 | 949 | [self reloadData]; 950 | } 951 | } 952 | 953 | /** 954 | * 用于转换 选择的时间 955 | */ 956 | -(NSDateFormatter *)simpleDateF 957 | { 958 | if (_simpleDateF==nil) { 959 | _simpleDateF = [[NSDateFormatter alloc] init]; 960 | } 961 | return _simpleDateF; 962 | } 963 | 964 | 965 | /** 966 | * 限制间隔 967 | */ 968 | -(void)setMinuteSpace:(NSUInteger)minuteSpace 969 | { 970 | if (minuteSpace<30&&60%minuteSpace==0) { 971 | _minuteSpace = minuteSpace; 972 | } 973 | 974 | } 975 | 976 | /** 977 | * 限制间隔 978 | */ 979 | -(void)setSecondSpace:(NSUInteger)secondSpace 980 | { 981 | if (secondSpace<30&&60%secondSpace==0) { 982 | _secondSpace = secondSpace; 983 | } 984 | } 985 | 986 | 987 | /** 988 | * 是否是闰年 989 | */ 990 | -(BOOL)isLeapYear:(NSInteger)year 991 | { 992 | if (year%4==0) { 993 | if (year%100==0) { 994 | if (year%400==0) { 995 | return YES; 996 | } 997 | //能被 4 100 整除 不能被400 整除的 不是闰年 998 | return NO; 999 | } 1000 | //能被4整除 不能被100整除的 是闰年 1001 | return YES; 1002 | } 1003 | //不能为4整除 不是闰年 1004 | return NO; 1005 | } 1006 | 1007 | /** 1008 | * 根据对应的年 和月 返回当月对应的天数 1009 | */ 1010 | -(NSInteger)getMonthDaysWithYear:(NSInteger)year andMoth:(NSInteger)moth 1011 | { 1012 | // 31 28 31 30 31 30 31 31 30 31 30 31 1013 | NSArray *daysOfMonth=@[@31,@28,@31,@30,@31,@30,@31,@31,@30,@31,@30,@31]; 1014 | 1015 | NSUInteger days=[daysOfMonth[moth-1] integerValue]; 1016 | 1017 | if (days==28) { 1018 | if ([self isLeapYear:year])days=29; 1019 | } 1020 | return days; 1021 | } 1022 | 1023 | -(NSDateComponents *)tempDateComps 1024 | { 1025 | if (_tempDateComps==nil) { 1026 | _tempDateComps = [[NSDateComponents alloc] init]; 1027 | } 1028 | return _tempDateComps; 1029 | } 1030 | 1031 | 1032 | -(NSString *)getDateStrWithDateFormatterString:(NSString *)formatterStr 1033 | { 1034 | if (self.date&&formatterStr&&formatterStr.length) { 1035 | 1036 | } 1037 | return @"格式化字符串不合法"; 1038 | } 1039 | 1040 | -(NSString *)getDateStr 1041 | { 1042 | if (self.date) { 1043 | return [[self.simpleDateF stringFromDate:self.date] stringByReplacingOccurrencesOfString:KSplit withString:@""]; 1044 | } 1045 | return @"NO Date"; 1046 | } 1047 | 1048 | /** 1049 | * 获取一天中的 最小时间 最大时间 1050 | */ 1051 | -(NSDate*)getDayLastDateWithDate:(NSDate*)date max:(BOOL)isGetMax 1052 | { 1053 | if (!date) { 1054 | return [NSDate date]; 1055 | } 1056 | 1057 | NSDateComponents*dateComps = [self.calendar components:KComUnit fromDate:date]; 1058 | NSInteger hour = dateComps.hour; 1059 | NSInteger minute = dateComps.minute; 1060 | NSInteger second = dateComps.second; 1061 | 1062 | NSDateComponents *newDateComp = [NSDateComponents new]; 1063 | if (isGetMax) { 1064 | newDateComp.hour = 24-hour-1; 1065 | newDateComp.minute = 60 - minute -1; 1066 | newDateComp.second = 60 - second -1; 1067 | }else 1068 | { 1069 | newDateComp.hour = -hour; 1070 | newDateComp.minute = -minute; 1071 | newDateComp.second = -second; 1072 | } 1073 | NSDate* lastDate = [self.calendar dateByAddingComponents:newDateComp toDate:date options:NSCalendarMatchLast]; 1074 | return lastDate; 1075 | } 1076 | @end 1077 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/JWDatePickerView/KBDatePickerViewkeyBoard.h: -------------------------------------------------------------------------------- 1 | // 2 | // KBSexkeyBoard.h 3 | // SunCarGuide 4 | // 5 | // Created by 朱建伟 on 15/12/17. 6 | // Copyright © 2015年 jryghq. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | #import "JWDatePickerView.h" 13 | 14 | @interface KBDatePickerViewkeyBoard : UIView 15 | 16 | /** 17 | * 快捷初始化 18 | */ 19 | -(instancetype)initWithFrame:(CGRect)frame title:(NSString*)title currentDayName:(NSString*)currentDayName; 20 | 21 | 22 | @property(nonatomic,strong,readonly)JWDatePickerView *pickerView; 23 | 24 | 25 | @property(nonatomic,copy)NSString *title; 26 | 27 | 28 | @property(nonatomic,copy)void(^didSelectedBlock)(NSDate *date,NSString* dateStr); 29 | 30 | 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/JWDatePickerView/KBDatePickerViewkeyBoard.m: -------------------------------------------------------------------------------- 1 | // 2 | // KBSexkeyBoard.m 3 | // SunCarGuide 4 | // 5 | // Created by 朱建伟 on 15/12/17. 6 | // Copyright © 2015年 jryghq. All rights reserved. 7 | // 8 | #import "KBDatePickerViewkeyBoard.h" 9 | @interface KBDatePickerViewkeyBoard() 10 | @property(nonatomic,strong)UIControl *cover; 11 | 12 | @property(nonatomic,strong)UIView *titleView; 13 | @property(nonatomic,strong)UILabel *titleLabel; 14 | @property(nonatomic,strong)UIButton *cancelBtn; 15 | @property(nonatomic,strong)UIButton *confirmBtn; 16 | 17 | 18 | @property(nonatomic,strong)NSString *currentTitle; 19 | 20 | @property(nonatomic,assign)BOOL isRemove; 21 | 22 | @property(nonatomic,assign)BOOL flag; 23 | 24 | 25 | @end 26 | 27 | @implementation KBDatePickerViewkeyBoard 28 | 29 | /** 30 | * 快捷初始化 31 | */ 32 | -(instancetype)initWithFrame:(CGRect)frame title:(NSString *)title currentDayName:(NSString *)currentDayName 33 | { 34 | if (self = [self initWithFrame:frame]) { 35 | self.title =title; 36 | self.titleLabel.text = title; 37 | self.currentTitle = currentDayName; 38 | } 39 | return self; 40 | } 41 | 42 | 43 | /** 44 | * 初始化 45 | */ 46 | -(instancetype)initWithFrame:(CGRect)frame 47 | { 48 | if(self = [super initWithFrame:frame]) 49 | { 50 | self.flag = YES; 51 | 52 | [self setClipsToBounds:NO]; 53 | 54 | _cover = [[UIControl alloc] init]; 55 | _cover.backgroundColor = [UIColor blackColor]; 56 | [_cover addTarget:self 57 | action:@selector(ExitKeyboard) forControlEvents:(UIControlEventTouchUpInside)]; 58 | _cover.alpha = 0.4; 59 | 60 | //标题View 61 | _titleView = [[UIView alloc] init]; 62 | [self addSubview:_titleView ]; 63 | 64 | //标题 65 | _titleLabel =[[UILabel alloc] init]; 66 | _titleLabel.text = @"请选择"; 67 | // CGFloat titleValue = 160; 68 | _titleLabel.textColor = [UIColor orangeColor];//[UIColor colorWithRed:(titleValue/255.0) green:(titleValue/255.0) blue:(titleValue/255.0) alpha:1]; 69 | _titleLabel.textAlignment = NSTextAlignmentCenter; 70 | _titleLabel.font = [UIFont systemFontOfSize:16]; 71 | [_titleView addSubview:_titleLabel]; 72 | 73 | //取消 74 | _cancelBtn = [[UIButton alloc] init]; 75 | [_cancelBtn setTitleColor:[UIColor orangeColor]forState:(UIControlStateNormal)]; 76 | [_cancelBtn setTitle:NSLocalizedString(@"取消",nil) forState:(UIControlStateNormal)]; 77 | _cancelBtn.titleLabel.font =[UIFont systemFontOfSize:16]; 78 | _cancelBtn.tag = 0; 79 | [_cancelBtn addTarget:self 80 | action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 81 | [_titleView addSubview:_cancelBtn]; 82 | 83 | //确定 84 | _confirmBtn = [[UIButton alloc] init]; 85 | [_confirmBtn setTitleColor:[UIColor orangeColor]forState:(UIControlStateNormal)]; 86 | [_confirmBtn setTitle:NSLocalizedString(@"确定",nil) forState:(UIControlStateNormal)]; 87 | _confirmBtn.titleLabel.font =[UIFont systemFontOfSize:16]; 88 | _confirmBtn.tag = 1; 89 | [_confirmBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 90 | [_titleView addSubview:_confirmBtn]; 91 | 92 | 93 | //pickerView 94 | _pickerView =[[JWDatePickerView alloc] init]; 95 | _pickerView.backgroundColor = [UIColor whiteColor]; 96 | [self addSubview:_pickerView]; 97 | 98 | } 99 | return self; 100 | } 101 | 102 | 103 | -(void)layoutSubviews 104 | { 105 | [super layoutSubviews]; 106 | 107 | CGFloat width = self.bounds.size.width; 108 | CGFloat height = 216; 109 | CGFloat margin = 10; 110 | 111 | CGFloat btnW = 80; 112 | 113 | CGFloat titleViewW = width; 114 | CGFloat titleViewH = 44; 115 | CGFloat titleViewX =0; 116 | CGFloat titleViewY =0; 117 | self.titleView.frame = CGRectMake(titleViewX, titleViewY, titleViewW, titleViewH); 118 | 119 | CGFloat cancelBtnW =btnW; 120 | CGFloat cancelBtnH = titleViewH; 121 | CGFloat cancelBtnX = margin; 122 | CGFloat cancelBtnY = 0; 123 | self.cancelBtn.frame =CGRectMake(cancelBtnX, cancelBtnY, cancelBtnW, cancelBtnH); 124 | 125 | 126 | CGFloat confirmBtnW = btnW; 127 | CGFloat confirmBtnH = titleViewH; 128 | CGFloat confirmBtnX = width - margin - confirmBtnW; 129 | CGFloat confirmBtnY = 0; 130 | self.confirmBtn.frame =CGRectMake(confirmBtnX, confirmBtnY, confirmBtnW, confirmBtnH); 131 | 132 | 133 | CGFloat titleLabelH = titleViewH; 134 | CGFloat titleLabelX = (cancelBtnX+cancelBtnW); 135 | CGFloat titleLabelW = confirmBtnX - titleLabelX; 136 | CGFloat titleLabelY = 0; 137 | self.titleLabel.frame = CGRectMake(titleLabelX, titleLabelY, titleLabelW, titleLabelH); 138 | 139 | 140 | CGFloat pickerViewX = 0; 141 | CGFloat pickerViewY = titleViewH; 142 | CGFloat pickerViewW = width; 143 | CGFloat pickerViewH = height - pickerViewY+margin; 144 | self.pickerView.frame = CGRectMake(pickerViewX, pickerViewY, pickerViewW, pickerViewH); 145 | 146 | } 147 | 148 | 149 | -(void)setTitle:(NSString *)title 150 | { 151 | _title = title; 152 | 153 | self.titleLabel.text = title; 154 | 155 | } 156 | 157 | 158 | /** 159 | * 此方法当 自定键盘移动到窗口上调用 160 | */ 161 | -(void)didMoveToWindow 162 | { 163 | [super didMoveToWindow]; 164 | if (!self.isRemove) { 165 | UIWindow *window = [UIApplication sharedApplication].keyWindow; 166 | self.cover.frame = CGRectMake(0,0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height); 167 | self.cover.alpha = 0.4; 168 | [window addSubview:self.cover]; 169 | }else 170 | { 171 | self.isRemove = NO; 172 | } 173 | } 174 | 175 | 176 | 177 | /** 178 | * 移除时 将蒙板一并移除 179 | */ 180 | -(void)removeFromSuperview 181 | { 182 | self.isRemove = YES; 183 | [self.cover removeFromSuperview]; 184 | [super removeFromSuperview]; 185 | } 186 | 187 | 188 | -(void)btnClick:(UIButton*)btn 189 | { 190 | if (btn.tag) {//确定 191 | if (self.didSelectedBlock&&self.pickerView.date) { 192 | self.didSelectedBlock(self.pickerView.date,[self.pickerView getDateStr]); 193 | } 194 | } 195 | 196 | self.cover.alpha = 0.021; 197 | 198 | [[UIApplication sharedApplication].keyWindow endEditing:YES]; 199 | } 200 | 201 | 202 | -(void)ExitKeyboard{ 203 | self.cover.alpha = 0.021; 204 | [[UIApplication sharedApplication].keyWindow endEditing:YES]; 205 | } 206 | 207 | @end 208 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/JWDatePickerView/JWDatePickerView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | #import "KBDatePickerViewkeyBoard.h" 9 | #import "JWDatePickerView.h" 10 | #import "ViewController.h" 11 | 12 | @interface ViewController () 13 | /** 14 | * pickerView 15 | */ 16 | @property(nonatomic,strong)JWDatePickerView* pickerView; 17 | 18 | /** 19 | * textField 20 | */ 21 | @property(nonatomic,strong)UITextField* txtField; 22 | 23 | /** 24 | * kb 25 | */ 26 | @property(nonatomic,strong)KBDatePickerViewkeyBoard* keyboard; 27 | @end 28 | 29 | @implementation ViewController 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | 34 | self.navigationItem.title = @"日期键盘"; 35 | 36 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"切换模式" style:(UIBarButtonItemStyleDone) target:self action:@selector(change)]; 37 | 38 | [self.view addSubview:self.pickerView]; 39 | 40 | [self.view addSubview:self.txtField]; 41 | } 42 | 43 | -(void)change 44 | { 45 | if(self.pickerView.pickerMode 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /JWDatePickerView-Swift/JWDatePickerKeyBoardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerKeyBoardView.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/10/17. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWDatePickerKeyBoardView: UIView { 12 | 13 | typealias ConfirmDateClosure = (Date) -> () 14 | 15 | var didConfirmDateClosure:ConfirmDateClosure? 16 | 17 | 18 | var isRemove:Bool = false 19 | 20 | var font:UIFont?{ 21 | willSet{ 22 | if let f = font { 23 | titleLabel?.font = f 24 | titleLabel?.text = "日期选择" 25 | cancelBtn?.titleLabel?.font = f 26 | confirmBtn?.titleLabel?.font = f 27 | } 28 | } 29 | } 30 | 31 | 32 | var title:String?{ 33 | willSet{ 34 | if let t = newValue{ 35 | titleLabel?.text = t 36 | } 37 | } 38 | } 39 | 40 | //蒙版 41 | private var cover:UIControl = { 42 | let view:UIControl = UIControl(frame: ScreenBounds) 43 | view.backgroundColor = UIColor.black 44 | view.alpha = 0.4 45 | 46 | return view 47 | }() 48 | 49 | //取消按钮 50 | private var cancelBtn:UIButton? 51 | 52 | //确定按钮 53 | private var confirmBtn:UIButton? 54 | 55 | //头部标题 56 | private var titleLabel:UILabel? 57 | 58 | //头部View 59 | private var titleView:UIView? 60 | 61 | //pickerView 62 | let pickerView:JWDatePickerView = JWDatePickerView() 63 | 64 | override init(frame: CGRect) { 65 | super.init(frame: frame) 66 | 67 | backgroundColor = UIColor.white 68 | 69 | cover.addTarget(self, action: #selector(JWDatePickerKeyBoardView.exitKeyBoard), for: UIControlEvents.touchUpInside) 70 | 71 | // FontPrompt_120_218_317_416_515_614_712_810_908 72 | font = Font4 73 | 74 | //标题View 75 | titleView = UIView() 76 | titleView?.backgroundColor = UIColor.orange 77 | addSubview(titleView!) 78 | 79 | //取消按钮 80 | cancelBtn = UIButton() 81 | cancelBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 82 | cancelBtn?.titleLabel?.font = font 83 | cancelBtn?.setTitle("取消", for: UIControlState.normal) 84 | cancelBtn?.setTitle("取消", for: UIControlState.selected) 85 | cancelBtn?.setTitleColor(UIColor.white, for: UIControlState.normal) 86 | cancelBtn?.tag = 0 87 | titleView?.addSubview(cancelBtn!) 88 | 89 | //确定按钮 90 | confirmBtn = UIButton() 91 | confirmBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 92 | confirmBtn?.titleLabel?.font = font 93 | confirmBtn?.setTitle("确定", for: UIControlState.normal) 94 | confirmBtn?.setTitle("确定", for: UIControlState.selected) 95 | confirmBtn?.setTitleColor(UIColor.white, for: UIControlState.normal) 96 | confirmBtn?.tag = 1 97 | titleView?.addSubview(confirmBtn!) 98 | 99 | //标题 100 | titleLabel = UILabel() 101 | titleLabel?.textAlignment = NSTextAlignment.center 102 | titleLabel?.font = font 103 | titleLabel?.text = title 104 | titleLabel?.textColor = UIColor.white 105 | titleView?.addSubview(titleLabel!) 106 | 107 | //pickerView 108 | addSubview(pickerView) 109 | } 110 | 111 | 112 | internal func btnClick(btn:UIButton) { 113 | if btn.tag == 1 { 114 | if let closure = didConfirmDateClosure{ 115 | if let date = pickerView.date{ 116 | closure(date) 117 | } 118 | } 119 | } 120 | 121 | cover.alpha = 0.021; 122 | UIApplication.shared.keyWindow?.endEditing(true) 123 | 124 | } 125 | 126 | override func layoutSubviews() { 127 | super.layoutSubviews() 128 | 129 | //titleView 130 | setH(256) 131 | 132 | let titleViewX:CGFloat = 0 133 | let titleViewY:CGFloat = 0 134 | let titleViewW:CGFloat = bounds.width 135 | let titleViewH:CGFloat = 50 136 | titleView?.frame = CGRect(x: titleViewX, y: titleViewY, width: titleViewW, height: titleViewH) 137 | 138 | let cancelX:CGFloat = 0 139 | let cancelY:CGFloat = 0 140 | let cancelW:CGFloat = 80 141 | let cancelH:CGFloat = titleViewH 142 | cancelBtn?.frame = CGRect(x: cancelX, y: cancelY, width: cancelW, height: cancelH) 143 | 144 | let confirmW:CGFloat = cancelW 145 | let confirmH:CGFloat = titleViewH 146 | let confirmX:CGFloat = bounds.width - confirmW 147 | let confirmY:CGFloat = 0 148 | confirmBtn?.frame = CGRect(x: confirmX, y: confirmY, width: confirmW, height: confirmH) 149 | 150 | let titleW:CGFloat = bounds.width - cancelW*2 151 | let titleH:CGFloat = titleViewH 152 | let titleX:CGFloat = (cancelBtn?.frame.maxX)! 153 | let titleY:CGFloat = 0 154 | titleLabel?.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH) 155 | 156 | 157 | let pickerX:CGFloat = 0 158 | let pickerY:CGFloat = titleViewH 159 | let pickerW:CGFloat = titleViewW 160 | let pickerH:CGFloat = bounds.size.height - titleViewH 161 | pickerView.frame = CGRect(x: pickerX, y: pickerY, width: pickerW, height: pickerH) 162 | 163 | } 164 | 165 | required init?(coder aDecoder: NSCoder) { 166 | fatalError("init(coder:) has not been implemented") 167 | } 168 | 169 | 170 | /** 171 | * 此方法当 自定键盘移动到窗口上调用 172 | */ 173 | override func didMoveToWindow() { 174 | super.didMoveToWindow() 175 | if isRemove == false { 176 | let window:UIWindow = UIApplication.shared.keyWindow! 177 | cover.frame = ScreenBounds 178 | cover.alpha = 0.4 179 | window.addSubview(cover) 180 | }else 181 | { 182 | isRemove = false 183 | } 184 | } 185 | 186 | 187 | /** 188 | * 移除时 将蒙板一并移除 189 | */ 190 | override func removeFromSuperview() { 191 | isRemove = true 192 | cover.removeFromSuperview() 193 | 194 | super.removeFromSuperview() 195 | } 196 | 197 | 198 | func exitKeyBoard() { 199 | cover.alpha = 0.021 200 | UIApplication.shared.keyWindow?.endEditing(true) 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /JWDatePickerView-Swift/JWDatePickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerView.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/10/13. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /* 12 | 13 | 使用方法 14 | pickerMode:JWDatePickerMode? 日期选择模式 15 | minDate:Date? 最小日期 16 | maxDate:Date? 最大日期 17 | date:Date? 当前日期 18 | enableLimited:Bool? 限制滚动区域模式 开启后最大最小日期至少有一个有值时有效 19 | unitStrClosure:((NSCalendar.Unit)-> String)? 获取单位的闭包 例如:年 月 日 时 分 秒 20 | didSelectedDateClosure:((Date)->())? 选中了日期 21 | */ 22 | 23 | /* 24 | 时间控件的模式 25 | */ 26 | public enum JWDatePickerMode:Int{ 27 | 28 | case dateAndTime// 年月日 时 分 秒 29 | case dateAndHour// 年月日 时 30 | case dateAndTimeRYear// 月日 时 分 秒 31 | case time// 时 分 秒 32 | case timeRSecond// 时 分 33 | case date// 年 月 日 34 | case dateAddHour// 年 月 日 时 35 | case dateAndTimeRSecond// 年月日 时 分 36 | case dateAndTimeRYearAndSecond// 月日 时 分 3 37 | case dateAndTimeForAllComponent// 年 月 日 时 分 秒 38 | } 39 | 40 | 41 | //Unit 42 | let UnitYear:NSCalendar.Unit = [.year] 43 | let UnitMonth:NSCalendar.Unit = [.month] 44 | let UnitDay:NSCalendar.Unit = [.day] 45 | let UnitHour:NSCalendar.Unit = [.hour] 46 | let UnitMinute:NSCalendar.Unit = [.minute] 47 | let UnitSecond:NSCalendar.Unit = [.second] 48 | let Option:NSCalendar.Options = NSCalendar.Options(rawValue: 0) 49 | 50 | 51 | class JWDatePickerView: UIView,UIPickerViewDelegate,UIPickerViewDataSource{ 52 | 53 | 54 | //MARK:公有方法 和 公有属性 55 | 56 | //获取单位 57 | var unitStrClosure:((NSCalendar.Unit)-> String)? 58 | 59 | //选中日期 60 | var didSelectedDateClosure:((Date)->())? 61 | 62 | //无限滚动的最大范围 63 | private let MaxRangeValue:Int = 88888 64 | 65 | //记录当日期选择模式中 无限滚动选项的当前日期 对应组选项索引 66 | private var currentIndex:Int? 67 | 68 | //是否正在滚动 69 | private var currentOperationComponent:Int = 0 70 | 71 | /** 72 | * 最小日期 73 | */ 74 | private var _minDate:Date? 75 | var minDate:Date?{ 76 | set{ 77 | 78 | if let minD = newValue{ 79 | _minDate = newValue 80 | //设置最大日期 如果最小日期比最大日期还要大 81 | if let tempMaxDate = maxDate{ 82 | //最小日期 比最大日期大 83 | if minD.compare(tempMaxDate) == ComparisonResult.orderedDescending{ 84 | //设置最大日期为最小日期 85 | _maxDate = minD 86 | }else{ 87 | //如果是需要限制在短时间内的 88 | let unitSubArray:[UInt] = (pickerModeDict[pickerMode!]?.first!)! 89 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{ 90 | let destinationDate = getDateAddOrReduceYear(date: minD, isAdd: true) 91 | 92 | //如果最大日期超过最小日期一年后的范围 93 | if tempMaxDate.compare(destinationDate) == ComparisonResult.orderedDescending{ 94 | _maxDate = destinationDate 95 | } 96 | } 97 | } 98 | }else{//最大日期没有值,如果模式需要限制在一年内 99 | let unitSubArray:[UInt] = (pickerModeDict[pickerMode!]?.first!)! 100 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{ 101 | 102 | //一年后的日期 103 | let destinationDate:Date = self.getDateAddOrReduceYear(date: minD, isAdd: true) 104 | //修改掉 105 | _maxDate = destinationDate 106 | } 107 | } 108 | 109 | //验证当前日期 110 | if let currentDate = self.date{ 111 | //判断当前日期是否在范围内 112 | 113 | //当前时间比最小时间 114 | if currentDate.compare(minD) == ComparisonResult.orderedAscending{ 115 | _date = minD 116 | } 117 | 118 | //判断最大日期的范围 119 | if let tempMaxDate = maxDate{ 120 | if currentDate.compare(tempMaxDate) == ComparisonResult.orderedDescending{//当前时间比最大时间大 121 | _date = tempMaxDate 122 | } 123 | } 124 | }else{ 125 | _date = minD 126 | } 127 | 128 | //刷新 129 | toCurentDate(animated: false) 130 | }else 131 | { 132 | _minDate = newValue 133 | 134 | //刷新 135 | toCurentDate(animated: false) 136 | } 137 | } 138 | get{ 139 | 140 | if _minDate == nil { 141 | 142 | let unitSubArray:[UInt] = (pickerModeDict[pickerMode!]?.first!)! 143 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{ 144 | 145 | //如果最大日期有值 146 | if let tempMaxDate = _maxDate{ 147 | let destinationDate = getDateAddOrReduceYear(date: tempMaxDate, isAdd: false) 148 | 149 | //设置时间 150 | self.minDate = destinationDate 151 | }else{ 152 | if let date = self.date{ 153 | //判断最小日期是否在一年的范围内 154 | var addComps:DateComponents = DateComponents() 155 | 156 | //闰年 157 | if self.isLeapYear(year: (self.currentDateCompnents?.year)!){ 158 | addComps.day = -((366-1)/2) 159 | }else{ 160 | addComps.day = -((365-1)/2) 161 | } 162 | 163 | //日期 164 | let lastDate:Date = calendar.date(byAdding: addComps, to:date, options: NSCalendar.Options.matchLast)! 165 | 166 | _minDate = lastDate 167 | } 168 | } 169 | } 170 | } 171 | 172 | return _minDate 173 | } 174 | } 175 | 176 | 177 | /** 178 | * 最大日期 179 | */ 180 | private var _maxDate:Date? 181 | var maxDate:Date? 182 | { 183 | set{ 184 | if let maxD = newValue{ 185 | _maxDate = maxD 186 | //设置最小日期 如果最大日期比最小日期还要小 187 | if let tempMinDate = minDate{ 188 | //最大日期 比最小日期小 189 | if maxD.compare(tempMinDate) == ComparisonResult.orderedAscending{ 190 | //设置最小日期为最大日期 191 | _minDate = maxD 192 | }else{ 193 | //如果是需要限制在短时间内的 194 | let unitSubArray:[UInt] = (pickerModeDict[pickerMode!]?.first!)! 195 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{ 196 | let destinationDate = getDateAddOrReduceYear(date: maxD, isAdd: false) 197 | 198 | //如果最小日期超过最大日期一年后的范围 199 | if tempMinDate.compare(destinationDate) == ComparisonResult.orderedAscending{ 200 | _minDate = destinationDate 201 | } 202 | } 203 | } 204 | }else{//最大日期没有值,如果模式需要限制在一年内 205 | let unitSubArray:[UInt] = (pickerModeDict[pickerMode!]?.first!)! 206 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{ 207 | 208 | //一年后的日期 209 | let destinationDate:Date = self.getDateAddOrReduceYear(date: maxD, isAdd: false) 210 | //修改掉 211 | _minDate = destinationDate 212 | } 213 | } 214 | 215 | //验证当前日期 216 | if let currentDate = self.date{ 217 | //判断当前日期是否在范围内 218 | 219 | if let tempMinDate = minDate{ 220 | //当前时间比最小时间 221 | if currentDate.compare(tempMinDate) == ComparisonResult.orderedAscending{ 222 | _date = tempMinDate 223 | } 224 | } 225 | 226 | //判断最大日期的范围 227 | if currentDate.compare(maxD) == ComparisonResult.orderedDescending{//当前时间比最大时间大 228 | _date = maxD 229 | } 230 | }else{ 231 | _date = maxD 232 | } 233 | 234 | 235 | //刷新 236 | toCurentDate(animated: false) 237 | }else 238 | { 239 | _maxDate = newValue 240 | 241 | //刷新 242 | toCurentDate(animated: false) 243 | } 244 | 245 | } 246 | get{ 247 | if _maxDate == nil { 248 | let unitSubArray:[UInt] = (pickerModeDict[pickerMode!]?.first!)! 249 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{ 250 | 251 | //如果最大日期有值 252 | if let tempMinDate = _minDate{ 253 | let destinationDate = getDateAddOrReduceYear(date: tempMinDate, isAdd: true) 254 | 255 | //设置时间 256 | self.maxDate = destinationDate 257 | }else{ 258 | if let date = self.date{ 259 | //判断最小日期是否在一年的范围内 260 | var addComps:DateComponents = DateComponents() 261 | 262 | //闰年 263 | if self.isLeapYear(year: (self.currentDateCompnents?.year)!){ 264 | addComps.day = (366-1)/2 265 | }else{ 266 | addComps.day = (365-1)/2 267 | } 268 | 269 | //日期 270 | let lastDate:Date = calendar.date(byAdding: addComps, to:date, options: NSCalendar.Options.matchLast)! 271 | 272 | _maxDate = lastDate 273 | } 274 | } 275 | 276 | 277 | } 278 | } 279 | 280 | return _maxDate 281 | } 282 | } 283 | 284 | /** 285 | * 当前日期 286 | */ 287 | private var _date:Date? 288 | var date:Date? 289 | { 290 | set{ 291 | if let d = newValue { 292 | if let tempMinD = minDate{ 293 | if d.compare(tempMinD) == ComparisonResult.orderedAscending{ 294 | print("指定日期不在范围内") 295 | return 296 | } 297 | } 298 | if let tempMaxD = maxDate 299 | { 300 | if d.compare(tempMaxD) == ComparisonResult.orderedDescending{ 301 | print("指定日期不在范围内") 302 | return 303 | } 304 | } 305 | 306 | 307 | //设置当前的comps 308 | currentDateCompnents = calendar.components(ComUnit, from: d) 309 | _date = d 310 | 311 | toCurentDate(animated: false) 312 | 313 | //刷新 314 | pickerView.reloadAllComponents() 315 | }else 316 | { 317 | _date = newValue 318 | 319 | pickerView .reloadAllComponents() 320 | } 321 | } 322 | get{ 323 | if let d = _date 324 | { 325 | return d 326 | }else 327 | { 328 | _date = Date() 329 | 330 | //设置当前的comps 331 | currentDateCompnents = calendar.components(ComUnit, from: _date!) 332 | 333 | return _date 334 | } 335 | } 336 | } 337 | 338 | 339 | //单位 340 | private var _unitDescDict:[UInt:String]! 341 | private var unitDescDict:[UInt:String]!{ 342 | set{ 343 | if let dict = newValue { 344 | _unitDescDict = dict 345 | } 346 | } 347 | get{ 348 | if _unitDescDict == nil { 349 | _unitDescDict = [UInt:String]() 350 | _unitDescDict[UnitYear.rawValue] = "年" 351 | _unitDescDict[UnitMonth.rawValue] = "月" 352 | _unitDescDict[UnitDay.rawValue] = "日" 353 | _unitDescDict[UnitHour.rawValue] = "时" 354 | _unitDescDict[UnitMinute.rawValue] = "分" 355 | _unitDescDict[UnitSecond.rawValue] = "秒" 356 | } 357 | 358 | return _unitDescDict 359 | } 360 | } 361 | 362 | /** 363 | * pickerModel 364 | */ 365 | private var _pickerMode:JWDatePickerMode? 366 | var pickerMode:JWDatePickerMode?{ 367 | get{ 368 | if _pickerMode == nil 369 | { 370 | _pickerMode = JWDatePickerMode.dateAndTimeForAllComponent 371 | 372 | } 373 | 374 | return _pickerMode 375 | } 376 | set{ 377 | if let mode = newValue { 378 | _pickerMode = mode 379 | 380 | pickerView.reloadAllComponents() 381 | 382 | //切换模式必须滚动到指定 383 | toCurentDate(animated: false) 384 | } 385 | } 386 | } 387 | 388 | //用来记录 当前选中的年月 389 | private var tempDateCompnents:DateComponents = DateComponents() 390 | 391 | 392 | //用来记录当前date 的 DateComponents 393 | private var _currentDateCompnents:DateComponents? 394 | var currentDateCompnents:DateComponents?{ 395 | set{ 396 | if let comps = newValue 397 | { 398 | _currentDateCompnents = comps 399 | }else 400 | { 401 | _currentDateCompnents = newValue//可能为nil 402 | } 403 | } 404 | get { 405 | 406 | //如果等于 nil 去获取 407 | if _currentDateCompnents == nil { 408 | //设置当前的comps 409 | _currentDateCompnents = calendar.components(ComUnit, from: date!) 410 | } 411 | return _currentDateCompnents 412 | } 413 | } 414 | 415 | //滚动到指定的日期 416 | func scrollToDate(date:Date?,animated:Bool) 417 | { 418 | 419 | if let d = date { 420 | //赋值私有属性 421 | _date = d 422 | 423 | //设置当前的comps 424 | currentDateCompnents = calendar.components(ComUnit, from: d) 425 | 426 | toCurentDate(animated: animated) 427 | } 428 | } 429 | 430 | 431 | /** 432 | * 是否是闰年 433 | */ 434 | func isLeapYear(year:Int) -> Bool { 435 | if (year%4==0) { 436 | if (year%100==0) { 437 | if (year%400==0) { 438 | return true 439 | } 440 | //能被 4 100 整除 不能被400 整除的 不是闰年 441 | return false 442 | } 443 | //能被4整除 不能被100整除的 是闰年 444 | return true 445 | } 446 | //不能为4整除 不是闰年 447 | return false 448 | 449 | } 450 | 451 | /** 452 | * 根据对应的年 和月 返回当月对应的天数 453 | */ 454 | func numberOfDaysInMonth(year:Int,month:Int) -> Int { 455 | // 31 28 31 30 31 30 31 31 30 31 30 31 456 | let daysOfMonth:[Int] = [31,28,31,30,31,30,31,31,30,31,30,31] 457 | 458 | let index:Int = (month - 1) 459 | var days:Int = daysOfMonth[index] 460 | 461 | if (days == 28) { 462 | if (isLeapYear(year: year)){ 463 | days = 29 464 | } 465 | } 466 | return days 467 | 468 | } 469 | 470 | /** 471 | * 获取一天中的 最小时间 最大时间 472 | */ 473 | func dayFirstOrLastDate(date:Date,isLast:Bool) -> Date { 474 | let dateComps:DateComponents = calendar.components(ComUnit, from: date) 475 | let hour:Int = dateComps.hour! 476 | let minute:Int = dateComps.minute! 477 | let second:Int = dateComps.second! 478 | 479 | var newDateComps:DateComponents = DateComponents() 480 | if (isLast) { 481 | newDateComps.hour = 24 - hour - 1; 482 | newDateComps.minute = 60 - minute - 1 483 | newDateComps.second = 60 - second - 1 484 | }else 485 | { 486 | newDateComps.hour = -hour; 487 | newDateComps.minute = -minute; 488 | newDateComps.second = -second; 489 | } 490 | 491 | let d:Date = calendar.date(byAdding: newDateComps, to: date, options: NSCalendar.Options.matchLast)! 492 | return d 493 | 494 | } 495 | 496 | 497 | 498 | //MARK:私有属性 499 | 500 | //开启后 如果 最大日期 或者 最小日期有值 则单向或者双向有限制 否则无效 默认不开启限制 501 | private var _enableLimited:Bool? 502 | var enableLimited:Bool?{ 503 | set{ 504 | if let enable = newValue{ 505 | _enableLimited = enable 506 | 507 | pickerView.reloadAllComponents() 508 | 509 | //刷新时间 510 | toCurentDate(animated: true) 511 | } 512 | } 513 | get{ 514 | if let enable = _enableLimited{ 515 | return enable 516 | }else 517 | { 518 | return false 519 | } 520 | } 521 | } 522 | 523 | 524 | //comUnit 525 | private let ComUnit:NSCalendar.Unit = [.year,.day,.month,.hour,.minute,.second] 526 | 527 | //记录 当前日期选中 528 | private var sourceCompnentRowDict:[Int:Int] = [Int:Int]() 529 | 530 | //Calendar 531 | private let calendar:NSCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!//NSCalendar(identifier: Calendar.Identifier.gregorian) 532 | 533 | //MARK:控件 534 | 535 | /** 536 | * pickerView 537 | */ 538 | private var pickerView:UIPickerView = UIPickerView() 539 | 540 | 541 | //日期选择控件模式 542 | private var pickerModeDict:[JWDatePickerMode:[[UInt]]] = { 543 | var pickerMode:[JWDatePickerMode:[[UInt]]] = [JWDatePickerMode:[[UInt]]]() 544 | 545 | // 年月日 时 分 秒 546 | pickerMode[.dateAndTime] = [[UnitYear.rawValue,UnitMonth.rawValue,UnitDay.rawValue],[UnitHour.rawValue],[UnitMinute.rawValue],[UnitSecond.rawValue]] 547 | 548 | // 月日 时 分 秒 549 | pickerMode[.dateAndTimeRYear] = [[UnitMonth.rawValue,UnitDay.rawValue],[UnitHour.rawValue],[UnitMinute.rawValue],[UnitSecond.rawValue]] 550 | 551 | // 年月日 时 552 | pickerMode[.dateAndHour] = [[UnitYear.rawValue,UnitMonth.rawValue,UnitDay.rawValue],[UnitHour.rawValue]] 553 | 554 | 555 | // 时分秒 556 | pickerMode[.time] = [[UnitHour.rawValue],[UnitMinute.rawValue],[UnitSecond.rawValue]] 557 | 558 | //时分 559 | pickerMode[.timeRSecond] = [[UnitHour.rawValue],[UnitMinute.rawValue]] 560 | 561 | // 年月日 562 | pickerMode[.date] = [[UnitYear.rawValue],[UnitMonth.rawValue],[UnitDay.rawValue]] 563 | 564 | // 年 月 日 时 565 | pickerMode[.dateAddHour] = [[UnitYear.rawValue,UnitMonth.rawValue,UnitDay.rawValue],[UnitHour.rawValue]] 566 | 567 | // 年月日 时 分 568 | pickerMode[.dateAndTimeRSecond] = [[UnitYear.rawValue,UnitMonth.rawValue,UnitDay.rawValue],[UnitHour.rawValue],[UnitMinute.rawValue]] 569 | 570 | //月日 时 分 571 | pickerMode[.dateAndTimeRYearAndSecond] = [[UnitMonth.rawValue,UnitDay.rawValue],[UnitHour.rawValue],[UnitMinute.rawValue],[UnitSecond.rawValue]] 572 | 573 | //年 月 日 时 分 秒 574 | pickerMode[.dateAndTimeForAllComponent] = [[UnitYear.rawValue],[UnitMonth.rawValue],[UnitDay.rawValue],[UnitHour.rawValue],[UnitMinute.rawValue],[UnitSecond.rawValue]] 575 | 576 | return pickerMode 577 | }() 578 | 579 | //初始化 580 | override init(frame: CGRect) { 581 | super.init(frame: frame) 582 | 583 | addSubview(pickerView) 584 | 585 | currentIndex = MaxRangeValue/2 586 | 587 | pickerView.dataSource = self 588 | 589 | //跳到当前日期 590 | toCurentDate(animated: false) 591 | 592 | pickerView.delegate = self 593 | 594 | } 595 | 596 | required init?(coder aDecoder: NSCoder) { 597 | fatalError("init(coder:) has not been implemented") 598 | } 599 | 600 | 601 | //MARK:pickerView代理和数据源 602 | internal func numberOfComponents(in pickerView: UIPickerView) -> Int { 603 | 604 | //1.获取当前模式 605 | let unitArray:[[UInt]] = pickerModeDict[pickerMode!]! 606 | 607 | //2.返回该模式下的components 608 | return unitArray.count 609 | } 610 | 611 | 612 | internal func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 613 | //1.获取当前模式 614 | let unitArray:[[UInt]] = pickerModeDict[pickerMode!]! 615 | //2.判断类型 616 | 617 | var rows = 1 618 | 619 | let unitSubArray:[UInt] = unitArray[component] 620 | 621 | let unit:UInt = unitSubArray.last! 622 | 623 | //3.分类型处理 624 | switch unit { 625 | case UnitYear.rawValue: 626 | //判断限制模式 627 | if limitedEnable() { 628 | //开启了限制返回数据源 629 | rows = numberOfRowsInComponetForLimited(component: component,unit: unit) 630 | }else 631 | { 632 | rows = MaxRangeValue 633 | } 634 | break 635 | case UnitMonth.rawValue: 636 | //判断限制模式 637 | if limitedEnable() { 638 | //开启了限制返回数据源 639 | rows = numberOfRowsInComponetForLimited(component: component,unit: unit) 640 | }else{ 641 | //默认12 642 | rows = 12 643 | //第一组的最后一个是 month 644 | if component == 0 { 645 | rows = MaxRangeValue 646 | } 647 | } 648 | break 649 | case UnitDay.rawValue: 650 | //判断限制模式 651 | if limitedEnable() { 652 | //开启了限制返回数据源 653 | rows = numberOfRowsInComponetForLimited(component: component,unit: unit) 654 | }else{ 655 | //第一组的最后一个是 days 656 | if component == 0 { 657 | rows = MaxRangeValue 658 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{//如果第一个是月份 659 | if isLeapYear(year: (currentDateCompnents?.year)!) { 660 | rows = 366-1 661 | }else{ 662 | rows = 355-1 663 | } 664 | } 665 | }else//不是第一组 说明需要考虑 选完月份后 该月的天数问题 666 | { 667 | rows = numberOfDaysInMonth(year:tempDateCompnents.year!, month: tempDateCompnents.month!) 668 | } 669 | } 670 | break 671 | case UnitHour.rawValue: 672 | rows = 24//无论是否开启 数据源 公用 673 | break 674 | case UnitMinute.rawValue: 675 | rows = 60 676 | break 677 | case UnitSecond.rawValue: 678 | rows = 60 679 | break 680 | default: 681 | 682 | break 683 | } 684 | return rows 685 | } 686 | 687 | 688 | //获取标题 689 | internal func pickerViewTitle(forRow row:Int,forComponent component:Int) -> String { 690 | 691 | self.currentOperationComponent = component 692 | 693 | //1.计算增量 694 | var addComps:DateComponents = DateComponents() 695 | 696 | //2.获取当前模式 697 | let unitArray:[[UInt]] = pickerModeDict[pickerMode!]! 698 | let unitSubArray:[UInt] = unitArray[component] 699 | let unit:UInt = unitSubArray.last! 700 | 701 | 702 | //3.判断是否第一组 703 | if component == 0 { 704 | //计算偏差 705 | switch unit { 706 | case UnitYear.rawValue: 707 | addComps.year = row - sourceCompnentRowDict[component]! 708 | break 709 | case UnitMonth.rawValue: 710 | addComps.month = row - sourceCompnentRowDict[component]! 711 | break 712 | case UnitDay.rawValue: 713 | addComps.day = row - sourceCompnentRowDict[component]! 714 | break 715 | default: 716 | //剩下的时分秒 717 | return String(format:"%02zd%@",row,unitDescDict[unit]!) 718 | // break 719 | } 720 | 721 | 722 | let tempDate:Date? = calendar.date(byAdding: addComps, to: date!) 723 | 724 | if let tDate:Date = tempDate { 725 | let dateComps:DateComponents = calendar.components(ComUnit, from: tDate) 726 | 727 | return getTitle(comps: dateComps, component:component) 728 | }else 729 | { 730 | return "title nil" 731 | } 732 | }else 733 | { 734 | if unit == UnitMonth.rawValue || unit == UnitDay.rawValue{ 735 | return String(format:"%02zd%@",row+1,unitDescDict[unit]!) 736 | }else{ 737 | return String(format:"%02zd%@",row,unitDescDict[unit]!) 738 | } 739 | 740 | } 741 | } 742 | 743 | 744 | 745 | internal func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat { 746 | //1.获取当前模式 747 | let unitArray:[[UInt]] = pickerModeDict[pickerMode!]! 748 | 749 | //2.计算全部的个数 750 | var totalCount:Int = 0 751 | var totalComponentCount:Int = 0 752 | 753 | var tempComponent = 0 754 | for unitSubArray:[UInt] in unitArray{ 755 | for unit:UInt in unitSubArray { 756 | var add:Int = (1 + ( unitDescDict[unit]?.characters.count)!) 757 | // 年的话,加位 758 | if unit == UnitYear.rawValue { 759 | add += 1 760 | } 761 | //计算总长 762 | totalCount += add 763 | //计算当前 764 | if tempComponent == component { 765 | totalComponentCount += add 766 | } 767 | } 768 | tempComponent += 1 769 | } 770 | 771 | //3.更合理的划分 772 | let width:CGFloat = (pickerView.bounds.width - 20.0) 773 | let baseWidth:CGFloat = width/CGFloat(unitArray.count) 774 | let mutiWidth:CGFloat = width - baseWidth 775 | 776 | //4.计算宽度 777 | let componentWidth:CGFloat = CGFloat(totalComponentCount)/CGFloat(totalCount)*mutiWidth + (baseWidth/CGFloat(unitArray.count)) 778 | 779 | return componentWidth 780 | 781 | } 782 | 783 | //返回选项高度 784 | internal func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { 785 | return pickerView.bounds.height/5 786 | } 787 | 788 | 789 | //返回label 790 | internal func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { 791 | 792 | let title:String = pickerViewTitle(forRow: row, forComponent: component) 793 | 794 | var label:UILabel? = nil 795 | 796 | if view != nil { 797 | label = view as! UILabel? 798 | }else{ 799 | label = UILabel() 800 | label?.textAlignment = NSTextAlignment.center 801 | // Font_120_218_317_416_515_614_712_810_908 802 | label?.font = Font4 803 | label?.text = title 804 | } 805 | return label! 806 | } 807 | 808 | 809 | //选择停止 810 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 811 | 812 | //优化掉 813 | if self.currentOperationComponent != component{ 814 | return 815 | } 816 | 817 | //1.获取当前模式 818 | let unitArray:[[UInt]] = pickerModeDict[pickerMode!]! 819 | 820 | //2.遍历 每一组最后一个 821 | var tempComponent:Int = 0 822 | 823 | //用来加减 824 | var addDateComps:DateComponents = DateComponents() 825 | 826 | for unitSubArray:[UInt] in unitArray 827 | { 828 | //当前选中 829 | var currentRow:Int = pickerView.selectedRow(inComponent: tempComponent) 830 | if component == tempComponent { 831 | currentRow = row 832 | } 833 | //单位 834 | let unit:UInt = unitSubArray.last! 835 | 836 | 837 | //分类型处理 838 | switch unit { 839 | case UnitYear.rawValue: 840 | addDateComps.year = currentRow - sourceCompnentRowDict[tempComponent]! 841 | break 842 | case UnitMonth.rawValue: 843 | addDateComps.month = currentRow - sourceCompnentRowDict[tempComponent]! 844 | break 845 | case UnitDay.rawValue: 846 | //如果不是第一组才需要验证 847 | if tempComponent != 0 { 848 | //验证日期 因为每一月的天数不一样 849 | //1.第一步先计算当前步骤选中的年月 850 | let tempDate:Date? = calendar.date(byAdding: addDateComps, to: self.date!, options: NSCalendar.Options.matchLast) 851 | let tempDateComps:DateComponents = calendar.components(ComUnit, from: tempDate!) 852 | tempDateCompnents.year = tempDateComps.year 853 | tempDateCompnents.month = tempDateComps.month 854 | //2.获取天数 855 | let days:Int = numberOfDaysInMonth(year: tempDateComps.year!, month: tempDateComps.month!) 856 | //3.判断当前选择项是否越界 857 | if currentRow > days - 1 {//假如目前为 12月 则 31天 索引 30 ,当选择至11月 则 30天 索引 29 若日期为 12月31 则选择回11月30,最大索引变为29 但当前索引为30 则会越界 858 | 859 | //先选回29再刷新 防止越界闪退 860 | pickerView.selectRow(days - 1, inComponent: tempComponent, animated: false) 861 | 862 | //更新掉 sourceCompentRowDict 和 currentRow 863 | currentRow = days - 1 864 | } 865 | //刷新月份coponent 不同的月份天数不同 随时更新 866 | pickerView.reloadComponent(tempComponent) 867 | } 868 | 869 | //计算时间 870 | addDateComps.day = currentRow - sourceCompnentRowDict[tempComponent]! 871 | break 872 | case UnitHour.rawValue: 873 | addDateComps.hour = currentRow - sourceCompnentRowDict[tempComponent]! 874 | break 875 | case UnitMinute.rawValue: 876 | addDateComps.minute = currentRow - sourceCompnentRowDict[tempComponent]! 877 | break 878 | case UnitSecond.rawValue: 879 | addDateComps.second = currentRow - sourceCompnentRowDict[tempComponent]! 880 | break 881 | default: 882 | 883 | break 884 | } 885 | tempComponent += 1 886 | } 887 | 888 | let date:Date? = calendar.date(byAdding: addDateComps, to: self.date!, options: NSCalendar.Options.matchLast) 889 | if let d = date { 890 | 891 | //标记 892 | var compareFlag:Bool = true 893 | 894 | //如果存在最小的时间 895 | if let minD:Date = minDate { 896 | //当前日期小于最小日期 897 | if(d.compare(minD) == ComparisonResult.orderedAscending){ 898 | if let closure = didSelectedDateClosure 899 | { 900 | closure(minD) 901 | } 902 | scrollToDate(date: minD, animated: true) 903 | compareFlag = false 904 | } 905 | } 906 | 907 | //如果存在最大时间 908 | if let maxD:Date = maxDate { 909 | //当前日期大于当前日期 910 | if(d.compare(maxD) == ComparisonResult.orderedDescending){ 911 | if let closure = didSelectedDateClosure 912 | { 913 | closure(maxD) 914 | } 915 | scrollToDate(date: maxD, animated: true) 916 | compareFlag = false 917 | } 918 | } 919 | 920 | //日期已定 921 | if compareFlag 922 | { 923 | //如果未开启限制模式,则自动滚动 无限滚动 924 | 925 | scrollToDate(date: d, animated: false) 926 | if let closure = didSelectedDateClosure 927 | { 928 | closure(d) 929 | } 930 | } 931 | } 932 | } 933 | 934 | 935 | //滚动到指定页 936 | internal func toCurentDate(animated:Bool) { 937 | 938 | tempDateCompnents.year = currentDateCompnents?.year 939 | tempDateCompnents.month = currentDateCompnents?.month 940 | 941 | //1.获取当前模式 942 | let unitArray:[[UInt]] = pickerModeDict[pickerMode!]! 943 | 944 | //2.设置选中 945 | var comps:DateComponents = currentDateCompnents! 946 | 947 | //3.分类型处理 948 | var component:Int = 0 949 | var tempRow:Int = 0 950 | for unitSubArray:[UInt] in unitArray{ 951 | let unit:UInt = unitSubArray.last! 952 | 953 | switch unit { 954 | case UnitYear.rawValue: 955 | //判断限制模式 956 | if limitedEnable() { 957 | //开启了限制返回数据源 958 | tempRow = selectedRowInComponentForLimited(component: component, unit: unit) 959 | }else{ 960 | //第一组的最后一个是 year 961 | tempRow = currentIndex! 962 | } 963 | break 964 | case UnitMonth.rawValue: 965 | //判断限制模式 966 | if limitedEnable() { 967 | //开启了限制返回数据源 968 | tempRow = selectedRowInComponentForLimited(component: component, unit: unit) 969 | }else{ 970 | if component == 0 { 971 | //第一组的最后一个是 month 972 | tempRow = currentIndex! 973 | }else 974 | { 975 | //指向 month 976 | tempRow = comps.month!-1 977 | } 978 | } 979 | break 980 | case UnitDay.rawValue: 981 | //判断限制模式 982 | if limitedEnable() { 983 | //开启了限制返回数据源 984 | tempRow = selectedRowInComponentForLimited(component: component, unit: unit) 985 | }else{ 986 | if component == 0{ 987 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue {//如果第一个是月份 即不是年 988 | if isLeapYear(year: (currentDateCompnents?.year)!) { 989 | //第一组的最后一个是 Day 990 | tempRow = (366-1)/2-1 991 | }else{ 992 | //第一组的最后一个是 Day 993 | tempRow = (365-1)/2-1 994 | } 995 | }else{ 996 | //第一组的最后一个是 Day 997 | tempRow = currentIndex! 998 | } 999 | }else 1000 | { 1001 | //指向 day 1002 | tempRow = comps.day! - 1 1003 | } 1004 | } 1005 | break 1006 | case UnitHour.rawValue: 1007 | //判断限制模式 1008 | if limitedEnable() { 1009 | //开启了限制返回数据源 1010 | tempRow = selectedRowInComponentForLimited(component: component, unit: unit) 1011 | }else{ 1012 | //指向 hour 1013 | tempRow = comps.hour! 1014 | } 1015 | break 1016 | case UnitMinute.rawValue: 1017 | //判断限制模式 1018 | if limitedEnable() { 1019 | //开启了限制返回数据源 1020 | tempRow = selectedRowInComponentForLimited(component: component, unit: unit) 1021 | }else{ 1022 | //指向 minute 1023 | tempRow = comps.minute! 1024 | } 1025 | break 1026 | case UnitSecond.rawValue: 1027 | //判断限制模式 1028 | if limitedEnable() { 1029 | //开启了限制返回数据源 1030 | tempRow = selectedRowInComponentForLimited(component: component, unit: unit) 1031 | }else{ 1032 | //指向 second 1033 | tempRow = comps.second! 1034 | } 1035 | break 1036 | default: 1037 | // 1038 | break 1039 | } 1040 | 1041 | //滑动到指定选项 1042 | pickerView.selectRow(tempRow, inComponent: component, animated: 1043 | animated) 1044 | //存储 1045 | sourceCompnentRowDict[component] = tempRow 1046 | 1047 | component += 1 1048 | 1049 | 1050 | } 1051 | pickerView.reloadAllComponents() 1052 | } 1053 | 1054 | //布局 1055 | override func layoutSubviews() { 1056 | super.layoutSubviews() 1057 | 1058 | let pickerX:CGFloat = 0 1059 | let pickerY:CGFloat = 0 1060 | let pickerW:CGFloat = bounds.width 1061 | let pickerH:CGFloat = bounds.height 1062 | pickerView.frame = CGRect(x: pickerX, y: pickerY, width: pickerW, height: pickerH) 1063 | } 1064 | 1065 | 1066 | //获取标题 1067 | internal func getTitle(comps:DateComponents,component:Int) -> String 1068 | { 1069 | var m_str:String = String() 1070 | 1071 | //1.获取当前模式 1072 | let unitArray:[[UInt]] = pickerModeDict[pickerMode!]! 1073 | let subArray:[UInt] = unitArray[component] 1074 | 1075 | for unit:UInt in subArray{ 1076 | //分类型处理 1077 | switch unit { 1078 | case UnitYear.rawValue: 1079 | m_str.append(String(format: "%04zd%@", comps.year!,unitDescDict[unit]!)) 1080 | break 1081 | case UnitMonth.rawValue: 1082 | m_str.append(String(format: "%02zd%@", comps.month!,unitDescDict[unit]!)) 1083 | break 1084 | case UnitDay.rawValue: 1085 | m_str.append(String(format: "%02zd%@", comps.day!,unitDescDict[unit]!)) 1086 | break 1087 | case UnitHour.rawValue: 1088 | m_str.append(String(format: "%02zd%@", comps.hour!,unitDescDict[unit]!)) 1089 | break 1090 | case UnitMinute.rawValue: 1091 | m_str.append(String(format: "%02zd%@", comps.minute!,unitDescDict[unit]!)) 1092 | break 1093 | case UnitSecond.rawValue: 1094 | m_str.append(String(format: "%02zd%@", comps.second!,unitDescDict[unit]!)) 1095 | break 1096 | default: 1097 | 1098 | break 1099 | } 1100 | } 1101 | return m_str 1102 | } 1103 | 1104 | 1105 | //MARK:用于开始限制功能部分 1106 | 1107 | //获取限制状态 1108 | internal func limitedEnable() -> Bool { 1109 | 1110 | //如果未开启 1111 | if(!enableLimited!) 1112 | { 1113 | let unitSubArray:[UInt] = (pickerModeDict[pickerMode!]?.first!)! 1114 | if unitSubArray.first != UnitYear.rawValue && unitSubArray.last == UnitDay.rawValue{ 1115 | return true 1116 | } 1117 | } 1118 | //默认 1119 | return enableLimited! 1120 | 1121 | } 1122 | 1123 | //限制情况下的 数据源 1124 | internal func numberOfRowsInComponetForLimited(component:Int,unit:UInt) -> Int { 1125 | //当前情况下表示第一个 1126 | let minD:Date? = minDate 1127 | //当前情况下表示第一个 1128 | let maxD:Date? = maxDate 1129 | 1130 | var numberOfRows = 0 1131 | 1132 | //分类型处理 1133 | switch unit { 1134 | case UnitYear.rawValue: 1135 | if minD != nil && maxD != nil {//都有值 1136 | numberOfRows = calendar.components(UnitYear, from: getDateRemoveStopLastUnit(date: minD!, lastUnit:UnitYear ,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit:UnitYear,isFromDate: false), options: NSCalendar.Options.matchLast).year!+1 1137 | }else{ 1138 | numberOfRows = MaxRangeValue 1139 | } 1140 | break 1141 | case UnitMonth.rawValue: 1142 | if component == 0 { 1143 | if minD != nil && maxD != nil {//都有值 1144 | numberOfRows = calendar.components(UnitMonth, from: getDateRemoveStopLastUnit(date: minD!, lastUnit:UnitYear,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit:UnitYear,isFromDate: false), options: NSCalendar.Options.matchLast).month! 1145 | }else{ 1146 | numberOfRows = MaxRangeValue 1147 | } 1148 | }else 1149 | { 1150 | numberOfRows = 12 1151 | } 1152 | break 1153 | case UnitDay.rawValue: 1154 | if component == 0 { 1155 | if minD != nil && maxD != nil {//都有值 1156 | numberOfRows = calendar.components(UnitDay, from: getDateRemoveStopLastUnit(date: minD!, lastUnit:UnitDay,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit:UnitDay,isFromDate: false), options: NSCalendar.Options.matchLast).day!+1 1157 | }else if minD == nil && maxD == nil { 1158 | let comps:DateComponents = calendar.components(ComUnit, from: minD!) 1159 | if isLeapYear(year: (comps.year!)) { 1160 | numberOfRows = 366-1 1161 | }else 1162 | { 1163 | numberOfRows = 365-1 1164 | } 1165 | }else if minD != nil{//只有最小日期 1166 | let comps:DateComponents = calendar.components(ComUnit, from: minD!) 1167 | if isLeapYear(year: (comps.year!)) { 1168 | numberOfRows = 366-1 1169 | }else 1170 | { 1171 | numberOfRows = 365-1 1172 | } 1173 | }else if maxD != nil{ 1174 | let comps:DateComponents = calendar.components(ComUnit, from: maxD!) 1175 | if isLeapYear(year: (comps.year!)) { 1176 | numberOfRows = 366 1177 | }else 1178 | { 1179 | numberOfRows = 365 1180 | } 1181 | } 1182 | }else 1183 | { 1184 | numberOfRows = numberOfDaysInMonth(year:tempDateCompnents.year!, month: tempDateCompnents.month!) 1185 | } 1186 | break 1187 | case UnitHour.rawValue: 1188 | numberOfRows = 24 1189 | break 1190 | case UnitMinute.rawValue: 1191 | numberOfRows = 60 1192 | break 1193 | case UnitSecond.rawValue: 1194 | numberOfRows = 60 1195 | break 1196 | default: 1197 | 1198 | break 1199 | } 1200 | return numberOfRows 1201 | } 1202 | 1203 | //选中row 1204 | internal func selectedRowInComponentForLimited(component:Int,unit:UInt) -> Int { 1205 | 1206 | //当前情况下表示第一个 1207 | let minD:Date? = minDate 1208 | //当前情况下表示第一个 1209 | let maxD:Date? = maxDate 1210 | 1211 | var selectedRows:Int = 0 1212 | 1213 | //分类型处理 1214 | switch unit { 1215 | case UnitYear.rawValue: 1216 | if minD != nil{//都有值 1217 | selectedRows = calendar.components(UnitYear, from: getDateRemoveStopLastUnit(date: minD!, lastUnit: UnitYear,isFromDate: true), to: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitYear,isFromDate: false), options: NSCalendar.Options.matchLast).year! 1218 | }else if maxD != nil { 1219 | selectedRows = MaxRangeValue - calendar.components(UnitYear, from: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitYear,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit: UnitYear,isFromDate: false), options: NSCalendar.Options.matchLast).year! 1220 | }else if minD != nil && maxD != nil{ 1221 | selectedRows = calendar.components(UnitYear, from: getDateRemoveStopLastUnit(date: minD!, lastUnit: UnitYear,isFromDate: true), to: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitYear,isFromDate: false), options: NSCalendar.Options.matchLast).year! 1222 | } 1223 | break 1224 | case UnitMonth.rawValue: 1225 | if component == 0 { 1226 | if minD != nil && maxD != nil {//都有值 1227 | selectedRows = calendar.components(UnitMonth, from: getDateRemoveStopLastUnit(date: minD!, lastUnit: UnitMonth,isFromDate: true), to:getDateRemoveStopLastUnit(date: maxD!, lastUnit: UnitMonth,isFromDate: false), options: NSCalendar.Options.matchLast).month! 1228 | }else if minD == nil && maxD == nil { 1229 | selectedRows = currentIndex! 1230 | }else if minD != nil{ 1231 | selectedRows = calendar.components(UnitMonth, from: getDateRemoveStopLastUnit(date: minD!, lastUnit: UnitMonth,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit: UnitMonth,isFromDate: false), options: NSCalendar.Options.matchLast).month! 1232 | }else if maxD != nil{ 1233 | selectedRows = MaxRangeValue - calendar.components(UnitMonth, from: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitMonth,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit: UnitMonth,isFromDate: false), options: NSCalendar.Options.matchLast).month! 1234 | } 1235 | }else 1236 | { 1237 | if minD != nil && maxD != nil{ 1238 | selectedRows = (currentDateCompnents?.month)! - 1 1239 | }else{ 1240 | selectedRows = currentIndex! 1241 | } 1242 | } 1243 | break 1244 | case UnitDay.rawValue: 1245 | if component == 0 { 1246 | if minD != nil && maxD != nil {//都有值 1247 | selectedRows = calendar.components(UnitDay, from: getDateRemoveStopLastUnit(date: minD!, lastUnit: UnitDay,isFromDate: true), to: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitDay,isFromDate: false), options: NSCalendar.Options.matchLast).day! 1248 | }else if minD == nil && maxD == nil { 1249 | let comps:DateComponents = calendar.components(ComUnit, from: minD!) 1250 | 1251 | if isLeapYear(year: (comps.year!)) { 1252 | selectedRows = (366-1)/2 1253 | }else 1254 | { 1255 | selectedRows = (365-1)/2 1256 | } 1257 | }else if minD != nil{ 1258 | selectedRows = calendar.components(UnitDay, from: getDateRemoveStopLastUnit(date: minD!, lastUnit: UnitDay,isFromDate: true), to: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitDay,isFromDate: false), options: NSCalendar.Options.matchLast).day! 1259 | 1260 | }else if maxD != nil{ 1261 | let comps:DateComponents = calendar.components(ComUnit, from: maxD!) 1262 | if isLeapYear(year: (comps.year!)) { 1263 | 1264 | selectedRows = 366 - 1 - calendar.components(UnitDay, from: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitDay,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit: UnitDay,isFromDate: false), options:Option).day! 1265 | }else 1266 | { 1267 | selectedRows = 365 - 1 - calendar.components(UnitDay, from: getDateRemoveStopLastUnit(date: date!, lastUnit: UnitDay,isFromDate: true), to: getDateRemoveStopLastUnit(date: maxD!, lastUnit: UnitDay,isFromDate: false), options:Option).day! 1268 | } 1269 | } 1270 | }else 1271 | { 1272 | selectedRows = (currentDateCompnents?.day!)!-1//索引 1273 | } 1274 | break 1275 | case UnitHour.rawValue: 1276 | selectedRows = (currentDateCompnents?.hour!)! 1277 | break 1278 | case UnitMinute.rawValue: 1279 | selectedRows = (currentDateCompnents?.minute!)! 1280 | break 1281 | case UnitSecond.rawValue: 1282 | selectedRows = (currentDateCompnents?.second!)! 1283 | break 1284 | default: 1285 | 1286 | break 1287 | } 1288 | return selectedRows 1289 | } 1290 | 1291 | 1292 | //获取一个日期 对应的 前后一年内人的日期 1293 | private func getDateAddOrReduceYear(date:Date,isAdd:Bool) -> Date { 1294 | //判断最小日期是否在一年的范围内 1295 | var addComps:DateComponents = DateComponents() 1296 | if isAdd{ 1297 | addComps.year = 1 1298 | addComps.day = -1 1299 | }else{ 1300 | addComps.year = -1 1301 | addComps.day = 1 1302 | } 1303 | //日期 1304 | let lastDate:Date = calendar.date(byAdding: addComps, to:date, options: NSCalendar.Options.matchLast)! 1305 | 1306 | return lastDate 1307 | } 1308 | 1309 | 1310 | //去掉多余的日期部分,保证计算日期的差的准确度 1311 | func getDateRemoveStopLastUnit(date:Date,lastUnit:NSCalendar.Unit,isFromDate:Bool) -> Date { 1312 | //初始化 1313 | let unitArray:[UInt] = [UnitYear.rawValue,UnitMonth.rawValue,UnitDay.rawValue,UnitHour.rawValue,UnitMinute.rawValue,UnitSecond.rawValue] 1314 | 1315 | let dateComps:DateComponents = self.calendar.components(ComUnit, from: date) 1316 | 1317 | 1318 | var flag:Bool = true 1319 | var addDateComps:DateComponents = DateComponents() 1320 | 1321 | //遍历 1322 | for unitRawValue in unitArray{ 1323 | switch unitRawValue { 1324 | case UnitYear.rawValue: 1325 | if flag { 1326 | addDateComps.year = 0 1327 | }else{ 1328 | addDateComps.year = -(dateComps.year)! 1329 | } 1330 | //判断 1331 | if unitRawValue == lastUnit.rawValue{ 1332 | flag = false 1333 | } 1334 | break 1335 | case UnitMonth.rawValue: 1336 | if flag { 1337 | addDateComps.month = 0 1338 | }else{ 1339 | addDateComps.month = -(dateComps.month)! 1340 | } 1341 | //判断 1342 | if unitRawValue == lastUnit.rawValue{ 1343 | flag = false 1344 | } 1345 | break 1346 | case UnitDay.rawValue: 1347 | if flag { 1348 | addDateComps.day = 0 1349 | }else{ 1350 | addDateComps.day = -(dateComps.day)! 1351 | } 1352 | //判断 1353 | if unitRawValue == lastUnit.rawValue{ 1354 | flag = false 1355 | } 1356 | break 1357 | case UnitHour.rawValue: 1358 | if flag { 1359 | addDateComps.hour = 0 1360 | }else{ 1361 | addDateComps.hour = -(dateComps.hour)! 1362 | } 1363 | //判断 1364 | if unitRawValue == lastUnit.rawValue{ 1365 | flag = false 1366 | } 1367 | break 1368 | case UnitMinute.rawValue: 1369 | if flag { 1370 | addDateComps.minute = 0 1371 | }else{ 1372 | addDateComps.minute = -(dateComps.minute)! 1373 | } 1374 | //判断 1375 | if unitRawValue == lastUnit.rawValue{ 1376 | flag = false 1377 | } 1378 | break 1379 | case UnitSecond.rawValue: 1380 | if flag { 1381 | addDateComps.second = 0 1382 | }else{ 1383 | addDateComps.second = -(dateComps.second)! 1384 | } 1385 | //判断 1386 | if unitRawValue == lastUnit.rawValue{ 1387 | flag = false 1388 | } 1389 | break 1390 | default: 1391 | break 1392 | } 1393 | } 1394 | 1395 | if !isFromDate{ 1396 | addDateComps.second = 1 1397 | } 1398 | 1399 | let lastDate:Date = self.calendar.date(byAdding: addDateComps, to: date, options: NSCalendar.Options.matchLast)! 1400 | 1401 | return lastDate 1402 | } 1403 | } 1404 | -------------------------------------------------------------------------------- /JWDatePickerView-Swift/JWPickerKeyBoardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerKeyBoardView.swift 3 | // CarServer 4 | // 5 | // Created by 朱建伟 on 2016/10/17. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class JWPickerKeyBoardView: UIView ,UIPickerViewDelegate,UIPickerViewDataSource{ 12 | 13 | typealias ConfirmDateClosure = (Int) -> () 14 | 15 | var didConfirmClosure:ConfirmDateClosure? 16 | 17 | var numberClosure:(()->Int)? 18 | 19 | var titleClosure:((Int)->String)? 20 | 21 | var isRemove:Bool = false 22 | 23 | var font:UIFont?{ 24 | willSet{ 25 | if let f = font { 26 | titleLabel?.font = f 27 | titleLabel?.text = "日期选择" 28 | cancelBtn?.titleLabel?.font = f 29 | confirmBtn?.titleLabel?.font = f 30 | } 31 | } 32 | } 33 | 34 | 35 | var title:String?{ 36 | willSet{ 37 | if let t = newValue{ 38 | titleLabel?.text = t 39 | } 40 | } 41 | } 42 | 43 | //蒙版 44 | private var cover:UIControl = { 45 | let view:UIControl = UIControl(frame: ScreenBounds) 46 | view.backgroundColor = UIColor.black 47 | view.alpha = 0.4 48 | 49 | return view 50 | }() 51 | 52 | //取消按钮 53 | private var cancelBtn:UIButton? 54 | 55 | //确定按钮 56 | private var confirmBtn:UIButton? 57 | 58 | //头部标题 59 | private var titleLabel:UILabel? 60 | 61 | //头部View 62 | private var titleView:UIView? 63 | 64 | //pickerView 65 | let pickerView:UIPickerView = UIPickerView() 66 | 67 | override init(frame: CGRect) { 68 | super.init(frame: frame) 69 | 70 | backgroundColor = UIColor.white 71 | 72 | pickerView.delegate = self 73 | pickerView.dataSource = self 74 | 75 | cover.addTarget(self, action: #selector(JWDatePickerKeyBoardView.exitKeyBoard), for: UIControlEvents.touchUpInside) 76 | 77 | // FontPrompt_120_218_317_416_515_614_712_810_908 78 | font = Font4 79 | 80 | //标题View 81 | titleView = UIView() 82 | let rgb:CGFloat = 220/255.0 83 | titleView?.backgroundColor = UIColor(red: rgb, green: rgb, blue: rgb, alpha: 1) 84 | addSubview(titleView!) 85 | 86 | //取消按钮 87 | cancelBtn = UIButton() 88 | cancelBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 89 | cancelBtn?.titleLabel?.font = font 90 | cancelBtn?.setTitle("取消", for: UIControlState.normal) 91 | cancelBtn?.setTitle("取消", for: UIControlState.selected) 92 | cancelBtn?.setTitleColor(UIColor.orange, for: UIControlState.normal) 93 | cancelBtn?.tag = 0 94 | titleView?.addSubview(cancelBtn!) 95 | 96 | //确定按钮 97 | confirmBtn = UIButton() 98 | confirmBtn?.addTarget(self, action:#selector(JWDatePickerKeyBoardView.btnClick(btn:)), for: UIControlEvents.touchUpInside) 99 | confirmBtn?.titleLabel?.font = font 100 | confirmBtn?.setTitle("确定", for: UIControlState.normal) 101 | confirmBtn?.setTitle("确定", for: UIControlState.selected) 102 | confirmBtn?.setTitleColor(UIColor.orange, for: UIControlState.normal) 103 | confirmBtn?.tag = 1 104 | titleView?.addSubview(confirmBtn!) 105 | 106 | //标题 107 | titleLabel = UILabel() 108 | titleLabel?.textAlignment = NSTextAlignment.center 109 | titleLabel?.font = font 110 | titleLabel?.text = title 111 | titleLabel?.textColor = FontGrayColor 112 | titleView?.addSubview(titleLabel!) 113 | 114 | //pickerView 115 | addSubview(pickerView) 116 | } 117 | 118 | 119 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 120 | return 1 121 | } 122 | 123 | 124 | 125 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 126 | if let closure = self.numberClosure{ 127 | if self.titleClosure != nil{ 128 | return closure() 129 | } 130 | } 131 | 132 | return 0 133 | 134 | } 135 | 136 | //返回选项高度 137 | internal func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { 138 | return pickerView.bounds.height/5 139 | } 140 | 141 | 142 | //返回label 143 | internal func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { 144 | 145 | let title:String = titleClosure!(row) 146 | 147 | var label:UILabel? = nil 148 | 149 | if view != nil { 150 | label = view as! UILabel? 151 | }else{ 152 | label = UILabel() 153 | label?.textAlignment = NSTextAlignment.center 154 | label?.textColor = FontGrayColor 155 | // Font_120_218_317_416_515_614_712_810_908 156 | label?.font = Font4 157 | label?.text = title 158 | } 159 | return label! 160 | } 161 | 162 | private var currentIndex:Int = 0 163 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 164 | currentIndex = row 165 | } 166 | 167 | 168 | internal func btnClick(btn:UIButton) { 169 | if btn.tag == 1 { 170 | if let closure = didConfirmClosure{ 171 | closure(self.currentIndex) 172 | } 173 | } 174 | 175 | cover.alpha = 0.021; 176 | UIApplication.shared.keyWindow?.endEditing(true) 177 | 178 | } 179 | 180 | override func layoutSubviews() { 181 | super.layoutSubviews() 182 | 183 | //titleView 184 | setH(256) 185 | 186 | let titleViewX:CGFloat = 0 187 | let titleViewY:CGFloat = 0 188 | let titleViewW:CGFloat = bounds.width 189 | let titleViewH:CGFloat = 50 190 | titleView?.frame = CGRect(x: titleViewX, y: titleViewY, width: titleViewW, height: titleViewH) 191 | 192 | let cancelX:CGFloat = 0 193 | let cancelY:CGFloat = 0 194 | let cancelW:CGFloat = 80 195 | let cancelH:CGFloat = titleViewH 196 | cancelBtn?.frame = CGRect(x: cancelX, y: cancelY, width: cancelW, height: cancelH) 197 | 198 | let confirmW:CGFloat = cancelW 199 | let confirmH:CGFloat = titleViewH 200 | let confirmX:CGFloat = bounds.width - confirmW 201 | let confirmY:CGFloat = 0 202 | confirmBtn?.frame = CGRect(x: confirmX, y: confirmY, width: confirmW, height: confirmH) 203 | 204 | let titleW:CGFloat = bounds.width - cancelW*2 205 | let titleH:CGFloat = titleViewH 206 | let titleX:CGFloat = (cancelBtn?.frame.maxX)! 207 | let titleY:CGFloat = 0 208 | titleLabel?.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH) 209 | 210 | 211 | let pickerX:CGFloat = 0 212 | let pickerY:CGFloat = titleViewH 213 | let pickerW:CGFloat = titleViewW 214 | let pickerH:CGFloat = bounds.size.height - titleViewH 215 | pickerView.frame = CGRect(x: pickerX, y: pickerY, width: pickerW, height: pickerH) 216 | 217 | } 218 | 219 | required init?(coder aDecoder: NSCoder) { 220 | fatalError("init(coder:) has not been implemented") 221 | } 222 | 223 | 224 | /** 225 | * 此方法当 自定键盘移动到窗口上调用 226 | */ 227 | override func didMoveToWindow() { 228 | super.didMoveToWindow() 229 | if isRemove == false { 230 | let window:UIWindow = UIApplication.shared.keyWindow! 231 | cover.frame = ScreenBounds 232 | cover.alpha = 0.4 233 | window.addSubview(cover) 234 | }else 235 | { 236 | isRemove = false 237 | } 238 | } 239 | 240 | 241 | /** 242 | * 移除时 将蒙板一并移除 243 | */ 244 | override func removeFromSuperview() { 245 | isRemove = true 246 | cover.removeFromSuperview() 247 | 248 | super.removeFromSuperview() 249 | } 250 | 251 | 252 | func exitKeyBoard() { 253 | cover.alpha = 0.021 254 | UIApplication.shared.keyWindow?.endEditing(true) 255 | } 256 | 257 | func reloadData() { 258 | self.currentIndex = 0 259 | pickerView.reloadAllComponents() 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /JWDatePickerView/JWDatePickerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerView.h 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | typedef NS_ENUM(NSUInteger,JWDatePickerMode) 13 | { 14 | JWDatePickerMode_DateAndTime,// 年月日 时 分 秒 4 15 | JWDatePickerMode_DateAndTimeRYear,// 月日 时 分 秒 4 16 | JWDatePickerMode_Time,// 时分秒 3 17 | JWDatePickerMode_TimeRSecond,//时分 2 18 | JWDatePickerMode_Date,// 年月日 3 19 | JWDatePickerMode_DateAddHour,// 年 月 日 时 4 20 | JWDatePickerMode_DateAndTimeRSecond,// 年月日 时 分 3 21 | JWDatePickerMode_DateAndTimeRYearAndSecond//月日 时 分 3 22 | }; 23 | 24 | @class JWDatePickerView; 25 | /** 26 | * 代理方法 27 | */ 28 | @protocol JWDatePickerViewDelegate 29 | 30 | /** 31 | * 返回单位文字 比如说 参数NSCalendarUnitYear 返回 年 例如 2016年 没有则返回@""或者nil 32 | */ 33 | -(NSString*)datePickerView:(JWDatePickerView*)datePickerView unitForCalendarUnit:(NSCalendarUnit)calendarUnit; 34 | 35 | @end 36 | 37 | @interface JWDatePickerView : UIView 38 | 39 | /** 40 | * delegate 41 | */ 42 | @property(nonatomic,weak)id delegate; 43 | 44 | /** 45 | * 最小日期 如果是 不显示年的模式下 自动将最大日期设置在 一年内 只显示时分秒的同理 46 | */ 47 | @property(nonatomic,strong)NSDate* minDate; 48 | 49 | /** 50 | * 当前日期 如果比最小日期小,设置最小日期为 date 51 | */ 52 | @property(nonatomic,strong)NSDate* date; 53 | 54 | /** 55 | * 最大日起 如果是 不显示年的模式下 自动将最小日期设置在 一年内 只显示时分秒的同理 56 | */ 57 | @property(nonatomic,strong)NSDate* maxDate; 58 | 59 | /** 60 | * pickerModel 61 | */ 62 | @property(nonatomic,assign)JWDatePickerMode pickerMode; 63 | 64 | 65 | /** 66 | * font 67 | */ 68 | @property(nonatomic,strong)UIFont* font; 69 | 70 | 71 | 72 | /** 73 | * minuteSpace 分钟间隔 条件 minuteSpace<30&&60%minuteSpace==0 74 | */ 75 | @property(nonatomic,assign)NSUInteger minuteSpace; 76 | 77 | /** 78 | * secondSpace 秒间隔 条件 secondSpace<30&&60%secondSpace==0 79 | */ 80 | @property(nonatomic,assign)NSUInteger secondSpace; 81 | 82 | /** 83 | * 刷新数据 84 | */ 85 | -(void)reloadData; 86 | 87 | 88 | /** 89 | * 四舍五入 date 数据 90 | */ 91 | -(NSDate*)getRoundDateWithDate:(NSDate*)date; 92 | 93 | /** 94 | * 获取一天中的 最小时间00:00:00 最大时间 23:59:59 95 | */ 96 | -(NSDate*)getDayLastDateWithDate:(NSDate*)date max:(BOOL)isGetMax; 97 | 98 | /** 99 | * 获取日期字符串 100 | */ 101 | -(NSString*)getDateStr; 102 | /** 103 | * 获取日期字符串 104 | */ 105 | -(NSString*)getDateStrWithDateFormatterString:(NSString*)formatterStr; 106 | @end 107 | -------------------------------------------------------------------------------- /JWDatePickerView/JWDatePickerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWDatePickerView.m 3 | // JWDatePickerView 4 | // 5 | // Created by 朱建伟 on 16/7/19. 6 | // Copyright © 2016年 zhujianwei. All rights reserved. 7 | // 8 | #define KSplit @"(#拆#)"//用来拆分 9 | 10 | #define KTotalFormatter @"yyyy MM dd HH mm ss" 11 | #define KTotalFormatterStr @"%@ %@ %@ %@ %@ %@" 12 | 13 | #define KComUnit NSCalendarUnitYear|NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond 14 | 15 | 16 | #import "JWDatePickerView.h" 17 | 18 | @interface JWDatePickerView() 19 | /** 20 | * bgView 21 | */ 22 | @property(nonatomic,strong)UIView* bgView; 23 | /** 24 | * pickerView 25 | */ 26 | @property(nonatomic,strong)UIPickerView* pickerView; 27 | 28 | /** 29 | * modeDict 30 | */ 31 | @property(nonatomic,strong)NSMutableDictionary* modeDict; 32 | /** 33 | * formatterDict 34 | */ 35 | @property(nonatomic,strong)NSMutableDictionary* formatterDict; 36 | 37 | /** 38 | * unitdict 39 | */ 40 | @property(nonatomic,strong)NSMutableDictionary* unitStrDict; 41 | 42 | /** 43 | * calendar 44 | */ 45 | @property(nonatomic,strong)NSCalendar* calendar; 46 | 47 | /** 48 | * dateFormmater 49 | */ 50 | @property(nonatomic,strong)NSDateFormatter* dateF; 51 | 52 | 53 | /** 54 | * formmater 55 | */ 56 | @property(nonatomic,strong)NSDateFormatter* simpleDateF; 57 | 58 | /** 59 | * tempComps 60 | */ 61 | @property(nonatomic,strong)NSDateComponents *tempDateComps; 62 | 63 | 64 | @end 65 | 66 | @implementation JWDatePickerView 67 | 68 | /** 69 | * 初始化控件 70 | */ 71 | -(instancetype)initWithFrame:(CGRect)frame 72 | { 73 | if (self = [super initWithFrame:frame]) { 74 | self.minuteSpace = 1; 75 | self.secondSpace = 1; 76 | 77 | self.bgView = [[UIView alloc] init]; 78 | [self addSubview:self.bgView]; 79 | 80 | //模式 81 | self.pickerMode = JWDatePickerMode_DateAddHour; 82 | self.font = [UIFont boldSystemFontOfSize:16]; 83 | 84 | //pickerView 85 | self.pickerView = [[UIPickerView alloc] init]; 86 | self.pickerView.backgroundColor = [UIColor whiteColor]; 87 | self.pickerView.delegate = self; 88 | self.pickerView.dataSource = self; 89 | [self.bgView addSubview:self.pickerView]; 90 | 91 | } 92 | return self; 93 | } 94 | 95 | 96 | - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView 97 | { 98 | //返回对应模式下的compsCount 99 | // if (self.pickerMode==JWDatePickerMode_DateAndTime||self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAddHour) { 100 | // return 4; 101 | // }else if (self.pickerMode==JWDatePickerMode_TimeRSecond) 102 | // { 103 | // return 2; 104 | // } 105 | // return 3; 106 | return [self.modeDict objectForKey:@(self.pickerMode)].count; 107 | } 108 | 109 | 110 | - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component 111 | { 112 | NSArray* groupUnitArray = self.modeDict[@(self.pickerMode)]; 113 | if (component>=groupUnitArray.count) { 114 | return 0; 115 | } 116 | NSNumber* number = [groupUnitArray[component] lastObject]; 117 | 118 | switch (number.unsignedIntegerValue) { 119 | case NSCalendarUnitYear: 120 | { 121 | NSInteger year= [self.calendar component:number.unsignedIntegerValue fromDate:[self getMaxDate]]-[self.calendar component:number.unsignedIntegerValue fromDate:[self getMinDate]]+1; 122 | 123 | 124 | return year; 125 | break; 126 | } 127 | case NSCalendarUnitMonth: 128 | return 12; 129 | break; 130 | case NSCalendarUnitDay: 131 | { 132 | if (self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour) {//这个模式下 年 月 日 或者 年 月 日 时 133 | //根据年月日刷新 天数 134 | NSInteger days =[self getMonthDaysWithYear:self.tempDateComps.year andMoth:self.tempDateComps.month]; 135 | return days; 136 | }else 137 | { 138 | 139 | return [self.calendar components:number.unsignedIntegerValue fromDate:[self getMinDate] toDate:[self getMaxDate] options:NSCalendarMatchLast].day+1; 140 | } 141 | break; 142 | } 143 | case NSCalendarUnitHour: 144 | return 24; 145 | break; 146 | 147 | case NSCalendarUnitMinute: 148 | return 60/self.minuteSpace; 149 | break; 150 | 151 | case NSCalendarUnitSecond: 152 | return 60/self.secondSpace; 153 | break; 154 | default: 155 | break; 156 | } 157 | 158 | return 1; 159 | } 160 | 161 | 162 | 163 | -(NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component 164 | { 165 | 166 | 167 | NSArray* groupUnitArray = self.modeDict[@(self.pickerMode)]; 168 | 169 | if (component>=groupUnitArray.count) { 170 | return @""; 171 | } 172 | NSNumber* number = [groupUnitArray[component] lastObject]; 173 | 174 | switch (number.unsignedIntegerValue) { 175 | case NSCalendarUnitYear:// 年月日 模式下用 176 | { 177 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 178 | dateComps.year = row; 179 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 180 | 181 | NSArray*dateArray = [[self.dateF stringFromDate:date] componentsSeparatedByString:KSplit]; 182 | 183 | return dateArray[component]; 184 | break; 185 | } 186 | case NSCalendarUnitMonth://年 月 日 模式下用 187 | { 188 | return [NSString stringWithFormat:@"%02zd%@",row+1,self.unitStrDict[@(NSCalendarUnitMonth)]]; 189 | break; 190 | } 191 | case NSCalendarUnitDay: 192 | { 193 | if (self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour) {//这个模式下 单独选择日期 194 | return [NSString stringWithFormat:@"%02zd%@",row+1,self.unitStrDict[@(NSCalendarUnitDay)]]; 195 | }else 196 | { 197 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 198 | dateComps.day = row; 199 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 200 | 201 | NSArray*dateArray = [[self.dateF stringFromDate:date] componentsSeparatedByString:KSplit]; 202 | 203 | return dateArray[component]; 204 | } 205 | break; 206 | } 207 | case NSCalendarUnitHour: 208 | { 209 | return [NSString stringWithFormat:@"%02zd%@",row,self.unitStrDict[@(NSCalendarUnitHour)]]; 210 | break; 211 | } 212 | case NSCalendarUnitMinute: 213 | { 214 | return [NSString stringWithFormat:@"%02zd%@",self.minuteSpace*row,self.unitStrDict[@(NSCalendarUnitMinute)]]; 215 | break; 216 | } 217 | case NSCalendarUnitSecond: 218 | { 219 | return [NSString stringWithFormat:@"%02zd%@",self.secondSpace*row,self.unitStrDict[@(NSCalendarUnitSecond)]]; 220 | break; 221 | } 222 | default: 223 | { 224 | return @"return void"; 225 | break; 226 | } 227 | } 228 | 229 | } 230 | 231 | /** 232 | * 选中完日期之后 233 | */ 234 | -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ 235 | 236 | //用于拼接日期 237 | __block NSMutableString* m_str = [NSMutableString string]; 238 | 239 | 240 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//前面拼 年 241 | //同年可用 242 | NSInteger yearRow = [self.pickerView selectedRowInComponent:0]; 243 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 244 | dateComps.day = yearRow; 245 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 246 | NSDateComponents* comps = [self.calendar components:KComUnit fromDate:date]; 247 | 248 | [m_str appendFormat:@"%@%@",[NSString stringWithFormat:@"%02zd",comps.year],self.unitStrDict[@(NSCalendarUnitYear)]]; 249 | }else if (self.pickerMode == JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//前面拼接 年月日 250 | { 251 | //同年可用 252 | NSDateComponents* comps = [self.calendar components:KComUnit fromDate:[self getMinDate]]; 253 | //yyyy年MM月dd号 254 | [m_str appendFormat:@"%@%@%@%@%@%@%@",[NSString stringWithFormat:@"%02zd",comps.year],self.unitStrDict[@(NSCalendarUnitYear)],[NSString stringWithFormat:@"%02zd",comps.month],self.unitStrDict[@(NSCalendarUnitMonth)],[NSString stringWithFormat:@"%02zd",comps.day],self.unitStrDict[@(NSCalendarUnitDay)],KSplit]; 255 | } 256 | 257 | NSArray* groupArray = self.modeDict[@(self.pickerMode)]; 258 | 259 | 260 | [groupArray enumerateObjectsUsingBlock:^(NSArray* _Nonnull unitArray, NSUInteger idxOut, BOOL * _Nonnull stop) { 261 | //拼接拆分 262 | if (idxOut!=0) { 263 | [m_str appendString:KSplit]; 264 | } 265 | //选择项 266 | NSInteger row = [self.pickerView selectedRowInComponent:idxOut]; 267 | NSString *title = [self pickerView:pickerView titleForRow:row forComponent:idxOut]; 268 | 269 | if (self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour){ 270 | //记录 day 对应的component 271 | if ([unitArray.lastObject unsignedIntegerValue]==NSCalendarUnitYear) { 272 | NSDateComponents*dateComps = [[NSDateComponents alloc] init]; 273 | dateComps.year = row; 274 | NSDate* date = [self.calendar dateByAddingComponents:dateComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 275 | self.tempDateComps.year = [self.calendar components:KComUnit fromDate:date].year; 276 | 277 | }else if ([unitArray.lastObject unsignedIntegerValue]==NSCalendarUnitMonth) 278 | { 279 | 280 | self.tempDateComps.month = row+1; 281 | } 282 | else if ([unitArray.lastObject unsignedIntegerValue]==NSCalendarUnitDay) { 283 | //获取到当前row 和刷新后的rowCount 比较 284 | NSInteger newRowCount= [self pickerView:pickerView numberOfRowsInComponent:idxOut]; 285 | if (row>=newRowCount) { 286 | [self.pickerView selectRow:newRowCount-1 inComponent:idxOut animated:NO]; 287 | 288 | title = [NSString stringWithFormat:@"%02zd%@",newRowCount,self.unitStrDict[@(NSCalendarUnitDay)]]; 289 | } 290 | [self.pickerView reloadAllComponents]; 291 | } 292 | } 293 | [m_str appendString:title]; 294 | 295 | }]; 296 | 297 | 298 | NSDate* date = [self.simpleDateF dateFromString:m_str]; 299 | 300 | if (!date) { 301 | NSLog(@"Date is nil"); 302 | return; 303 | } 304 | 305 | if(date){[self checkWithSelectedDate:date];}else{NSLog(@"Date 为 nil");} 306 | 307 | 308 | } 309 | 310 | 311 | /** 312 | * 根据选择的时间 验证最大时间和最小时间 可以择调用回调 313 | */ 314 | -(void)checkWithSelectedDate:(NSDate*)date 315 | { 316 | if ([date compare:self.maxDate]==NSOrderedDescending){ 317 | _date = self.maxDate; 318 | [self scrollToCurrentDate:self.date animated:YES]; 319 | }else if ([date compare:self.minDate]==NSOrderedAscending) { 320 | _date = self.minDate; 321 | [self scrollToCurrentDate:self.date animated:YES]; 322 | }else 323 | { 324 | _date = date; 325 | } 326 | } 327 | 328 | 329 | /** 330 | * 四舍五入 date 数据 331 | */ 332 | -(NSDate*)getRoundDateWithDate:(NSDate*)date 333 | { 334 | NSDateComponents *com = [self.calendar components:KComUnit fromDate:date]; 335 | 336 | NSInteger day = com.day; 337 | NSInteger hour = (com.hour%KHourSpace?com.hour/KHourSpace+1:com.hour/KHourSpace)*KHourSpace; 338 | NSInteger minute = (com.minute%KMinuteSpace?com.minute/KMinuteSpace+1:com.minute/KMinuteSpace)*KMinuteSpace; 339 | 340 | 341 | NSDateComponents* dateAdd = [[NSDateComponents alloc] init]; 342 | dateAdd.minute = minute - com.minute; 343 | dateAdd.hour = hour - com.hour; 344 | dateAdd.day = day - com.day; 345 | 346 | 347 | return [self.calendar dateByAddingComponents:dateAdd toDate:date options:0]; 348 | } 349 | 350 | 351 | 352 | /** 353 | * 宽度 354 | */ 355 | - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component 356 | { 357 | NSInteger coms = [self numberOfComponentsInPickerView:self.pickerView]; 358 | CGFloat width = self.bounds.size.width-(coms-1)*10; 359 | 360 | NSString* formatterStr = [self getDateFormatterWithPickModel:self.pickerMode]; 361 | NSArray* array = [formatterStr componentsSeparatedByString:KSplit]; 362 | 363 | __block NSUInteger maxLength = 0; 364 | [array enumerateObjectsUsingBlock:^(NSString* _Nonnull str, NSUInteger idx, BOOL * _Nonnull stop) { 365 | maxLength+=str.length; 366 | }]; 367 | return width/maxLength*[array[component] length]; 368 | } 369 | 370 | /** 371 | * 高度 372 | */ 373 | - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component 374 | { 375 | return (self.bounds.size.height-20)/4; 376 | } 377 | 378 | 379 | 380 | 381 | -(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view 382 | { 383 | UILabel* label = nil; 384 | if (view&&[view isKindOfClass:[UILabel class]]) { 385 | label =(UILabel*)view; 386 | }else 387 | { 388 | label= [[UILabel alloc] init]; 389 | label.textAlignment =NSTextAlignmentCenter; 390 | label.font =self.font?self.font:[UIFont systemFontOfSize:16]; 391 | } 392 | label.text = [self pickerView:pickerView titleForRow:row forComponent:component]; 393 | return label; 394 | } 395 | 396 | 397 | 398 | /** 399 | * 布局 400 | */ 401 | -(void)layoutSubviews 402 | { 403 | [super layoutSubviews]; 404 | 405 | self.bgView.frame = self.bounds; 406 | 407 | CGFloat pickerX = 0; 408 | CGFloat pickerY = 10; 409 | CGFloat pickerW = self.bounds.size.width; 410 | CGFloat pickerH = self.bounds.size.height-20; 411 | self.pickerView.frame= CGRectMake(pickerX, pickerY, pickerW, pickerH); 412 | 413 | } 414 | 415 | 416 | 417 | /** 418 | * 设置dateFormmater 419 | */ 420 | -(NSString*)getDateFormatterWithPickModel:(JWDatePickerMode)mode 421 | { 422 | __block NSMutableString* m_str = [NSMutableString string]; 423 | 424 | NSArray* groupArray = self.modeDict[@(mode)]; 425 | 426 | 427 | [groupArray enumerateObjectsUsingBlock:^(NSArray* _Nonnull unitArray, NSUInteger idx, BOOL * _Nonnull stop) { 428 | //拼接拆分 429 | if (idx!=0) { 430 | [m_str appendString:KSplit]; 431 | } 432 | //遍历模式 433 | [unitArray enumerateObjectsUsingBlock:^(NSNumber* _Nonnull unitNumber, NSUInteger idx, BOOL * _Nonnull stop) { 434 | //拼接日历格式化字符串 435 | [m_str appendString:(self.formatterDict[unitNumber])]; 436 | 437 | //拼接单位 438 | if (self.delegate&&[self.delegate respondsToSelector:@selector(datePickerView:unitForCalendarUnit:)]) { 439 | NSString* unitStr = self.unitStrDict[unitNumber]; 440 | [m_str appendString:unitStr]; 441 | } 442 | }]; 443 | }]; 444 | return m_str; 445 | 446 | } 447 | 448 | 449 | 450 | /** 451 | * 滚动到指定的位置 452 | */ 453 | -(void)reloadDataNeedScrollTo:(BOOL)need 454 | { 455 | 456 | [self.pickerView reloadAllComponents]; 457 | 458 | 459 | [self.pickerView setNeedsLayout]; 460 | 461 | 462 | if([self getCurrentDate]&&need)//滑动到显示时间 463 | { 464 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 465 | [self scrollToCurrentDate:[self getCurrentDate] animated:YES]; 466 | }); 467 | } 468 | } 469 | 470 | /** 471 | * 滚动到指定的时间 472 | */ 473 | -(void)scrollToCurrentDate:(NSDate*)date animated:(BOOL)animated 474 | { 475 | 476 | NSArray* groupUnitArray = self.modeDict[@(self.pickerMode)]; 477 | 478 | [groupUnitArray enumerateObjectsUsingBlock:^(NSArray* _Nonnull unitArray, NSUInteger idx, BOOL * _Nonnull stop) { 479 | 480 | NSNumber *number = unitArray.lastObject; 481 | //获取的对应的组的rowCount 482 | NSInteger maxRowCount = [self pickerView:self.pickerView numberOfRowsInComponent:idx]; 483 | //分项处理 484 | switch (number.unsignedIntegerValue) { 485 | case NSCalendarUnitYear:// 年月日 模式下用 486 | { 487 | NSInteger year= [self.calendar component:number.unsignedIntegerValue fromDate:date]-[self.calendar component:number.unsignedIntegerValue fromDate:[self getMinDate]]; 488 | 489 | if (year*)modeDict 710 | { 711 | if (_modeDict==nil) { 712 | _modeDict = [NSMutableDictionary dictionary]; 713 | // JWDatePickerMode_DateAndTime,// 年月日 时分秒 714 | _modeDict[@(JWDatePickerMode_DateAndTime)]= @[@[@(NSCalendarUnitYear),@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)],@[@(NSCalendarUnitSecond)]]; 715 | 716 | // JWDatePickerMode_Time,// 时分秒 717 | _modeDict[@(JWDatePickerMode_Time)]= @[@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)],@[@(NSCalendarUnitSecond)]]; 718 | 719 | 720 | // JWDatePickerMode_TimeRSecond,// 时分 721 | _modeDict[@(JWDatePickerMode_TimeRSecond)]= @[@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)]]; 722 | 723 | // JWDatePickerMode_Date,// 年月日 724 | _modeDict[@(JWDatePickerMode_Date)]= @[@[@(NSCalendarUnitYear)],@[@(NSCalendarUnitMonth)],@[@(NSCalendarUnitDay)]]; 725 | 726 | // JWDatePickerMode_DateAndTimeRYear,// 月日 时 分 秒 727 | _modeDict[@(JWDatePickerMode_DateAndTimeRYear)]= @[@[@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)],@[@(NSCalendarUnitSecond)]]; 728 | 729 | // JWDatePickerMode_DateAddHour,// 年月日 时 分 730 | _modeDict[@(JWDatePickerMode_DateAddHour)]= @[@[@(NSCalendarUnitYear)],@[@(NSCalendarUnitMonth)],@[@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)]]; 731 | 732 | // JWDatePickerMode_DateAndTimeRSecond,// 年月日 时 分 733 | _modeDict[@(JWDatePickerMode_DateAndTimeRSecond)]= @[@[@(NSCalendarUnitYear),@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)]]; 734 | 735 | 736 | // JWDatePickerMode_DateAndTimeRYearAndSecond,//月日 时分 737 | _modeDict[@(JWDatePickerMode_DateAndTimeRYearAndSecond)]= @[@[@(NSCalendarUnitMonth),@(NSCalendarUnitDay)],@[@(NSCalendarUnitHour)],@[@(NSCalendarUnitMinute)]]; 738 | 739 | } 740 | 741 | return _modeDict; 742 | } 743 | 744 | /** 745 | * 格式化字符串 746 | */ 747 | -(NSDateFormatter *)dateF 748 | { 749 | if (_dateF==nil) { 750 | _dateF = [[NSDateFormatter alloc] init]; 751 | } 752 | return _dateF; 753 | } 754 | 755 | -(NSMutableDictionary *)unitStrDict 756 | { 757 | if (_unitStrDict ==nil&&self.delegate) { 758 | _unitStrDict = [NSMutableDictionary dictionary]; 759 | 760 | [self.formatterDict enumerateKeysAndObjectsUsingBlock:^(NSNumber* _Nonnull unitNumber, id _Nonnull obj, BOOL * _Nonnull stop) { 761 | 762 | NSString* unitStr = @""; 763 | 764 | if (self.delegate&&[self.delegate respondsToSelector:@selector(datePickerView:unitForCalendarUnit:)]) { 765 | unitStr = [self.delegate datePickerView:self unitForCalendarUnit:unitNumber.unsignedIntegerValue]; 766 | //验证拆分 767 | if([unitStr rangeOfString:KSplit].location!=NSNotFound) 768 | { 769 | unitStr = @""; 770 | } 771 | } 772 | _unitStrDict[unitNumber] = unitStr; 773 | }]; 774 | 775 | } 776 | return _unitStrDict; 777 | } 778 | 779 | /** 780 | * 格式化 781 | */ 782 | -(NSMutableDictionary *)formatterDict 783 | { 784 | if (_formatterDict ==nil) { 785 | _formatterDict = [NSMutableDictionary dictionary]; 786 | _formatterDict[@(NSCalendarUnitYear)]= @"yyyy"; 787 | _formatterDict[@(NSCalendarUnitMonth)]= @"MM"; 788 | _formatterDict[@(NSCalendarUnitDay)]= @"dd"; 789 | _formatterDict[@(NSCalendarUnitHour)]= @"HH"; 790 | _formatterDict[@(NSCalendarUnitMinute)]= @"mm"; 791 | _formatterDict[@(NSCalendarUnitSecond)]= @"ss"; 792 | } 793 | return _formatterDict; 794 | } 795 | 796 | /** 797 | * 设置日期格式 798 | */ 799 | -(void)setDateFDateFormatter 800 | { 801 | //1.获取到当前模式下的 格式化字符串 802 | NSString *dateFormatterStr = [self getDateFormatterWithPickModel:self.pickerMode]; 803 | self.dateF.dateFormat = dateFormatterStr; 804 | 805 | //2.拼接 转换选中模式下的格式化字符串 806 | NSString* sourceFormatter = @""; 807 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//前面拼 年 808 | sourceFormatter = [NSString stringWithFormat:@"yyyy%@",self.unitStrDict[@(NSCalendarUnitYear)]]; 809 | }else if (self.pickerMode == JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//前面拼接 年月日 810 | { 811 | sourceFormatter =[NSString stringWithFormat:@"yyyy%@MM%@dd%@%@",self.unitStrDict[@(NSCalendarUnitYear)],self.unitStrDict[@(NSCalendarUnitMonth)],self.unitStrDict[@(NSCalendarUnitDay)],KSplit]; 812 | } 813 | 814 | //3.完整拼接 815 | NSString* lastFormatterStr = [NSString stringWithFormat:@"%@%@",sourceFormatter,dateFormatterStr]; 816 | 817 | //4.设置格式化字符串 818 | self.simpleDateF.dateFormat= lastFormatterStr; 819 | 820 | } 821 | 822 | 823 | 824 | /** 825 | * 获取最大时间 826 | */ 827 | -(NSDate*)getMinDate 828 | { 829 | if (self.minDate==nil) { 830 | 831 | if (self.maxDate) {//设置 最小日期的时候 832 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//同一年 833 | NSDateComponents *addComps = [[NSDateComponents alloc] init]; 834 | addComps.year = -1; 835 | addComps.day = 1; 836 | self.minDate = [self.calendar dateByAddingComponents:addComps toDate:self.maxDate options:NSCalendarMatchLast]; 837 | }else if (self.pickerMode==JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//同一天 838 | { 839 | self.minDate = [self getDayLastDateWithDate:self.maxDate max:NO]; 840 | } 841 | else{ 842 | NSDateComponents* comps = [[NSDateComponents alloc] init]; 843 | comps.year = -1; 844 | self.minDate = [self.calendar dateByAddingComponents:comps toDate:self.maxDate options:NSCalendarMatchLast]; 845 | } 846 | }else 847 | { 848 | self.minDate = [NSDate date]; 849 | } 850 | } 851 | return self.minDate; 852 | } 853 | 854 | /** 855 | * 获取最小时间 856 | */ 857 | -(NSDate*)getMaxDate 858 | { 859 | if (self.maxDate ==nil) { 860 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//同一年 861 | NSDateComponents *addComps = [[NSDateComponents alloc] init]; 862 | addComps.year = 1; 863 | addComps.day = -1; 864 | _maxDate = [self.calendar dateByAddingComponents:addComps toDate:[self getMinDate] options:NSCalendarMatchLast]; 865 | }else if (self.pickerMode==JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//同一天 866 | { 867 | _maxDate = [self getDayLastDateWithDate:self.minDate max:YES]; 868 | } 869 | else{ 870 | NSDateComponents* comps = [[NSDateComponents alloc] init]; 871 | comps.year = 1; 872 | _maxDate = [self.calendar dateByAddingComponents:comps toDate:[self getMinDate] options:NSCalendarMatchLast]; 873 | } 874 | } 875 | return self.maxDate; 876 | } 877 | 878 | /** 879 | * 获取当前时间 880 | */ 881 | -(NSDate*)getCurrentDate 882 | { 883 | if (self.date ==nil) { 884 | self.date = [self getMinDate]; 885 | } 886 | return self.date; 887 | } 888 | 889 | 890 | 891 | /** 892 | * 切换模式时刷新 893 | */ 894 | -(void)setPickerMode:(JWDatePickerMode)pickerMode 895 | { 896 | 897 | _pickerMode = pickerMode; 898 | 899 | 900 | [self setDateFDateFormatter]; 901 | 902 | //年 月 日 单选 903 | if(self.pickerMode==JWDatePickerMode_Date||self.pickerMode==JWDatePickerMode_DateAddHour){ 904 | if (!self.date) { 905 | self.date = [self getMinDate]; 906 | } 907 | NSDateComponents *comps= [self.calendar components:KComUnit fromDate:self.date]; 908 | self.tempDateComps.year=comps.year; 909 | self.tempDateComps.month = comps.month; 910 | } 911 | 912 | if (self.minDate&&self.maxDate) { 913 | if (self.pickerMode==JWDatePickerMode_DateAndTimeRYear||self.pickerMode==JWDatePickerMode_DateAndTimeRYearAndSecond) {//同一年 914 | // 最小时间1年后的时间 compare 最大时间 915 | NSDateComponents *addComps = [[NSDateComponents alloc] init]; 916 | addComps.year = 1; 917 | addComps.day = -1; 918 | NSDate* lastMaxDate = [self.calendar dateByAddingComponents:addComps toDate:self.minDate options:NSCalendarMatchLast]; 919 | if ([lastMaxDate compare:self.maxDate]==NSOrderedAscending) { 920 | 921 | self.maxDate = lastMaxDate; 922 | [self.pickerView reloadAllComponents]; 923 | } 924 | }else if (self.pickerMode==JWDatePickerMode_Time||self.pickerMode==JWDatePickerMode_TimeRSecond)//同一天 925 | { 926 | NSDate* lastMaxDate = [self getDayLastDateWithDate:self.minDate max:YES]; 927 | 928 | if ([lastMaxDate compare:self.maxDate]==NSOrderedAscending) { 929 | self.maxDate = lastMaxDate; 930 | [self.pickerView reloadAllComponents]; 931 | } 932 | } 933 | } 934 | 935 | [self reloadDataNeedScrollTo:YES]; 936 | 937 | } 938 | 939 | /** 940 | * 设置代理是刷新 941 | */ 942 | -(void)setDelegate:(id)delegate 943 | { 944 | if (delegate) { 945 | _delegate = delegate; 946 | 947 | [self setDateFDateFormatter]; 948 | 949 | [self reloadData]; 950 | } 951 | } 952 | 953 | /** 954 | * 用于转换 选择的时间 955 | */ 956 | -(NSDateFormatter *)simpleDateF 957 | { 958 | if (_simpleDateF==nil) { 959 | _simpleDateF = [[NSDateFormatter alloc] init]; 960 | } 961 | return _simpleDateF; 962 | } 963 | 964 | 965 | /** 966 | * 限制间隔 967 | */ 968 | -(void)setMinuteSpace:(NSUInteger)minuteSpace 969 | { 970 | if (minuteSpace<30&&60%minuteSpace==0) { 971 | _minuteSpace = minuteSpace; 972 | } 973 | 974 | } 975 | 976 | /** 977 | * 限制间隔 978 | */ 979 | -(void)setSecondSpace:(NSUInteger)secondSpace 980 | { 981 | if (secondSpace<30&&60%secondSpace==0) { 982 | _secondSpace = secondSpace; 983 | } 984 | } 985 | 986 | 987 | /** 988 | * 是否是闰年 989 | */ 990 | -(BOOL)isLeapYear:(NSInteger)year 991 | { 992 | if (year%4==0) { 993 | if (year%100==0) { 994 | if (year%400==0) { 995 | return YES; 996 | } 997 | //能被 4 100 整除 不能被400 整除的 不是闰年 998 | return NO; 999 | } 1000 | //能被4整除 不能被100整除的 是闰年 1001 | return YES; 1002 | } 1003 | //不能为4整除 不是闰年 1004 | return NO; 1005 | } 1006 | 1007 | /** 1008 | * 根据对应的年 和月 返回当月对应的天数 1009 | */ 1010 | -(NSInteger)getMonthDaysWithYear:(NSInteger)year andMoth:(NSInteger)moth 1011 | { 1012 | // 31 28 31 30 31 30 31 31 30 31 30 31 1013 | NSArray *daysOfMonth=@[@31,@28,@31,@30,@31,@30,@31,@31,@30,@31,@30,@31]; 1014 | 1015 | NSUInteger days=[daysOfMonth[moth-1] integerValue]; 1016 | 1017 | if (days==28) { 1018 | if ([self isLeapYear:year])days=29; 1019 | } 1020 | return days; 1021 | } 1022 | 1023 | -(NSDateComponents *)tempDateComps 1024 | { 1025 | if (_tempDateComps==nil) { 1026 | _tempDateComps = [[NSDateComponents alloc] init]; 1027 | } 1028 | return _tempDateComps; 1029 | } 1030 | 1031 | 1032 | -(NSString *)getDateStrWithDateFormatterString:(NSString *)formatterStr 1033 | { 1034 | if (self.date&&formatterStr&&formatterStr.length) { 1035 | 1036 | } 1037 | return @"格式化字符串不合法"; 1038 | } 1039 | 1040 | -(NSString *)getDateStr 1041 | { 1042 | if (self.date) { 1043 | return [[self.simpleDateF stringFromDate:self.date] stringByReplacingOccurrencesOfString:KSplit withString:@""]; 1044 | } 1045 | return @"NO Date"; 1046 | } 1047 | 1048 | /** 1049 | * 获取一天中的 最小时间 最大时间 1050 | */ 1051 | -(NSDate*)getDayLastDateWithDate:(NSDate*)date max:(BOOL)isGetMax 1052 | { 1053 | if (!date) { 1054 | return [NSDate date]; 1055 | } 1056 | 1057 | NSDateComponents*dateComps = [self.calendar components:KComUnit fromDate:date]; 1058 | NSInteger hour = dateComps.hour; 1059 | NSInteger minute = dateComps.minute; 1060 | NSInteger second = dateComps.second; 1061 | 1062 | NSDateComponents *newDateComp = [NSDateComponents new]; 1063 | if (isGetMax) { 1064 | newDateComp.hour = 24-hour-1; 1065 | newDateComp.minute = 60 - minute -1; 1066 | newDateComp.second = 60 - second -1; 1067 | }else 1068 | { 1069 | newDateComp.hour = -hour; 1070 | newDateComp.minute = -minute; 1071 | newDateComp.second = -second; 1072 | } 1073 | NSDate* lastDate = [self.calendar dateByAddingComponents:newDateComp toDate:date options:NSCalendarMatchLast]; 1074 | return lastDate; 1075 | } 1076 | @end 1077 | -------------------------------------------------------------------------------- /JWDatePickerView/KBDatePickerViewkeyBoard.h: -------------------------------------------------------------------------------- 1 | // 2 | // KBSexkeyBoard.h 3 | // SunCarGuide 4 | // 5 | // Created by 朱建伟 on 15/12/17. 6 | // Copyright © 2015年 jryghq. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | #import "JWDatePickerView.h" 13 | 14 | @interface KBDatePickerViewkeyBoard : UIView 15 | 16 | /** 17 | * 快捷初始化 18 | */ 19 | -(instancetype)initWithFrame:(CGRect)frame title:(NSString*)title currentDayName:(NSString*)currentDayName; 20 | 21 | 22 | @property(nonatomic,strong,readonly)JWDatePickerView *pickerView; 23 | 24 | 25 | @property(nonatomic,copy)NSString *title; 26 | 27 | 28 | @property(nonatomic,copy)void(^didSelectedBlock)(NSDate *date,NSString* dateStr); 29 | 30 | 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /JWDatePickerView/KBDatePickerViewkeyBoard.m: -------------------------------------------------------------------------------- 1 | // 2 | // KBSexkeyBoard.m 3 | // SunCarGuide 4 | // 5 | // Created by 朱建伟 on 15/12/17. 6 | // Copyright © 2015年 jryghq. All rights reserved. 7 | // 8 | #import "KBDatePickerViewkeyBoard.h" 9 | @interface KBDatePickerViewkeyBoard() 10 | @property(nonatomic,strong)UIControl *cover; 11 | 12 | @property(nonatomic,strong)UIView *titleView; 13 | @property(nonatomic,strong)UILabel *titleLabel; 14 | @property(nonatomic,strong)UIButton *cancelBtn; 15 | @property(nonatomic,strong)UIButton *confirmBtn; 16 | 17 | 18 | @property(nonatomic,strong)NSString *currentTitle; 19 | 20 | @property(nonatomic,assign)BOOL isRemove; 21 | 22 | @property(nonatomic,assign)BOOL flag; 23 | 24 | 25 | @end 26 | 27 | @implementation KBDatePickerViewkeyBoard 28 | 29 | /** 30 | * 快捷初始化 31 | */ 32 | -(instancetype)initWithFrame:(CGRect)frame title:(NSString *)title currentDayName:(NSString *)currentDayName 33 | { 34 | if (self = [self initWithFrame:frame]) { 35 | self.title =title; 36 | self.titleLabel.text = title; 37 | self.currentTitle = currentDayName; 38 | } 39 | return self; 40 | } 41 | 42 | 43 | /** 44 | * 初始化 45 | */ 46 | -(instancetype)initWithFrame:(CGRect)frame 47 | { 48 | if(self = [super initWithFrame:frame]) 49 | { 50 | self.flag = YES; 51 | 52 | [self setClipsToBounds:NO]; 53 | 54 | _cover = [[UIControl alloc] init]; 55 | _cover.backgroundColor = [UIColor blackColor]; 56 | [_cover addTarget:self 57 | action:@selector(ExitKeyboard) forControlEvents:(UIControlEventTouchUpInside)]; 58 | _cover.alpha = 0.4; 59 | 60 | //标题View 61 | _titleView = [[UIView alloc] init]; 62 | [self addSubview:_titleView ]; 63 | 64 | //标题 65 | _titleLabel =[[UILabel alloc] init]; 66 | _titleLabel.text = @"请选择"; 67 | // CGFloat titleValue = 160; 68 | _titleLabel.textColor = [UIColor orangeColor];//[UIColor colorWithRed:(titleValue/255.0) green:(titleValue/255.0) blue:(titleValue/255.0) alpha:1]; 69 | _titleLabel.textAlignment = NSTextAlignmentCenter; 70 | _titleLabel.font = [UIFont systemFontOfSize:16]; 71 | [_titleView addSubview:_titleLabel]; 72 | 73 | //取消 74 | _cancelBtn = [[UIButton alloc] init]; 75 | [_cancelBtn setTitleColor:[UIColor orangeColor]forState:(UIControlStateNormal)]; 76 | [_cancelBtn setTitle:NSLocalizedString(@"取消",nil) forState:(UIControlStateNormal)]; 77 | _cancelBtn.titleLabel.font =[UIFont systemFontOfSize:16]; 78 | _cancelBtn.tag = 0; 79 | [_cancelBtn addTarget:self 80 | action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 81 | [_titleView addSubview:_cancelBtn]; 82 | 83 | //确定 84 | _confirmBtn = [[UIButton alloc] init]; 85 | [_confirmBtn setTitleColor:[UIColor orangeColor]forState:(UIControlStateNormal)]; 86 | [_confirmBtn setTitle:NSLocalizedString(@"确定",nil) forState:(UIControlStateNormal)]; 87 | _confirmBtn.titleLabel.font =[UIFont systemFontOfSize:16]; 88 | _confirmBtn.tag = 1; 89 | [_confirmBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 90 | [_titleView addSubview:_confirmBtn]; 91 | 92 | 93 | //pickerView 94 | _pickerView =[[JWDatePickerView alloc] init]; 95 | _pickerView.backgroundColor = [UIColor whiteColor]; 96 | [self addSubview:_pickerView]; 97 | 98 | } 99 | return self; 100 | } 101 | 102 | 103 | -(void)layoutSubviews 104 | { 105 | [super layoutSubviews]; 106 | 107 | CGFloat width = self.bounds.size.width; 108 | CGFloat height = 216; 109 | CGFloat margin = 10; 110 | 111 | CGFloat btnW = 80; 112 | 113 | CGFloat titleViewW = width; 114 | CGFloat titleViewH = 44; 115 | CGFloat titleViewX =0; 116 | CGFloat titleViewY =0; 117 | self.titleView.frame = CGRectMake(titleViewX, titleViewY, titleViewW, titleViewH); 118 | 119 | CGFloat cancelBtnW =btnW; 120 | CGFloat cancelBtnH = titleViewH; 121 | CGFloat cancelBtnX = margin; 122 | CGFloat cancelBtnY = 0; 123 | self.cancelBtn.frame =CGRectMake(cancelBtnX, cancelBtnY, cancelBtnW, cancelBtnH); 124 | 125 | 126 | CGFloat confirmBtnW = btnW; 127 | CGFloat confirmBtnH = titleViewH; 128 | CGFloat confirmBtnX = width - margin - confirmBtnW; 129 | CGFloat confirmBtnY = 0; 130 | self.confirmBtn.frame =CGRectMake(confirmBtnX, confirmBtnY, confirmBtnW, confirmBtnH); 131 | 132 | 133 | CGFloat titleLabelH = titleViewH; 134 | CGFloat titleLabelX = (cancelBtnX+cancelBtnW); 135 | CGFloat titleLabelW = confirmBtnX - titleLabelX; 136 | CGFloat titleLabelY = 0; 137 | self.titleLabel.frame = CGRectMake(titleLabelX, titleLabelY, titleLabelW, titleLabelH); 138 | 139 | 140 | CGFloat pickerViewX = 0; 141 | CGFloat pickerViewY = titleViewH; 142 | CGFloat pickerViewW = width; 143 | CGFloat pickerViewH = height - pickerViewY+margin; 144 | self.pickerView.frame = CGRectMake(pickerViewX, pickerViewY, pickerViewW, pickerViewH); 145 | 146 | } 147 | 148 | 149 | -(void)setTitle:(NSString *)title 150 | { 151 | _title = title; 152 | 153 | self.titleLabel.text = title; 154 | 155 | } 156 | 157 | 158 | /** 159 | * 此方法当 自定键盘移动到窗口上调用 160 | */ 161 | -(void)didMoveToWindow 162 | { 163 | [super didMoveToWindow]; 164 | if (!self.isRemove) { 165 | UIWindow *window = [UIApplication sharedApplication].keyWindow; 166 | self.cover.frame = CGRectMake(0,0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height); 167 | self.cover.alpha = 0.4; 168 | [window addSubview:self.cover]; 169 | }else 170 | { 171 | self.isRemove = NO; 172 | } 173 | } 174 | 175 | 176 | 177 | /** 178 | * 移除时 将蒙板一并移除 179 | */ 180 | -(void)removeFromSuperview 181 | { 182 | self.isRemove = YES; 183 | [self.cover removeFromSuperview]; 184 | [super removeFromSuperview]; 185 | } 186 | 187 | 188 | -(void)btnClick:(UIButton*)btn 189 | { 190 | if (btn.tag) {//确定 191 | if (self.didSelectedBlock&&self.pickerView.date) { 192 | self.didSelectedBlock(self.pickerView.date,[self.pickerView getDateStr]); 193 | } 194 | } 195 | 196 | self.cover.alpha = 0.021; 197 | 198 | [[UIApplication sharedApplication].keyWindow endEditing:YES]; 199 | } 200 | 201 | 202 | -(void)ExitKeyboard{ 203 | self.cover.alpha = 0.021; 204 | [[UIApplication sharedApplication].keyWindow endEditing:YES]; 205 | } 206 | 207 | @end 208 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JWDatePickerView 2 | 3 | 4 | ====介绍:本控件是为了扩展苹果的日历控件而编写, 5 | 6 | swift版本提供 10 个模式 7 | ``` 8 | public enum JWDatePickerMode:Int{ 9 | 10 | case dateAndTime// 年月日 时 分 秒 11 | case dateAndHour// 年月日 时 12 | case dateAndTimeRYear// 月日 时 分 秒 13 | case time// 时 分 秒 14 | case timeRSecond// 时 分 15 | case date// 年 月 日 16 | case dateAddHour// 年 月 日 时 17 | case dateAndTimeRSecond// 年月日 时 分 18 | case dateAndTimeRYearAndSecond// 月日 时 分 3 19 | case dateAndTimeForAllComponent// 年 月 日 时 分 秒 20 | } 21 | ``` 22 | 23 | OC版本主要提供8 个模式 24 | ``` 25 | JWDatePickerMode_DateAndTime,// 年月日 时 分 秒 26 | JWDatePickerMode_DateAndTimeRYear,// 月日 时 分 秒 27 | JWDatePickerMode_Time,// 时分秒 28 | JWDatePickerMode_TimeRSecond,//时分 29 | JWDatePickerMode_Date,// 年月日 30 | JWDatePickerMode_DateAddHour,// 年 月 日 时 31 | JWDatePickerMode_DateAndTimeRSecond,// 年月日 时 分 32 | JWDatePickerMode_DateAndTimeRYearAndSecond//月日 时 分 33 | ``` 34 | 35 | 36 | ====介绍:通过代理指定单位 37 | ``` 38 | -(NSString *)datePickerView:(JWDatePickerView *)datePickerView unitForCalendarUnit:(NSCalendarUnit)calendarUnit 39 | { 40 | 41 | switch (calendarUnit) { 42 | case NSCalendarUnitYear: 43 | return @"年"; 44 | break; 45 | case NSCalendarUnitMonth: 46 | return @"月"; 47 | break; 48 | case NSCalendarUnitDay: 49 | return @"日"; 50 | break; 51 | case NSCalendarUnitHour: 52 | return @"时"; 53 | break; 54 | case NSCalendarUnitMinute: 55 | return @"分"; 56 | break; 57 | case NSCalendarUnitSecond: 58 | return @"秒"; 59 | break; 60 | default: 61 | break; 62 | } 63 | return @" "; 64 | } 65 | ``` 66 | ====演示 67 |
68 | ![](https://github.com/GitHubOfJW/JWDatePickerView/blob/master/Source/PickerView.gif) 69 | -------------------------------------------------------------------------------- /Source/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JWDatePickerView/dec25a1cc59e871d7b8ecbcf145e59036b3e7a0d/Source/.DS_Store -------------------------------------------------------------------------------- /Source/PickerView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubOfJW/JWDatePickerView/dec25a1cc59e871d7b8ecbcf145e59036b3e7a0d/Source/PickerView.gif --------------------------------------------------------------------------------