├── .gitignore ├── DoubleSliderView ├── DoubleSliderView.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── DoubleSliderView │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── DoubleSliderView.swift │ ├── Info.plist │ ├── UIView+Extension.swift │ └── ViewController.swift ├── LICENSE ├── README.md └── imageFolder ├── doubleslider_1.gif └── doubleslider_2.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 23D2DB3E21E9DDE8008D4C79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23D2DB3D21E9DDE8008D4C79 /* AppDelegate.swift */; }; 11 | 23D2DB4021E9DDE8008D4C79 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23D2DB3F21E9DDE8008D4C79 /* ViewController.swift */; }; 12 | 23D2DB4321E9DDE8008D4C79 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23D2DB4121E9DDE8008D4C79 /* Main.storyboard */; }; 13 | 23D2DB4521E9DDE9008D4C79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23D2DB4421E9DDE9008D4C79 /* Assets.xcassets */; }; 14 | 23D2DB4821E9DDE9008D4C79 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23D2DB4621E9DDE9008D4C79 /* LaunchScreen.storyboard */; }; 15 | 23D2DB5021E9DE96008D4C79 /* DoubleSliderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23D2DB4F21E9DE95008D4C79 /* DoubleSliderView.swift */; }; 16 | 23D2DB5221E9DEFE008D4C79 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23D2DB5121E9DEFE008D4C79 /* UIView+Extension.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 23D2DB3A21E9DDE8008D4C79 /* DoubleSliderView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DoubleSliderView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 23D2DB3D21E9DDE8008D4C79 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 23D2DB3F21E9DDE8008D4C79 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 23 | 23D2DB4221E9DDE8008D4C79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 23D2DB4421E9DDE9008D4C79 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | 23D2DB4721E9DDE9008D4C79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | 23D2DB4921E9DDE9008D4C79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 23D2DB4F21E9DE95008D4C79 /* DoubleSliderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleSliderView.swift; sourceTree = ""; }; 28 | 23D2DB5121E9DEFE008D4C79 /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 23D2DB3721E9DDE8008D4C79 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 23D2DB3121E9DDE8008D4C79 = { 43 | isa = PBXGroup; 44 | children = ( 45 | 23D2DB3C21E9DDE8008D4C79 /* DoubleSliderView */, 46 | 23D2DB3B21E9DDE8008D4C79 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | 23D2DB3B21E9DDE8008D4C79 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 23D2DB3A21E9DDE8008D4C79 /* DoubleSliderView.app */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 23D2DB3C21E9DDE8008D4C79 /* DoubleSliderView */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 23D2DB5121E9DEFE008D4C79 /* UIView+Extension.swift */, 62 | 23D2DB4F21E9DE95008D4C79 /* DoubleSliderView.swift */, 63 | 23D2DB3D21E9DDE8008D4C79 /* AppDelegate.swift */, 64 | 23D2DB3F21E9DDE8008D4C79 /* ViewController.swift */, 65 | 23D2DB4121E9DDE8008D4C79 /* Main.storyboard */, 66 | 23D2DB4421E9DDE9008D4C79 /* Assets.xcassets */, 67 | 23D2DB4621E9DDE9008D4C79 /* LaunchScreen.storyboard */, 68 | 23D2DB4921E9DDE9008D4C79 /* Info.plist */, 69 | ); 70 | path = DoubleSliderView; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | 23D2DB3921E9DDE8008D4C79 /* DoubleSliderView */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = 23D2DB4C21E9DDE9008D4C79 /* Build configuration list for PBXNativeTarget "DoubleSliderView" */; 79 | buildPhases = ( 80 | 23D2DB3621E9DDE8008D4C79 /* Sources */, 81 | 23D2DB3721E9DDE8008D4C79 /* Frameworks */, 82 | 23D2DB3821E9DDE8008D4C79 /* Resources */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = DoubleSliderView; 89 | productName = DoubleSliderView; 90 | productReference = 23D2DB3A21E9DDE8008D4C79 /* DoubleSliderView.app */; 91 | productType = "com.apple.product-type.application"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | 23D2DB3221E9DDE8008D4C79 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastSwiftUpdateCheck = 1010; 100 | LastUpgradeCheck = 1010; 101 | ORGANIZATIONNAME = DU; 102 | TargetAttributes = { 103 | 23D2DB3921E9DDE8008D4C79 = { 104 | CreatedOnToolsVersion = 10.1; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = 23D2DB3521E9DDE8008D4C79 /* Build configuration list for PBXProject "DoubleSliderView" */; 109 | compatibilityVersion = "Xcode 9.3"; 110 | developmentRegion = en; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | Base, 115 | ); 116 | mainGroup = 23D2DB3121E9DDE8008D4C79; 117 | productRefGroup = 23D2DB3B21E9DDE8008D4C79 /* Products */; 118 | projectDirPath = ""; 119 | projectRoot = ""; 120 | targets = ( 121 | 23D2DB3921E9DDE8008D4C79 /* DoubleSliderView */, 122 | ); 123 | }; 124 | /* End PBXProject section */ 125 | 126 | /* Begin PBXResourcesBuildPhase section */ 127 | 23D2DB3821E9DDE8008D4C79 /* Resources */ = { 128 | isa = PBXResourcesBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | 23D2DB4821E9DDE9008D4C79 /* LaunchScreen.storyboard in Resources */, 132 | 23D2DB4521E9DDE9008D4C79 /* Assets.xcassets in Resources */, 133 | 23D2DB4321E9DDE8008D4C79 /* Main.storyboard in Resources */, 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXResourcesBuildPhase section */ 138 | 139 | /* Begin PBXSourcesBuildPhase section */ 140 | 23D2DB3621E9DDE8008D4C79 /* Sources */ = { 141 | isa = PBXSourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | 23D2DB4021E9DDE8008D4C79 /* ViewController.swift in Sources */, 145 | 23D2DB3E21E9DDE8008D4C79 /* AppDelegate.swift in Sources */, 146 | 23D2DB5221E9DEFE008D4C79 /* UIView+Extension.swift in Sources */, 147 | 23D2DB5021E9DE96008D4C79 /* DoubleSliderView.swift in Sources */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXSourcesBuildPhase section */ 152 | 153 | /* Begin PBXVariantGroup section */ 154 | 23D2DB4121E9DDE8008D4C79 /* Main.storyboard */ = { 155 | isa = PBXVariantGroup; 156 | children = ( 157 | 23D2DB4221E9DDE8008D4C79 /* Base */, 158 | ); 159 | name = Main.storyboard; 160 | sourceTree = ""; 161 | }; 162 | 23D2DB4621E9DDE9008D4C79 /* LaunchScreen.storyboard */ = { 163 | isa = PBXVariantGroup; 164 | children = ( 165 | 23D2DB4721E9DDE9008D4C79 /* Base */, 166 | ); 167 | name = LaunchScreen.storyboard; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXVariantGroup section */ 171 | 172 | /* Begin XCBuildConfiguration section */ 173 | 23D2DB4A21E9DDE9008D4C79 /* Debug */ = { 174 | isa = XCBuildConfiguration; 175 | buildSettings = { 176 | ALWAYS_SEARCH_USER_PATHS = NO; 177 | CLANG_ANALYZER_NONNULL = YES; 178 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 179 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 180 | CLANG_CXX_LIBRARY = "libc++"; 181 | CLANG_ENABLE_MODULES = YES; 182 | CLANG_ENABLE_OBJC_ARC = YES; 183 | CLANG_ENABLE_OBJC_WEAK = YES; 184 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 185 | CLANG_WARN_BOOL_CONVERSION = YES; 186 | CLANG_WARN_COMMA = YES; 187 | CLANG_WARN_CONSTANT_CONVERSION = YES; 188 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 189 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 190 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 191 | CLANG_WARN_EMPTY_BODY = YES; 192 | CLANG_WARN_ENUM_CONVERSION = YES; 193 | CLANG_WARN_INFINITE_RECURSION = YES; 194 | CLANG_WARN_INT_CONVERSION = YES; 195 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 196 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 197 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 199 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 200 | CLANG_WARN_STRICT_PROTOTYPES = YES; 201 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 202 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 203 | CLANG_WARN_UNREACHABLE_CODE = YES; 204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 205 | CODE_SIGN_IDENTITY = "iPhone Developer"; 206 | COPY_PHASE_STRIP = NO; 207 | DEBUG_INFORMATION_FORMAT = dwarf; 208 | ENABLE_STRICT_OBJC_MSGSEND = YES; 209 | ENABLE_TESTABILITY = YES; 210 | GCC_C_LANGUAGE_STANDARD = gnu11; 211 | GCC_DYNAMIC_NO_PIC = NO; 212 | GCC_NO_COMMON_BLOCKS = YES; 213 | GCC_OPTIMIZATION_LEVEL = 0; 214 | GCC_PREPROCESSOR_DEFINITIONS = ( 215 | "DEBUG=1", 216 | "$(inherited)", 217 | ); 218 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 219 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 220 | GCC_WARN_UNDECLARED_SELECTOR = YES; 221 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 222 | GCC_WARN_UNUSED_FUNCTION = YES; 223 | GCC_WARN_UNUSED_VARIABLE = YES; 224 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 225 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 226 | MTL_FAST_MATH = YES; 227 | ONLY_ACTIVE_ARCH = YES; 228 | SDKROOT = iphoneos; 229 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 230 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 231 | }; 232 | name = Debug; 233 | }; 234 | 23D2DB4B21E9DDE9008D4C79 /* Release */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 240 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 241 | CLANG_CXX_LIBRARY = "libc++"; 242 | CLANG_ENABLE_MODULES = YES; 243 | CLANG_ENABLE_OBJC_ARC = YES; 244 | CLANG_ENABLE_OBJC_WEAK = YES; 245 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 246 | CLANG_WARN_BOOL_CONVERSION = YES; 247 | CLANG_WARN_COMMA = YES; 248 | CLANG_WARN_CONSTANT_CONVERSION = YES; 249 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 250 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 251 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INFINITE_RECURSION = YES; 255 | CLANG_WARN_INT_CONVERSION = YES; 256 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 258 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 259 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 260 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 261 | CLANG_WARN_STRICT_PROTOTYPES = YES; 262 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 263 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 264 | CLANG_WARN_UNREACHABLE_CODE = YES; 265 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 266 | CODE_SIGN_IDENTITY = "iPhone Developer"; 267 | COPY_PHASE_STRIP = NO; 268 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 269 | ENABLE_NS_ASSERTIONS = NO; 270 | ENABLE_STRICT_OBJC_MSGSEND = YES; 271 | GCC_C_LANGUAGE_STANDARD = gnu11; 272 | GCC_NO_COMMON_BLOCKS = YES; 273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 275 | GCC_WARN_UNDECLARED_SELECTOR = YES; 276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 277 | GCC_WARN_UNUSED_FUNCTION = YES; 278 | GCC_WARN_UNUSED_VARIABLE = YES; 279 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 280 | MTL_ENABLE_DEBUG_INFO = NO; 281 | MTL_FAST_MATH = YES; 282 | SDKROOT = iphoneos; 283 | SWIFT_COMPILATION_MODE = wholemodule; 284 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 285 | VALIDATE_PRODUCT = YES; 286 | }; 287 | name = Release; 288 | }; 289 | 23D2DB4D21E9DDE9008D4C79 /* Debug */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 293 | CODE_SIGN_STYLE = Automatic; 294 | DEVELOPMENT_TEAM = CQR28XF52U; 295 | INFOPLIST_FILE = DoubleSliderView/Info.plist; 296 | LD_RUNPATH_SEARCH_PATHS = ( 297 | "$(inherited)", 298 | "@executable_path/Frameworks", 299 | ); 300 | PRODUCT_BUNDLE_IDENTIFIER = personl.dk.DoubleSliderView; 301 | PRODUCT_NAME = "$(TARGET_NAME)"; 302 | SWIFT_VERSION = 4.2; 303 | TARGETED_DEVICE_FAMILY = "1,2"; 304 | }; 305 | name = Debug; 306 | }; 307 | 23D2DB4E21E9DDE9008D4C79 /* Release */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 311 | CODE_SIGN_STYLE = Automatic; 312 | DEVELOPMENT_TEAM = CQR28XF52U; 313 | INFOPLIST_FILE = DoubleSliderView/Info.plist; 314 | LD_RUNPATH_SEARCH_PATHS = ( 315 | "$(inherited)", 316 | "@executable_path/Frameworks", 317 | ); 318 | PRODUCT_BUNDLE_IDENTIFIER = personl.dk.DoubleSliderView; 319 | PRODUCT_NAME = "$(TARGET_NAME)"; 320 | SWIFT_VERSION = 4.2; 321 | TARGETED_DEVICE_FAMILY = "1,2"; 322 | }; 323 | name = Release; 324 | }; 325 | /* End XCBuildConfiguration section */ 326 | 327 | /* Begin XCConfigurationList section */ 328 | 23D2DB3521E9DDE8008D4C79 /* Build configuration list for PBXProject "DoubleSliderView" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | 23D2DB4A21E9DDE9008D4C79 /* Debug */, 332 | 23D2DB4B21E9DDE9008D4C79 /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | 23D2DB4C21E9DDE9008D4C79 /* Build configuration list for PBXNativeTarget "DoubleSliderView" */ = { 338 | isa = XCConfigurationList; 339 | buildConfigurations = ( 340 | 23D2DB4D21E9DDE9008D4C79 /* Debug */, 341 | 23D2DB4E21E9DDE9008D4C79 /* Release */, 342 | ); 343 | defaultConfigurationIsVisible = 0; 344 | defaultConfigurationName = Release; 345 | }; 346 | /* End XCConfigurationList section */ 347 | }; 348 | rootObject = 23D2DB3221E9DDE8008D4C79 /* Project object */; 349 | } 350 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DoubleSliderView 4 | // 5 | // Created by 杜奎 on 2019/1/12. 6 | // Copyright © 2019 DU. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/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 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/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 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/DoubleSliderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DPDoubleSliderView.swift 3 | // DatePlay 4 | // 5 | // Created by DU on 2018/11/19. 6 | // Copyright © 2018年 DU. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DoubleSliderView: UIView { 12 | //当前最小的值 13 | var curMinValue: CGFloat = 0 14 | //当前最大的值 15 | var curMaxValue: CGFloat = 0 16 | //是否需要动画 17 | var needAnimation = false 18 | //手势起手位置类型 0 未在按钮上 not on button ; 1 在左边按钮上 on left button ; 2 在右边按钮上 on right button ; 3 两者重叠 overlap 19 | var dragType: Int = 0 20 | //间隔大小 21 | var minInterval: CGFloat = 0 22 | private var minIntervalWidth: CGFloat = 0 23 | 24 | //左侧按钮的中心位置 left btn's center 25 | private var minCenter: CGPoint = CGPoint.zero 26 | //右侧按钮的中心位置 right btn's center 27 | private var maxCenter: CGPoint = CGPoint.zero 28 | 29 | private var marginCenterX: CGFloat = 0 30 | 31 | //滑块位置改变后的回调 isLeft 是否是左边 finish手势是否结束 32 | var sliderBtnLocationChangeBlock: ((_ isLeft: Bool, _ finish: Bool)->())? 33 | 34 | override init(frame: CGRect) { 35 | super.init(frame: frame) 36 | 37 | if self.height < 35 + 20 { 38 | self.height = 55 39 | } 40 | self.marginCenterX = 17.5 41 | self.createUI() 42 | } 43 | 44 | required init?(coder aDecoder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | private func createUI() { 49 | self.addSubview(self.minLineView) 50 | self.addSubview(self.midLineView) 51 | self.addSubview(self.maxLineView) 52 | self.addSubview(self.minSliderBtn) 53 | self.addSubview(self.maxSliderBtn) 54 | 55 | self.curMinValue = 0 56 | self.curMaxValue = 1 57 | 58 | self.minSliderBtn.centerY = self.height * 0.5 59 | self.maxSliderBtn.centerY = self.height * 0.5 60 | self.minSliderBtn.x = 0 61 | self.maxSliderBtn.right = self.width 62 | 63 | self.minLineView.centerY = self.height * 0.5 64 | self.midLineView.centerY = self.height * 0.5 65 | self.maxLineView.centerY = self.height * 0.5 66 | 67 | self.changeLineViewWidth() 68 | 69 | self.addGestureRecognizer(UIPanGestureRecognizer.init(target: self, action: #selector(sliderBtnPanAction(gesture:)))) 70 | } 71 | 72 | //MARK:- actions 73 | 74 | @objc func sliderBtnPanAction(gesture: UIPanGestureRecognizer) { 75 | let location = gesture.location(in: self) 76 | let point = gesture.translation(in: self) 77 | switch gesture.state { 78 | case .began: 79 | 80 | let minSliderFrame = CGRect.init(x: self.minSliderBtn.x - 10, y: self.minSliderBtn.y - 10, width: self.minSliderBtn.width + 20, height: self.minSliderBtn.height + 20) 81 | let maxSliderFrame = CGRect.init(x: self.maxSliderBtn.x - 10, y: self.maxSliderBtn.y - 10, width: self.maxSliderBtn.width + 20, height: self.maxSliderBtn.height + 20) 82 | 83 | let inMinSliderBtn = minSliderFrame.contains(location) 84 | let inMaxSliderBtn = maxSliderFrame.contains(location) 85 | if inMinSliderBtn && !inMaxSliderBtn { 86 | print("从左边开始触摸 start drag from left") 87 | self.dragType = 1 88 | }else if !inMinSliderBtn && inMaxSliderBtn { 89 | print("从右边开始触摸 start drag from right") 90 | self.dragType = 2 91 | }else if !inMaxSliderBtn && !inMinSliderBtn { 92 | print("没有触动到按钮 not on button") 93 | self.dragType = 0 94 | }else { 95 | let leftOffset = abs(location.x - self.minSliderBtn.centerX) 96 | let rightOffset = abs(location.x - self.maxSliderBtn.centerX) 97 | if leftOffset > rightOffset { 98 | print("挨着,往右边 start drag from right") 99 | self.dragType = 2 100 | }else if leftOffset < rightOffset { 101 | print("挨着,往左边 start drag from left") 102 | self.dragType = 1 103 | }else { 104 | print("正中间 overlap") 105 | self.dragType = 3 106 | } 107 | } 108 | if self.dragType == 1 { 109 | self.minCenter = self.minSliderBtn.center 110 | self.bringSubviewToFront(self.minSliderBtn) 111 | }else if self.dragType == 2 { 112 | self.maxCenter = self.maxSliderBtn.center 113 | self.bringSubviewToFront(self.maxSliderBtn) 114 | } 115 | if self.minInterval > 0 { 116 | self.minIntervalWidth = (self.width - self.marginCenterX * 2) * CGFloat(self.minInterval) 117 | } 118 | 119 | case .changed: 120 | if self.dragType == 3 { 121 | if point.x > 0 { 122 | self.dragType = 2 123 | self.maxCenter = self.maxSliderBtn.center 124 | self.bringSubviewToFront(self.maxSliderBtn) 125 | print("从中间往右 from center to right") 126 | }else if point.x < 0 { 127 | self.dragType = 1 128 | print("从中间往左 from center to left") 129 | self.minCenter = self.minSliderBtn.center 130 | self.bringSubviewToFront(self.minSliderBtn) 131 | } 132 | } 133 | if dragType != 0 && dragType != 3 { 134 | if self.dragType == 1 { 135 | self.minSliderBtn.center = CGPoint.init(x: self.minCenter.x + point.x, y: self.minCenter.y) 136 | if self.minSliderBtn.right > self.maxSliderBtn.right - self.minIntervalWidth { 137 | self.minSliderBtn.right = self.maxSliderBtn.right - minIntervalWidth 138 | }else { 139 | if self.minSliderBtn.centerX < self.marginCenterX { 140 | self.minSliderBtn.centerX = self.marginCenterX 141 | } 142 | if self.minSliderBtn.centerX > self.width - self.marginCenterX { 143 | self.minSliderBtn.centerX = self.width - self.marginCenterX 144 | } 145 | } 146 | self.changeLineViewWidth() 147 | self.changeValueFromLocation() 148 | if self.sliderBtnLocationChangeBlock != nil { 149 | self.sliderBtnLocationChangeBlock!(true, false) 150 | } 151 | }else { 152 | self.maxSliderBtn.center = CGPoint.init(x: self.maxCenter.x + point.x, y: self.maxCenter.y) 153 | if self.maxSliderBtn.x < self.minSliderBtn.x + self.minIntervalWidth { 154 | self.maxSliderBtn.x = self.minSliderBtn.x + self.minIntervalWidth 155 | }else { 156 | if self.maxSliderBtn.centerX < self.marginCenterX { 157 | self.maxSliderBtn.centerX = self.marginCenterX 158 | } 159 | if self.maxSliderBtn.centerX > self.width - self.marginCenterX { 160 | self.maxSliderBtn.centerX = self.width - self.marginCenterX 161 | } 162 | } 163 | self.changeLineViewWidth() 164 | self.changeValueFromLocation() 165 | if self.sliderBtnLocationChangeBlock != nil { 166 | self.sliderBtnLocationChangeBlock!(false, false) 167 | } 168 | } 169 | } 170 | case .ended: 171 | if self.dragType == 1 { 172 | self.changeValueFromLocation() 173 | if self.sliderBtnLocationChangeBlock != nil { 174 | self.sliderBtnLocationChangeBlock!(true, true) 175 | } 176 | }else if self.dragType == 2 { 177 | self.changeValueFromLocation() 178 | if self.sliderBtnLocationChangeBlock != nil { 179 | self.sliderBtnLocationChangeBlock!(false, true) 180 | } 181 | } 182 | //重置 reset 183 | self.dragType = 0 184 | default: 185 | break 186 | } 187 | } 188 | 189 | //改变值域的线宽 190 | private func changeLineViewWidth() { 191 | self.minLineView.width = self.minSliderBtn.centerX 192 | self.minLineView.x = 0 193 | 194 | self.maxLineView.width = self.width - self.maxSliderBtn.centerX 195 | self.maxLineView.right = self.width 196 | 197 | self.midLineView.width = self.maxSliderBtn.centerX - self.minSliderBtn.centerX 198 | self.midLineView.x = self.minLineView.right 199 | } 200 | //根据滑块位置改变当前最小和最大的值 201 | private func changeValueFromLocation() { 202 | let contentWidth: CGFloat = self.width - self.marginCenterX * 2 203 | self.curMinValue = (self.minSliderBtn.centerX - self.marginCenterX)/contentWidth 204 | self.curMaxValue = (self.maxSliderBtn.centerX - self.marginCenterX)/contentWidth 205 | } 206 | //根据当前最小和最大的值改变滑块位置 207 | func changeLocationFromValue() { 208 | let contentWidth: CGFloat = self.width - self.marginCenterX * 2 209 | 210 | if needAnimation { 211 | UIView.animate(withDuration: 0.2) { 212 | self.minSliderBtn.centerX = self.marginCenterX + self.curMinValue * contentWidth 213 | self.maxSliderBtn.centerX = self.marginCenterX + self.curMaxValue * contentWidth 214 | self.changeLineViewWidth() 215 | } 216 | }else { 217 | self.minSliderBtn.centerX = self.marginCenterX + self.curMinValue * contentWidth 218 | self.maxSliderBtn.centerX = self.marginCenterX + self.curMaxValue * contentWidth 219 | self.changeLineViewWidth() 220 | } 221 | if self.curMinValue == self.curMaxValue { 222 | if self.curMaxValue == 0 { 223 | self.bringSubviewToFront(self.maxSliderBtn) 224 | }else { 225 | self.bringSubviewToFront(self.minSliderBtn) 226 | } 227 | } 228 | } 229 | 230 | //MARK:- setter & getter 231 | 232 | var minTintColor: UIColor? { 233 | didSet { 234 | if minTintColor != nil { 235 | self.minLineView.backgroundColor = minTintColor! 236 | } 237 | } 238 | } 239 | var maxTintColor: UIColor? { 240 | didSet { 241 | if maxTintColor != nil { 242 | self.maxLineView.backgroundColor = maxTintColor! 243 | } 244 | } 245 | } 246 | var midTintColor: UIColor? { 247 | didSet { 248 | if midTintColor != nil { 249 | self.midLineView.backgroundColor = midTintColor! 250 | } 251 | } 252 | } 253 | 254 | private lazy var minSliderBtn: UIButton = { 255 | let btn = UIButton.init(type: UIButton.ButtonType.custom) 256 | btn.size = CGSize.init(width: 35, height: 35) 257 | btn.backgroundColor = UIColor.white 258 | btn.layer.cornerRadius = 17.5 259 | btn.layer.shadowColor = UIColor.black.cgColor 260 | btn.layer.shadowOffset = CGSize.init(width: 0, height: 1) 261 | btn.layer.shadowOpacity = Float(0.15) 262 | btn.layer.shadowRadius = 5 263 | // btn.showsTouchWhenHighlighted = true 264 | btn.isUserInteractionEnabled = false 265 | return btn 266 | }() 267 | 268 | private lazy var maxSliderBtn: UIButton = { 269 | let btn = UIButton.init(type: UIButton.ButtonType.custom) 270 | btn.size = CGSize.init(width: 35, height: 35) 271 | btn.backgroundColor = UIColor.white 272 | btn.layer.cornerRadius = 17.5 273 | btn.layer.shadowColor = UIColor.black.cgColor 274 | btn.layer.shadowOffset = CGSize.init(width: 0, height: 1) 275 | btn.layer.shadowOpacity = Float(0.15) 276 | btn.layer.shadowRadius = 5 277 | // btn.showsTouchWhenHighlighted = true 278 | btn.isUserInteractionEnabled = false 279 | return btn 280 | }() 281 | 282 | private lazy var minLineView: UIView = { 283 | let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 5)) 284 | view.backgroundColor = UIColor.init(red: 162.0/255.0, green: 141.0/255.0, blue: 255.0/255.0, alpha: 0.2) 285 | view.isUserInteractionEnabled = false 286 | return view 287 | }() 288 | private lazy var maxLineView: UIView = { 289 | let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 5)) 290 | view.backgroundColor = UIColor.init(red: 162.0/255.0, green: 141.0/255.0, blue: 255.0/255.0, alpha: 0.2) 291 | view.isUserInteractionEnabled = false 292 | return view 293 | }() 294 | private lazy var midLineView: UIView = { 295 | let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 5)) 296 | view.backgroundColor = UIColor.init(red: 162.0/255.0, green: 141.0/255.0, blue: 255.0/255.0, alpha: 1.0) 297 | view.isUserInteractionEnabled = false 298 | return view 299 | }() 300 | } 301 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.swift 3 | // ImitationOfTodayNews 4 | // 5 | // Created by DU on 2017/5/24. 6 | // Copyright © 2017年 DU. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | enum OscillatoryAnimationType { 13 | case bigger 14 | case smaller 15 | } 16 | 17 | extension UIView{ 18 | var x : CGFloat { 19 | get { 20 | return frame.origin.x 21 | } 22 | set { 23 | var tempFrame : CGRect = frame 24 | tempFrame.origin.x = newValue 25 | frame = tempFrame 26 | } 27 | } 28 | 29 | var y : CGFloat { 30 | get { 31 | return frame.origin.y 32 | } 33 | set { 34 | var tempFrame : CGRect = frame 35 | tempFrame.origin.y = newValue 36 | frame = tempFrame 37 | } 38 | } 39 | 40 | var width : CGFloat { 41 | get { 42 | return frame.size.width 43 | } 44 | set { 45 | var tempFrame : CGRect = frame 46 | tempFrame.size.width = newValue 47 | frame = tempFrame 48 | } 49 | } 50 | 51 | var height : CGFloat { 52 | get { 53 | return frame.size.height 54 | } 55 | set { 56 | var tempFrame : CGRect = frame 57 | tempFrame.size.height = newValue 58 | frame = tempFrame 59 | } 60 | } 61 | 62 | var centerX : CGFloat { 63 | get { 64 | return center.x 65 | } 66 | set { 67 | var tempCenter : CGPoint = center 68 | tempCenter.x = newValue 69 | center = tempCenter 70 | } 71 | } 72 | var centerY : CGFloat { 73 | get { 74 | return center.y 75 | } 76 | set { 77 | var tempCenter : CGPoint = center 78 | tempCenter.y = newValue 79 | center = tempCenter 80 | } 81 | } 82 | var size : CGSize { 83 | get { 84 | return frame.size 85 | } 86 | set { 87 | var tempFrame : CGRect = frame 88 | tempFrame.size = newValue 89 | frame = tempFrame 90 | } 91 | } 92 | 93 | var right : CGFloat { 94 | get { 95 | return frame.origin.x + frame.size.width 96 | } 97 | set { 98 | var tempFrame : CGRect = frame 99 | tempFrame.origin.x = newValue - frame.size.width 100 | frame = tempFrame 101 | } 102 | } 103 | 104 | var bottom : CGFloat { 105 | get { 106 | return frame.origin.y + frame.size.height 107 | } 108 | set { 109 | var tempFrame : CGRect = frame 110 | tempFrame.origin.y = newValue - frame.size.height 111 | frame = tempFrame 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /DoubleSliderView/DoubleSliderView/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DoubleSliderView 4 | // 5 | // Created by 杜奎 on 2019/1/12. 6 | // Copyright © 2019 DU. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | var minAge: Int = 12 14 | var maxAge: Int = 35 15 | var curMinAge: Int = 12 16 | var curMaxAge: Int = 35 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | self.view.backgroundColor = UIColor.white 22 | 23 | self.view.addSubview(self.ageLabel) 24 | self.view.addSubview(self.ageTipsLabel) 25 | self.view.addSubview(self.doubleSliderView) 26 | 27 | self.ageLabel.centerY = 156 28 | self.ageLabel.x = 52 29 | 30 | self.ageTipsLabel.centerY = self.ageLabel.centerY 31 | self.ageTipsLabel.x = self.ageLabel.right + 7 32 | 33 | self.doubleSliderView.x = 52 34 | self.doubleSliderView.y = 185 - 10 35 | 36 | // Do any additional setup after loading the view, typically from a nib. 37 | } 38 | 39 | //MARK: - private func 40 | //根据值获取整数 41 | private func fetchInt(from value: CGFloat) -> CGFloat { 42 | var newValue: CGFloat = floor(value) 43 | let changeValue = value - newValue 44 | if changeValue >= 0.5 { 45 | newValue = newValue + 1 46 | } 47 | return newValue 48 | } 49 | 50 | private func sliderValueChangeAction(isLeft: Bool, finish: Bool) { 51 | if isLeft { 52 | let age = CGFloat(self.maxAge - self.minAge) * self.doubleSliderView.curMinValue 53 | let tmpAge = self.fetchInt(from: age) 54 | self.curMinAge = Int(tmpAge) + self.minAge 55 | self.changeAgeTipsText() 56 | }else { 57 | let age = CGFloat(self.maxAge - self.minAge) * self.doubleSliderView.curMaxValue 58 | let tmpAge = self.fetchInt(from: age) 59 | self.curMaxAge = Int(tmpAge) + self.minAge 60 | self.changeAgeTipsText() 61 | } 62 | if finish { 63 | self.changeSliderValue() 64 | } 65 | } 66 | //值取整后可能改变了原始的大小,所以需要重新改变滑块的位置 67 | private func changeSliderValue() { 68 | let finishMinValue = CGFloat(self.curMinAge - self.minAge)/CGFloat(self.maxAge - self.minAge) 69 | let finishMaxValue = CGFloat(self.curMaxAge - self.minAge)/CGFloat(self.maxAge - self.minAge) 70 | self.doubleSliderView.curMinValue = finishMinValue 71 | self.doubleSliderView.curMaxValue = finishMaxValue 72 | self.doubleSliderView.changeLocationFromValue() 73 | } 74 | 75 | private func changeAgeTipsText() { 76 | if self.curMinAge == self.curMaxAge { 77 | self.ageTipsLabel.text = "\(self.curMinAge)岁" 78 | }else { 79 | self.ageTipsLabel.text = "\(self.curMinAge)~\(self.curMaxAge)岁" 80 | } 81 | self.ageTipsLabel.sizeToFit() 82 | self.ageTipsLabel.centerY = self.ageLabel.centerY 83 | self.ageTipsLabel.x = self.ageLabel.right + 7 84 | } 85 | 86 | //MARK:- setter & getter 87 | 88 | private lazy var ageLabel: UILabel = { 89 | let label = UILabel.init() 90 | label.textColor = UIColor.black 91 | label.font = UIFont.systemFont(ofSize: 15, weight: .medium) 92 | label.text = "年龄 age" 93 | label.sizeToFit() 94 | return label 95 | }() 96 | 97 | private lazy var ageTipsLabel: UILabel = { 98 | let label = UILabel.init() 99 | label.textColor = UIColor.black 100 | label.font = UIFont.systemFont(ofSize: 15, weight: .medium) 101 | label.text = "\(self.minAge)~\(self.maxAge)岁" 102 | label.sizeToFit() 103 | return label 104 | }() 105 | 106 | private lazy var doubleSliderView: DoubleSliderView = { 107 | let view = DoubleSliderView.init(frame: CGRect.init(x: 0, y: 0, width: self.view.width - 52 * 2, height: 35 + 20)) 108 | view.needAnimation = true 109 | // if self.maxAge > self.minAge { 110 | // view.minInterval = 4.0/CGFloat(self.maxAge - self.minAge) 111 | // } 112 | view.sliderBtnLocationChangeBlock = { [weak self] isLeft,finish in 113 | self?.sliderValueChangeAction(isLeft: isLeft, finish: finish) 114 | } 115 | return view 116 | }() 117 | 118 | } 119 | 120 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 DU 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DoubleSliderView-swift 2 | 双向滑动,滑块可以重叠, 左边滑块不能移动到右边滑块的右边。Two slider can overlap, and the left slider can not be moved to right of the right slider 3 | 4 | 5 | [OC版本](https://github.com/anonymity-du/DoubleSliderView-Objective-C) 6 | 7 | 示例 8 | 9 | ![image](https://github.com/anonymity-du/DoubleSliderView-swift/blob/master/imageFolder/doubleslider_1.gif) 10 | 11 | 12 | 增加了最小间隙 13 | 14 | ![image](https://github.com/anonymity-du/DoubleSliderView-swift/blob/master/imageFolder/doubleslider_2.gif) 15 | -------------------------------------------------------------------------------- /imageFolder/doubleslider_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anonymity-du/DoubleSliderView-swift/a1fd5cf055996cd98859caa63d24da7101dddd40/imageFolder/doubleslider_1.gif -------------------------------------------------------------------------------- /imageFolder/doubleslider_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anonymity-du/DoubleSliderView-swift/a1fd5cf055996cd98859caa63d24da7101dddd40/imageFolder/doubleslider_2.gif --------------------------------------------------------------------------------