├── CustomRulerView ├── CustomRulerView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── apple.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── apple.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist └── CustomRulerView │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── View │ ├── RulerCollectionViewCell.h │ ├── RulerCollectionViewCell.m │ ├── RulerLayout.h │ ├── RulerLayout.m │ ├── RulerView.h │ └── RulerView.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── README.md /CustomRulerView/CustomRulerView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 185E27BD2109634D00059D18 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 185E27BC2109634D00059D18 /* AppDelegate.m */; }; 11 | 185E27C02109634D00059D18 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 185E27BF2109634D00059D18 /* ViewController.m */; }; 12 | 185E27C32109634D00059D18 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 185E27C12109634D00059D18 /* Main.storyboard */; }; 13 | 185E27C52109634F00059D18 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 185E27C42109634F00059D18 /* Assets.xcassets */; }; 14 | 185E27C82109634F00059D18 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 185E27C62109634F00059D18 /* LaunchScreen.storyboard */; }; 15 | 185E27CB2109634F00059D18 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 185E27CA2109634F00059D18 /* main.m */; }; 16 | 185E27D8210963EA00059D18 /* RulerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 185E27D4210963EA00059D18 /* RulerView.m */; }; 17 | 185E27D9210963EA00059D18 /* RulerLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 185E27D5210963EA00059D18 /* RulerLayout.m */; }; 18 | 185E27DA210963EA00059D18 /* RulerCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 185E27D7210963EA00059D18 /* RulerCollectionViewCell.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 185E27B82109634D00059D18 /* CustomRulerView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CustomRulerView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 185E27BB2109634D00059D18 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 24 | 185E27BC2109634D00059D18 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 25 | 185E27BE2109634D00059D18 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 26 | 185E27BF2109634D00059D18 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 27 | 185E27C22109634D00059D18 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 185E27C42109634F00059D18 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | 185E27C72109634F00059D18 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | 185E27C92109634F00059D18 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 185E27CA2109634F00059D18 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 185E27D2210963EA00059D18 /* RulerLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RulerLayout.h; sourceTree = ""; }; 33 | 185E27D3210963EA00059D18 /* RulerCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RulerCollectionViewCell.h; sourceTree = ""; }; 34 | 185E27D4210963EA00059D18 /* RulerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RulerView.m; sourceTree = ""; }; 35 | 185E27D5210963EA00059D18 /* RulerLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RulerLayout.m; sourceTree = ""; }; 36 | 185E27D6210963EA00059D18 /* RulerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RulerView.h; sourceTree = ""; }; 37 | 185E27D7210963EA00059D18 /* RulerCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RulerCollectionViewCell.m; sourceTree = ""; }; 38 | /* End PBXFileReference section */ 39 | 40 | /* Begin PBXFrameworksBuildPhase section */ 41 | 185E27B52109634D00059D18 /* Frameworks */ = { 42 | isa = PBXFrameworksBuildPhase; 43 | buildActionMask = 2147483647; 44 | files = ( 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | 185E27AF2109634D00059D18 = { 52 | isa = PBXGroup; 53 | children = ( 54 | 185E27BA2109634D00059D18 /* CustomRulerView */, 55 | 185E27B92109634D00059D18 /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | 185E27B92109634D00059D18 /* Products */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 185E27B82109634D00059D18 /* CustomRulerView.app */, 63 | ); 64 | name = Products; 65 | sourceTree = ""; 66 | }; 67 | 185E27BA2109634D00059D18 /* CustomRulerView */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 185E27D1210963EA00059D18 /* View */, 71 | 185E27BB2109634D00059D18 /* AppDelegate.h */, 72 | 185E27BC2109634D00059D18 /* AppDelegate.m */, 73 | 185E27BE2109634D00059D18 /* ViewController.h */, 74 | 185E27BF2109634D00059D18 /* ViewController.m */, 75 | 185E27C12109634D00059D18 /* Main.storyboard */, 76 | 185E27C42109634F00059D18 /* Assets.xcassets */, 77 | 185E27C62109634F00059D18 /* LaunchScreen.storyboard */, 78 | 185E27C92109634F00059D18 /* Info.plist */, 79 | 185E27CA2109634F00059D18 /* main.m */, 80 | ); 81 | path = CustomRulerView; 82 | sourceTree = ""; 83 | }; 84 | 185E27D1210963EA00059D18 /* View */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 185E27D6210963EA00059D18 /* RulerView.h */, 88 | 185E27D4210963EA00059D18 /* RulerView.m */, 89 | 185E27D3210963EA00059D18 /* RulerCollectionViewCell.h */, 90 | 185E27D7210963EA00059D18 /* RulerCollectionViewCell.m */, 91 | 185E27D2210963EA00059D18 /* RulerLayout.h */, 92 | 185E27D5210963EA00059D18 /* RulerLayout.m */, 93 | ); 94 | path = View; 95 | sourceTree = ""; 96 | }; 97 | /* End PBXGroup section */ 98 | 99 | /* Begin PBXNativeTarget section */ 100 | 185E27B72109634D00059D18 /* CustomRulerView */ = { 101 | isa = PBXNativeTarget; 102 | buildConfigurationList = 185E27CE2109634F00059D18 /* Build configuration list for PBXNativeTarget "CustomRulerView" */; 103 | buildPhases = ( 104 | 185E27B42109634D00059D18 /* Sources */, 105 | 185E27B52109634D00059D18 /* Frameworks */, 106 | 185E27B62109634D00059D18 /* Resources */, 107 | ); 108 | buildRules = ( 109 | ); 110 | dependencies = ( 111 | ); 112 | name = CustomRulerView; 113 | productName = CustomRulerView; 114 | productReference = 185E27B82109634D00059D18 /* CustomRulerView.app */; 115 | productType = "com.apple.product-type.application"; 116 | }; 117 | /* End PBXNativeTarget section */ 118 | 119 | /* Begin PBXProject section */ 120 | 185E27B02109634D00059D18 /* Project object */ = { 121 | isa = PBXProject; 122 | attributes = { 123 | LastUpgradeCheck = 0940; 124 | ORGANIZATIONNAME = apple; 125 | TargetAttributes = { 126 | 185E27B72109634D00059D18 = { 127 | CreatedOnToolsVersion = 9.4.1; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = 185E27B32109634D00059D18 /* Build configuration list for PBXProject "CustomRulerView" */; 132 | compatibilityVersion = "Xcode 9.3"; 133 | developmentRegion = en; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = 185E27AF2109634D00059D18; 140 | productRefGroup = 185E27B92109634D00059D18 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | 185E27B72109634D00059D18 /* CustomRulerView */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | 185E27B62109634D00059D18 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 185E27C82109634F00059D18 /* LaunchScreen.storyboard in Resources */, 155 | 185E27C52109634F00059D18 /* Assets.xcassets in Resources */, 156 | 185E27C32109634D00059D18 /* Main.storyboard in Resources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXResourcesBuildPhase section */ 161 | 162 | /* Begin PBXSourcesBuildPhase section */ 163 | 185E27B42109634D00059D18 /* Sources */ = { 164 | isa = PBXSourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | 185E27C02109634D00059D18 /* ViewController.m in Sources */, 168 | 185E27CB2109634F00059D18 /* main.m in Sources */, 169 | 185E27D9210963EA00059D18 /* RulerLayout.m in Sources */, 170 | 185E27DA210963EA00059D18 /* RulerCollectionViewCell.m in Sources */, 171 | 185E27BD2109634D00059D18 /* AppDelegate.m in Sources */, 172 | 185E27D8210963EA00059D18 /* RulerView.m in Sources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXSourcesBuildPhase section */ 177 | 178 | /* Begin PBXVariantGroup section */ 179 | 185E27C12109634D00059D18 /* Main.storyboard */ = { 180 | isa = PBXVariantGroup; 181 | children = ( 182 | 185E27C22109634D00059D18 /* Base */, 183 | ); 184 | name = Main.storyboard; 185 | sourceTree = ""; 186 | }; 187 | 185E27C62109634F00059D18 /* LaunchScreen.storyboard */ = { 188 | isa = PBXVariantGroup; 189 | children = ( 190 | 185E27C72109634F00059D18 /* Base */, 191 | ); 192 | name = LaunchScreen.storyboard; 193 | sourceTree = ""; 194 | }; 195 | /* End PBXVariantGroup section */ 196 | 197 | /* Begin XCBuildConfiguration section */ 198 | 185E27CC2109634F00059D18 /* Debug */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ALWAYS_SEARCH_USER_PATHS = NO; 202 | CLANG_ANALYZER_NONNULL = YES; 203 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 204 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 205 | CLANG_CXX_LIBRARY = "libc++"; 206 | CLANG_ENABLE_MODULES = YES; 207 | CLANG_ENABLE_OBJC_ARC = YES; 208 | CLANG_ENABLE_OBJC_WEAK = YES; 209 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 210 | CLANG_WARN_BOOL_CONVERSION = YES; 211 | CLANG_WARN_COMMA = YES; 212 | CLANG_WARN_CONSTANT_CONVERSION = YES; 213 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 216 | CLANG_WARN_EMPTY_BODY = YES; 217 | CLANG_WARN_ENUM_CONVERSION = YES; 218 | CLANG_WARN_INFINITE_RECURSION = YES; 219 | CLANG_WARN_INT_CONVERSION = YES; 220 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 221 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 222 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 223 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 224 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 225 | CLANG_WARN_STRICT_PROTOTYPES = YES; 226 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 227 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 228 | CLANG_WARN_UNREACHABLE_CODE = YES; 229 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 230 | CODE_SIGN_IDENTITY = "iPhone Developer"; 231 | COPY_PHASE_STRIP = NO; 232 | DEBUG_INFORMATION_FORMAT = dwarf; 233 | ENABLE_STRICT_OBJC_MSGSEND = YES; 234 | ENABLE_TESTABILITY = YES; 235 | GCC_C_LANGUAGE_STANDARD = gnu11; 236 | GCC_DYNAMIC_NO_PIC = NO; 237 | GCC_NO_COMMON_BLOCKS = YES; 238 | GCC_OPTIMIZATION_LEVEL = 0; 239 | GCC_PREPROCESSOR_DEFINITIONS = ( 240 | "DEBUG=1", 241 | "$(inherited)", 242 | ); 243 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 244 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 245 | GCC_WARN_UNDECLARED_SELECTOR = YES; 246 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 247 | GCC_WARN_UNUSED_FUNCTION = YES; 248 | GCC_WARN_UNUSED_VARIABLE = YES; 249 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 250 | MTL_ENABLE_DEBUG_INFO = YES; 251 | ONLY_ACTIVE_ARCH = YES; 252 | SDKROOT = iphoneos; 253 | }; 254 | name = Debug; 255 | }; 256 | 185E27CD2109634F00059D18 /* Release */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ALWAYS_SEARCH_USER_PATHS = NO; 260 | CLANG_ANALYZER_NONNULL = YES; 261 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 262 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 263 | CLANG_CXX_LIBRARY = "libc++"; 264 | CLANG_ENABLE_MODULES = YES; 265 | CLANG_ENABLE_OBJC_ARC = YES; 266 | CLANG_ENABLE_OBJC_WEAK = YES; 267 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 268 | CLANG_WARN_BOOL_CONVERSION = YES; 269 | CLANG_WARN_COMMA = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 274 | CLANG_WARN_EMPTY_BODY = YES; 275 | CLANG_WARN_ENUM_CONVERSION = YES; 276 | CLANG_WARN_INFINITE_RECURSION = YES; 277 | CLANG_WARN_INT_CONVERSION = YES; 278 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 280 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 281 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 282 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 283 | CLANG_WARN_STRICT_PROTOTYPES = YES; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 286 | CLANG_WARN_UNREACHABLE_CODE = YES; 287 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 288 | CODE_SIGN_IDENTITY = "iPhone Developer"; 289 | COPY_PHASE_STRIP = NO; 290 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 291 | ENABLE_NS_ASSERTIONS = NO; 292 | ENABLE_STRICT_OBJC_MSGSEND = YES; 293 | GCC_C_LANGUAGE_STANDARD = gnu11; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 296 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 297 | GCC_WARN_UNDECLARED_SELECTOR = YES; 298 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 299 | GCC_WARN_UNUSED_FUNCTION = YES; 300 | GCC_WARN_UNUSED_VARIABLE = YES; 301 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 302 | MTL_ENABLE_DEBUG_INFO = NO; 303 | SDKROOT = iphoneos; 304 | VALIDATE_PRODUCT = YES; 305 | }; 306 | name = Release; 307 | }; 308 | 185E27CF2109634F00059D18 /* Debug */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 312 | CODE_SIGN_IDENTITY = "iPhone Developer"; 313 | CODE_SIGN_STYLE = Manual; 314 | DEVELOPMENT_TEAM = RNL7SY23B3; 315 | INFOPLIST_FILE = CustomRulerView/Info.plist; 316 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 317 | LD_RUNPATH_SEARCH_PATHS = ( 318 | "$(inherited)", 319 | "@executable_path/Frameworks", 320 | ); 321 | PRODUCT_BUNDLE_IDENTIFIER = com.icarbonx.healthbuddy.qiye; 322 | PRODUCT_NAME = "$(TARGET_NAME)"; 323 | PROVISIONING_PROFILE_SPECIFIER = GeneralDevProfile; 324 | TARGETED_DEVICE_FAMILY = "1,2"; 325 | }; 326 | name = Debug; 327 | }; 328 | 185E27D02109634F00059D18 /* Release */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 332 | CODE_SIGN_IDENTITY = "iPhone Developer"; 333 | CODE_SIGN_STYLE = Manual; 334 | DEVELOPMENT_TEAM = RNL7SY23B3; 335 | INFOPLIST_FILE = CustomRulerView/Info.plist; 336 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 337 | LD_RUNPATH_SEARCH_PATHS = ( 338 | "$(inherited)", 339 | "@executable_path/Frameworks", 340 | ); 341 | PRODUCT_BUNDLE_IDENTIFIER = com.icarbonx.healthbuddy.qiye; 342 | PRODUCT_NAME = "$(TARGET_NAME)"; 343 | PROVISIONING_PROFILE_SPECIFIER = GeneralDevProfile; 344 | TARGETED_DEVICE_FAMILY = "1,2"; 345 | }; 346 | name = Release; 347 | }; 348 | /* End XCBuildConfiguration section */ 349 | 350 | /* Begin XCConfigurationList section */ 351 | 185E27B32109634D00059D18 /* Build configuration list for PBXProject "CustomRulerView" */ = { 352 | isa = XCConfigurationList; 353 | buildConfigurations = ( 354 | 185E27CC2109634F00059D18 /* Debug */, 355 | 185E27CD2109634F00059D18 /* Release */, 356 | ); 357 | defaultConfigurationIsVisible = 0; 358 | defaultConfigurationName = Release; 359 | }; 360 | 185E27CE2109634F00059D18 /* Build configuration list for PBXNativeTarget "CustomRulerView" */ = { 361 | isa = XCConfigurationList; 362 | buildConfigurations = ( 363 | 185E27CF2109634F00059D18 /* Debug */, 364 | 185E27D02109634F00059D18 /* Release */, 365 | ); 366 | defaultConfigurationIsVisible = 0; 367 | defaultConfigurationName = Release; 368 | }; 369 | /* End XCConfigurationList section */ 370 | }; 371 | rootObject = 185E27B02109634D00059D18 /* Project object */; 372 | } 373 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView.xcodeproj/project.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/concerned123/CustomRulerView/9ddf47a09978c40251dda21fdd72c0580eca1079/CustomRulerView/CustomRulerView.xcodeproj/project.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView.xcodeproj/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CustomRulerView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | CustomRulerView.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CustomRulerView 4 | // 5 | // Created by apple on 2018/7/26. 6 | // Copyright © 2018年 apple. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CustomRulerView 4 | // 5 | // Created by apple on 2018/7/26. 6 | // Copyright © 2018年 apple. 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 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/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 | } -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/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 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/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 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/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 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/View/RulerCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // RulerCollectionViewCell.h 3 | // TYFitFore 4 | // 5 | // Created by apple on 2018/7/5. 6 | // Copyright © 2018年 tangpeng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RulerConfig; 12 | 13 | #define UIColorFromHex(s) [UIColor colorWithRed:(((s & 0xFF0000) >> 16 )) / 255.0 green:((( s & 0xFF00 ) >> 8 )) / 255.0 blue:(( s & 0xFF )) / 255.0 alpha:1.0] 14 | #define SCREEN_WIDTH_RULER ([[UIScreen mainScreen] bounds].size.width) 15 | #define SCREEN_HEIGHT_RULER ([[UIScreen mainScreen] bounds].size.height) 16 | 17 | typedef NS_ENUM(NSInteger, RulerNumberDirection) { 18 | numberTop = 0, //水平方向:数字在上,刻度尺在下 19 | numberBottom, //水平方向:数字在下,刻度尺在上 20 | numberLeft, //垂直方向:数字在左,刻度尺在右 21 | numberRight //垂直方向:数字在右,刻度尺在左 22 | }; 23 | 24 | @interface RulerCollectionViewCell : UICollectionViewCell 25 | 26 | @property (nonatomic, strong) RulerConfig *rulerConfig; 27 | @property (nonatomic, assign) NSInteger index; /**< cell下标 */ 28 | 29 | /** 选中当前cell */ 30 | - (void)makeCellSelect; 31 | /** 隐藏当前cell的文字 */ 32 | - (void)makeCellHiddenText; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/View/RulerCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // RulerCollectionViewCell.m 3 | // TYFitFore 4 | // 5 | // Created by apple on 2018/7/5. 6 | // Copyright © 2018年 tangpeng. All rights reserved. 7 | // 8 | 9 | #import "RulerCollectionViewCell.h" 10 | #import "RulerView.h" 11 | 12 | #define kNumberTop (self.rulerConfig.numberDirection == numberTop) 13 | #define kNumberBottom (self.rulerConfig.numberDirection == numberBottom) 14 | #define kNumberLeft (self.rulerConfig.numberDirection == numberLeft) 15 | #define kNumberRight (self.rulerConfig.numberDirection == numberRight) 16 | #define kHorizontalCell (kNumberTop || kNumberBottom) 17 | 18 | @interface RulerCollectionViewCell () 19 | 20 | @property (nonatomic, strong) UIImageView *ruleImageView; 21 | @property (nonatomic, strong) CATextLayer *textLayer; 22 | @property (nonatomic, strong) CATextLayer *selectTextLayer; 23 | 24 | @end 25 | 26 | @implementation RulerCollectionViewCell 27 | 28 | - (instancetype)initWithFrame:(CGRect)frame { 29 | if (self = [super initWithFrame:frame]) { 30 | [self.contentView addSubview:self.ruleImageView]; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)layoutSubviews { 36 | [super layoutSubviews]; 37 | 38 | if (self.index % 10 == 0) { 39 | NSString *text = @""; 40 | if (self.rulerConfig.isDecimal) { 41 | NSInteger showIndex = self.index/10 + self.rulerConfig.min; 42 | if (self.rulerConfig.reverse) { 43 | showIndex = self.rulerConfig.max - showIndex + self.rulerConfig.min; 44 | text = [NSString stringWithFormat:@"%ld", showIndex]; 45 | } else { 46 | text = [NSString stringWithFormat:@"%ld", self.index/10 + self.rulerConfig.min]; 47 | } 48 | } else { 49 | NSInteger showIndex = self.index + self.rulerConfig.min; 50 | if (self.rulerConfig.reverse) { 51 | text = [NSString stringWithFormat:@"%ld", (long)(self.rulerConfig.max - showIndex + self.rulerConfig.min)]; 52 | } else { 53 | text = [NSString stringWithFormat:@"%ld", (long)showIndex]; 54 | } 55 | } 56 | 57 | //字体 (采用当前最大值的位数显示字体) 58 | CGSize size = [[self maxString] boundingRectWithSize:CGSizeMake(SCREEN_WIDTH_RULER, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: self.rulerConfig.numberFont} context:nil].size; 59 | 60 | if (kHorizontalCell) { 61 | //水平方向 62 | CGFloat startY = 0; 63 | if (kNumberTop) { 64 | //数字在上面,刻度尺在下方 65 | startY = self.rulerConfig.shortScaleStart - self.rulerConfig.distanceFromScaleToNumber - size.height; 66 | } else if (kNumberBottom) { 67 | //数字在下面,刻度尺在上方 68 | startY = self.rulerConfig.shortScaleStart + self.rulerConfig.shortScaleLength + self.rulerConfig.distanceFromScaleToNumber; 69 | } 70 | self.textLayer.frame = CGRectMake((CGRectGetWidth(self.contentView.frame) - size.width)/2.0, startY, size.width, size.height); 71 | } else { 72 | //垂直方向 73 | CGFloat startX = 0; 74 | if (kNumberLeft) { 75 | //数字在左边,刻度尺在右边 76 | startX = self.rulerConfig.shortScaleStart - self.rulerConfig.distanceFromScaleToNumber - size.width; 77 | } else if (kNumberRight) { 78 | //数字在右边,刻度尺在左边 79 | startX = self.rulerConfig.shortScaleStart + self.rulerConfig.shortScaleLength + self.rulerConfig.distanceFromScaleToNumber; 80 | } 81 | self.textLayer.frame = CGRectMake(startX, (CGRectGetHeight(self.contentView.frame) - size.height)/2.0, size.width, size.height); 82 | } 83 | 84 | self.textLayer.string = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: self.rulerConfig.numberFont, NSForegroundColorAttributeName: self.rulerConfig.numberColor}]; 85 | self.textLayer.actions = @{@"contents": [NSNull null]}; 86 | if (!self.textLayer.superlayer) { 87 | [self.contentView.layer addSublayer:self.textLayer]; 88 | } 89 | } else { 90 | self.textLayer.string = nil; 91 | } 92 | 93 | self.selectTextLayer.string = nil; 94 | [self.selectTextLayer removeFromSuperlayer]; 95 | 96 | //刻度尺 97 | CGFloat length = ((self.index % 5 == 0) ? self.rulerConfig.longScaleLength : self.rulerConfig.shortScaleLength); 98 | CGFloat start = ((self.index % 5 == 0) ? self.rulerConfig.longScaleStart : self.rulerConfig.shortScaleStart); 99 | self.ruleImageView.frame = kHorizontalCell ? CGRectMake(0, start, self.rulerConfig.scaleWidth, length) : CGRectMake(start, 0, length, self.rulerConfig.scaleWidth); 100 | self.ruleImageView.layer.cornerRadius = self.rulerConfig.scaleWidth/2.0; 101 | self.ruleImageView.backgroundColor = self.rulerConfig.scaleColor; 102 | } 103 | 104 | #pragma mark - 选中当前cell 105 | - (void)makeCellSelect { 106 | 107 | self.selectTextLayer = nil; 108 | NSString *text = @""; 109 | if (self.rulerConfig.isDecimal) { 110 | double showIndex = self.index/10.0 + self.rulerConfig.min; 111 | if (self.rulerConfig.reverse) { 112 | showIndex = self.rulerConfig.max - showIndex + self.rulerConfig.min; 113 | text = [self notRounding:showIndex afterPoint:1]; 114 | } else { 115 | text = [self notRounding:self.index/10.0 + self.rulerConfig.min afterPoint:1]; 116 | } 117 | } else { 118 | NSInteger showIndex = self.index + self.rulerConfig.min; 119 | if (self.rulerConfig.reverse) { 120 | text = [NSString stringWithFormat:@"%ld", (long)(self.rulerConfig.max - showIndex + self.rulerConfig.min)]; 121 | } else { 122 | text = [NSString stringWithFormat:@"%ld", (long)showIndex]; 123 | } 124 | } 125 | CGSize size = [text boundingRectWithSize:CGSizeMake(SCREEN_WIDTH_RULER, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont fontWithName:@"PingFangSC-Semibold" size:18]} context:nil].size; 126 | 127 | if (kHorizontalCell) { 128 | //水平方向 129 | CGFloat startY = 0; 130 | if (kNumberTop) { 131 | //数字在上方,刻度尺在下方 132 | startY = self.rulerConfig.shortScaleStart - self.rulerConfig.distanceFromScaleToNumber - size.height - 12; 133 | } else if (kNumberBottom) { 134 | //数字在下方,刻度尺在上方 135 | startY = self.rulerConfig.shortScaleStart + self.rulerConfig.shortScaleLength + self.rulerConfig.distanceFromScaleToNumber + 12; 136 | } 137 | self.selectTextLayer.frame = CGRectMake((CGRectGetWidth(self.contentView.frame) - size.width)/2.0, startY, size.width, size.height); 138 | } else { 139 | //垂直方向 140 | CGFloat startX = 0; 141 | if (kNumberLeft) { 142 | //数字在左边,刻度尺在右边 143 | startX = self.rulerConfig.shortScaleStart - self.rulerConfig.distanceFromScaleToNumber - size.width - 12; 144 | } else if (kNumberRight) { 145 | //数字在右边,刻度尺在左边 146 | startX = self.rulerConfig.shortScaleStart + self.rulerConfig.shortScaleLength + self.rulerConfig.distanceFromScaleToNumber + 12; 147 | } 148 | self.selectTextLayer.frame = CGRectMake(startX, (CGRectGetHeight(self.contentView.frame) - size.height)/2.0, size.width, size.height); 149 | } 150 | 151 | self.selectTextLayer.string = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: [UIFont fontWithName:@"PingFangSC-Semibold" size:18], NSForegroundColorAttributeName: UIColorFromHex(0x20C6BA)}]; 152 | self.selectTextLayer.actions = @{@"contents": [NSNull null]}; 153 | [self.contentView.layer addSublayer:self.selectTextLayer]; 154 | self.textLayer.string = nil; 155 | } 156 | 157 | #pragma mark - 隐藏当前cell的文字 158 | - (void)makeCellHiddenText { 159 | self.textLayer.string = nil; 160 | } 161 | 162 | #pragma mark - getter 163 | - (UIImageView *)ruleImageView { 164 | if (!_ruleImageView) { 165 | _ruleImageView = [[UIImageView alloc] init]; 166 | } 167 | return _ruleImageView; 168 | } 169 | 170 | - (CATextLayer *)textLayer { 171 | if (!_textLayer) { 172 | _textLayer = [CATextLayer layer]; 173 | _textLayer.contentsScale = [UIScreen mainScreen].scale; 174 | _textLayer.alignmentMode = @"center"; 175 | } 176 | return _textLayer; 177 | } 178 | 179 | - (CATextLayer *)selectTextLayer { 180 | if (!_selectTextLayer) { 181 | _selectTextLayer = [CATextLayer layer]; 182 | _selectTextLayer.contentsScale = [UIScreen mainScreen].scale; 183 | } 184 | return _selectTextLayer; 185 | } 186 | 187 | #pragma mark - other 188 | /** 根据最大值,求出当前位数的最大值 */ 189 | - (NSString *)maxString { 190 | NSInteger num = self.rulerConfig.max; 191 | NSMutableString *maxNumberString = [NSMutableString string]; 192 | 193 | while (num > 0) { 194 | [maxNumberString appendFormat:@"9"]; 195 | num = num / 10; 196 | } 197 | 198 | return maxNumberString; 199 | } 200 | 201 | - (NSString *)notRounding:(float)price afterPoint:(int)position { 202 | // price:需要处理的数字, 203 | // position:保留小数点第几位 204 | // NSRoundDown代表的就是 只舍不入 205 | NSDecimalNumberHandler* roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain 206 | scale:position 207 | raiseOnExactness:NO 208 | raiseOnOverflow:NO 209 | raiseOnUnderflow:NO 210 | raiseOnDivideByZero:NO]; 211 | NSDecimalNumber *ouncesDecimal; 212 | NSDecimalNumber *roundedOunces; 213 | 214 | ouncesDecimal = [[NSDecimalNumber alloc] initWithFloat:price]; 215 | roundedOunces = [ouncesDecimal decimalNumberByRoundingAccordingToBehavior:roundingBehavior]; 216 | return [NSString stringWithFormat:@"%@", roundedOunces]; 217 | } 218 | 219 | @end 220 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/View/RulerLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // RulerLayout.h 3 | // TYFitFore 4 | // 5 | // Created by apple on 2018/7/5. 6 | // Copyright © 2018年 tangpeng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RulerLayout : UICollectionViewLayout 12 | 13 | @property (nonatomic, assign) CGFloat spacing; /**< cell间距 */ 14 | @property (nonatomic, assign) CGSize itemSize; 15 | @property (nonatomic, assign) NSInteger actualLength; /**< 数据的实际长度 */ 16 | @property (nonatomic, assign) NSInteger offset; 17 | @property (nonatomic, assign) UICollectionViewScrollDirection scrollDirection; /**< 滑动方向 */ 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/View/RulerLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // RulerLayout.m 3 | // TYFitFore 4 | // 5 | // Created by apple on 2018/7/5. 6 | // Copyright © 2018年 tangpeng. All rights reserved. 7 | // 8 | 9 | #import "RulerLayout.h" 10 | 11 | @implementation RulerLayout 12 | 13 | - (instancetype)init { 14 | 15 | if (self = [super init]) { 16 | [self defaultSetup]; 17 | } 18 | return self; 19 | } 20 | 21 | /** 默认设置 */ 22 | - (void)defaultSetup { 23 | self.itemSize = CGSizeMake(280, 400); 24 | self.scrollDirection = UICollectionViewScrollDirectionHorizontal; 25 | } 26 | 27 | - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { 28 | return YES; 29 | } 30 | 31 | - (CGSize)collectionViewContentSize { 32 | //计算额外组数 33 | NSInteger total = [self.collectionView numberOfItemsInSection:0]; 34 | NSInteger loop = total / self.actualLength; 35 | CGFloat extra = 0; 36 | 37 | if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) { 38 | extra = (loop - 1) * ((self.itemSize.width + self.spacing) * 4 + self.itemSize.width/2.0); 39 | } else { 40 | extra = (loop - 1) * ((self.itemSize.height + self.spacing) * 4 + self.itemSize.height/2.0); 41 | } 42 | 43 | //item总数 44 | NSInteger count = [self.collectionView numberOfItemsInSection:0]; 45 | //宽度 水平方向 = item总数 * (item宽度 + space) - space ; 垂直方向 = collectionView的宽度 46 | CGFloat width = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? count*(self.itemSize.width+self.spacing)-self.spacing + extra : self.collectionView.bounds.size.width; 47 | //高度 水平方向 = collectionView的宽度 ; 垂直方向 = item总数 * (item宽度 + space) - space 48 | CGFloat height = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? self.collectionView.bounds.size.height : count*(self.itemSize.height+self.spacing)-self.spacing + extra; 49 | return CGSizeMake(width, height); 50 | 51 | /* 52 | //item总数 53 | NSInteger count = [self.collectionView numberOfItemsInSection:0]; 54 | //宽度 水平方向 = item总数 * (item宽度 + space) - space ; 垂直方向 = collectionView的宽度 55 | CGFloat width = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? count*(self.itemSize.width+self.spacing)-self.spacing : self.collectionView.bounds.size.width; 56 | //高度 水平方向 = collectionView的宽度 ; 垂直方向 = item总数 * (item宽度 + space) - space 57 | CGFloat height = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? self.collectionView.bounds.size.height : count*(self.itemSize.height+self.spacing)-self.spacing; 58 | return CGSizeMake(width, height); 59 | */ 60 | } 61 | 62 | /** 设置每个indexPath对应cell的Attribute */ 63 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { 64 | //循环组数 65 | NSInteger loop = indexPath.row / self.actualLength; 66 | UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 67 | attribute.size = self.itemSize; 68 | 69 | //每个循环最后一个cell的间距 70 | CGFloat extra = 0; 71 | if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) { 72 | extra = loop * ((self.itemSize.width + self.spacing) * 4 + self.itemSize.width/2.0); 73 | } else { 74 | extra = loop * ((self.itemSize.height + self.spacing) * 4 + self.itemSize.height/2.0); 75 | } 76 | //x坐标 水平方向 = item总数 * (item宽度 + space) ; 垂直方向 = (collectionView - item宽度) / 2.0 (居中显示) 77 | CGFloat x = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? 78 | (indexPath.item * (self.spacing + self.itemSize.width)) + extra : 79 | (0.5 * (self.collectionView.bounds.size.width - self.itemSize.width)); 80 | 81 | //y坐标 水平方向 = (collectionView - item宽度) / 2.0 (居中显示) ; 垂直方向 = item总数 * (item高度 + space) 82 | CGFloat y = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? 83 | (0.5 * (self.collectionView.bounds.size.height - self.itemSize.height)) : 84 | (indexPath.item * (self.spacing + self.itemSize.height)) + extra; 85 | 86 | //cell的实际frame 87 | attribute.frame = CGRectMake(x, y, attribute.size.width, attribute.size.height); 88 | 89 | return attribute; 90 | 91 | /* 92 | UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 93 | attribute.size = self.itemSize; 94 | 95 | //x坐标 水平方向 = item总数 * (item宽度 + space) ; 垂直方向 = (collectionView - item宽度) / 2.0 (居中显示) 96 | CGFloat x = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? 97 | (indexPath.item * (self.spacing + self.itemSize.width)) : 98 | (0.5 * (self.collectionView.bounds.size.width - self.itemSize.width)); 99 | 100 | //y坐标 水平方向 = (collectionView - item宽度) / 2.0 (居中显示) ; 垂直方向 = item总数 * (item高度 + space) 101 | CGFloat y = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? 102 | (0.5 * (self.collectionView.bounds.size.height - self.itemSize.height)) : 103 | (indexPath.item * (self.spacing + self.itemSize.height)); 104 | 105 | //cell的实际frame 106 | attribute.frame = CGRectMake(x, y, attribute.size.width, attribute.size.height); 107 | 108 | return attribute; 109 | */ 110 | } 111 | 112 | /** 返回指定rect范围中所有cell的Attributes */ 113 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 114 | NSArray *attributes = [self attributesInRect:rect]; 115 | return attributes; 116 | } 117 | 118 | /** 调整最终的偏移 */ 119 | - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { 120 | 121 | //原本应该在屏幕中的rect 122 | CGRect oldRect = CGRectMake(proposedContentOffset.x, proposedContentOffset.y, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); 123 | NSArray *attributes = [self layoutAttributesForElementsInRect:oldRect]; 124 | 125 | //原本应该停留在collectionView中点的位置 126 | //水平方向中点位置 = 原本应该停留的x点 + collectionView一半宽 127 | //垂直方向中点位置 = 原本应该停留的y点 + collectionView一半高 128 | CGFloat center = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? 129 | (proposedContentOffset.x + 0.5*self.collectionView.bounds.size.width) : 130 | (proposedContentOffset.y + 0.5*self.collectionView.bounds.size.height); 131 | 132 | //找到距离collectionView中点位置最近的cell 并且计算他们之间的最小距离(minOffset) 133 | CGFloat minOffset = MAXFLOAT; 134 | for (UICollectionViewLayoutAttributes *attribute in attributes) { 135 | 136 | //判断当前是第几组 137 | NSInteger loopGroup = attribute.indexPath.item / self.actualLength; 138 | if (attribute.indexPath.item % self.offset == loopGroup % self.offset) { 139 | CGFloat offset = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? (attribute.center.x - center) : (attribute.center.y - center); 140 | if (ABS(offset) < ABS(minOffset)) { 141 | minOffset = offset; 142 | } 143 | } 144 | } 145 | 146 | //所以最终应该停留的位置 = collectionView中点位置 + 与之距离最近cell的距离(minOffset) = 实际停留的cell位置 147 | CGFloat newX = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? (proposedContentOffset.x + minOffset) : proposedContentOffset.x; 148 | CGFloat newY = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? proposedContentOffset.y : (proposedContentOffset.y + minOffset); 149 | 150 | return CGPointMake(newX, newY); 151 | } 152 | 153 | /** 返回指定范围内的attributes数组 */ 154 | - (NSArray *)attributesInRect:(CGRect)rect { 155 | //计算额外组数 156 | //一轮循环的总偏移量 157 | CGFloat scaleWidth = 0; 158 | NSInteger scrollLoop = 0; 159 | if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) { 160 | scaleWidth = self.itemSize.width; 161 | CGFloat oneRoundOffset = (scaleWidth + self.spacing) * self.actualLength + ((scaleWidth + self.spacing) * 4 + scaleWidth/2.0); 162 | scrollLoop = rect.origin.x / oneRoundOffset; 163 | } else { 164 | scaleWidth = self.itemSize.height; 165 | CGFloat oneRoundOffset = (scaleWidth + self.spacing) * self.actualLength + ((scaleWidth + self.spacing) * 4 + scaleWidth/2.0); 166 | scrollLoop = rect.origin.y / oneRoundOffset; 167 | } 168 | CGFloat extra = scrollLoop * ((scaleWidth + self.spacing) * 4 + scaleWidth/2.0); 169 | 170 | //指定范围内的第一个cell的下标 171 | NSInteger preIndex = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? 172 | ((rect.origin.x-extra)/(self.itemSize.width+self.spacing)) : 173 | ((rect.origin.y-extra)/(self.itemSize.height+self.spacing)); 174 | preIndex = ((preIndex < 0) ? 0 : preIndex); //防止下标越界 175 | 176 | //指定范围内的最后一个cell下标 177 | NSInteger latIndex = (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? 178 | ((CGRectGetMaxX(rect)-extra)/(self.itemSize.width+self.spacing)) : 179 | ((CGRectGetMaxY(rect)-extra)/(self.itemSize.height+self.spacing)); 180 | 181 | NSInteger itemCount = [self.collectionView numberOfItemsInSection:0]; 182 | latIndex = ((latIndex >= itemCount) ? itemCount-1 : latIndex); //防止下标越界 183 | 184 | NSMutableArray *rectAttributes = [NSMutableArray array]; 185 | //将对应下标的attribute存入数组中 186 | for (NSInteger i=preIndex; i<=latIndex; i++) { 187 | NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; 188 | UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:indexPath]; 189 | if (CGRectIntersectsRect(rect, attribute.frame)) { 190 | [rectAttributes addObject:attribute]; 191 | } 192 | } 193 | return rectAttributes; 194 | } 195 | 196 | - (void)setSpacing:(CGFloat)spacing { 197 | if (_spacing != spacing) { 198 | _spacing = spacing; 199 | [self invalidateLayout]; 200 | } 201 | } 202 | 203 | - (void)setItemSize:(CGSize)itemSize { 204 | if (!CGSizeEqualToSize(_itemSize, itemSize)) { 205 | _itemSize = itemSize; 206 | [self invalidateLayout]; 207 | } 208 | } 209 | 210 | - (void)setScrollDirection:(UICollectionViewScrollDirection)scrollDirection { 211 | if (_scrollDirection != scrollDirection) { 212 | _scrollDirection = scrollDirection; 213 | [self invalidateLayout]; 214 | } 215 | } 216 | 217 | - (NSInteger)offset { 218 | if (_offset <= 0) { 219 | return 1; 220 | } 221 | return _offset; 222 | } 223 | 224 | @end 225 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/View/RulerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // RulerView.h 3 | // TYFitFore 4 | // 5 | // Created by apple on 2018/7/5. 6 | // Copyright © 2018年 tangpeng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "RulerCollectionViewCell.h" 12 | 13 | @protocol RulerViewDelegate 14 | 15 | - (void)rulerSelectValue:(double)value tag:(NSInteger)tag; 16 | 17 | @end 18 | 19 | @interface RulerConfig : NSObject 20 | 21 | //视图属性 22 | @property (nonatomic, assign) CGFloat shortScaleLength; /**< 短刻度长度 */ 23 | @property (nonatomic, assign) CGFloat longScaleLength; /**< 长刻度长度 */ 24 | @property (nonatomic, assign) CGFloat scaleWidth; /**< 刻度尺宽度 */ 25 | @property (nonatomic, strong) UIColor *scaleColor; /**< 刻度颜色 */ 26 | @property (nonatomic, assign) CGFloat shortScaleStart; /**< 短刻度起始位置 */ 27 | @property (nonatomic, assign) CGFloat longScaleStart; /**< 长刻度起始位置 */ 28 | @property (nonatomic, assign) CGFloat distanceBetweenScale; /**< 刻度之间的距离 */ 29 | @property (nonatomic, assign) RulerNumberDirection numberDirection; /**< 数字方向 */ 30 | @property (nonatomic, assign) CGFloat distanceFromScaleToNumber; /**< 刻度和数字之间的距离 */ 31 | //指示器 32 | @property (nonatomic, assign) CGSize pointSize; /**< 指示视图宽高 */ 33 | @property (nonatomic, strong) UIColor *pointColor; /**< 指示视图颜色 */ 34 | @property (nonatomic, assign) CGFloat pointStart; /**< 指示器视图起始位置 */ 35 | //数字属性 36 | @property (nonatomic, strong) UIFont *numberFont; /**< 数字字体 */ 37 | @property (nonatomic, strong) UIColor *numberColor; /**< 数字颜色 */ 38 | //刻度相关 39 | @property (nonatomic, assign) NSInteger max; /**< 最大值 */ 40 | @property (nonatomic, assign) NSInteger min; /**< 最小值 */ 41 | @property (nonatomic, assign) double defaultNumber; /**< 默认值 */ 42 | @property (nonatomic, assign) NSInteger offset; /**< 每次偏移的刻度尺单位 */ 43 | //选项 44 | @property (nonatomic, assign) BOOL isDecimal; /**< 保留一位小数类型 */ 45 | @property (nonatomic, assign) BOOL selectionEnable; /**< 是否允许选中 */ 46 | @property (nonatomic, assign) BOOL useGradient; /**< 是否使用渐变色 */ 47 | @property (nonatomic, assign) BOOL reverse; /**< 刻度尺反向 */ 48 | @property (nonatomic, assign) BOOL infiniteLoop; /**< 刻度尺循环 */ 49 | 50 | + (instancetype)defaultConfig; 51 | 52 | @end 53 | 54 | @interface RulerView : UIView 55 | 56 | @property (nonatomic, strong) RulerConfig *rulerConfig; /**< 属性设置 */ 57 | @property (nonatomic, weak) id delegate; /**< 代理 */ 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/View/RulerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // RulerView.m 3 | // TYFitFore 4 | // 5 | // Created by apple on 2018/7/5. 6 | // Copyright © 2018年 tangpeng. All rights reserved. 7 | // 8 | 9 | #import "RulerView.h" 10 | 11 | #import "RulerLayout.h" 12 | 13 | #define kDirectionHorizontal (self.rulerLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) 14 | 15 | static NSString *const rulerCollectionViewCellIdentifier = @"rulerCollectionViewCellIdentifier"; 16 | 17 | @implementation RulerConfig 18 | 19 | #pragma mark - 默认设置 20 | + (instancetype)defaultConfig { 21 | RulerConfig *config = [[RulerConfig alloc] init]; 22 | //刻度高度 23 | config.shortScaleLength = 17.5; 24 | config.longScaleLength = 25; 25 | //刻度宽度 26 | config.scaleWidth = 2; 27 | //刻度起始位置 28 | config.shortScaleStart = 25; 29 | config.longScaleStart = 25; 30 | //刻度颜色 31 | config.scaleColor = UIColorFromHex(0xdae0ed); 32 | //刻度之间的距离 33 | config.distanceBetweenScale = 8; 34 | //刻度距离数字的距离 35 | config.distanceFromScaleToNumber = 35; 36 | //指示视图属性设置 37 | config.pointSize = CGSizeMake(4, 40); 38 | config.pointColor = UIColorFromHex(0x20c6ba); 39 | config.pointStart = 20; 40 | //文字属性 41 | config.numberFont = [UIFont systemFontOfSize:11]; 42 | config.numberColor = UIColorFromHex(0x617272); 43 | config.numberDirection = numberBottom; 44 | config.min = 0; 45 | config.offset = 1; 46 | 47 | return config; 48 | } 49 | 50 | @end 51 | 52 | @interface RulerView () 53 | 54 | @property (nonatomic, strong) RulerLayout *rulerLayout; 55 | @property (nonatomic, strong) UICollectionView *rulerCollectionView; /**< 刻度尺实际实现视图 */ 56 | @property (nonatomic, strong) UIImageView *indicatorView; /**< 指示器视图 */ 57 | //layer层,渐变layer 58 | @property (nonatomic, strong) CAGradientLayer *startGradientLayer; 59 | @property (nonatomic, strong) CAGradientLayer *endGradientLayer; 60 | 61 | @property (nonatomic, assign) NSInteger selectIndex; /**< 当前选中的下标 */ 62 | @property (nonatomic, strong) NSMutableArray *indexArray; /**< 下标数组 */ 63 | @property (nonatomic, assign) BOOL activeDelegate; /**< 允许调用代理方法 */ 64 | @property (nonatomic, assign) NSInteger scrollLoop; /**< 当前滑动循环组 */ 65 | 66 | @end 67 | 68 | @implementation RulerView 69 | 70 | - (instancetype)initWithFrame:(CGRect)frame { 71 | if (self = [super initWithFrame:frame]) { 72 | self.rulerConfig = [RulerConfig defaultConfig]; 73 | } 74 | return self; 75 | } 76 | 77 | - (void)willMoveToSuperview:(UIView *)newSuperview { 78 | [super willMoveToSuperview:newSuperview]; 79 | //视图布局 80 | [self layoutViews]; 81 | } 82 | 83 | #pragma mark - 视图布局 84 | - (void)layoutViews { 85 | 86 | //添加渐变层 87 | if (self.rulerConfig.useGradient) { 88 | [self addStartGradientLayer]; 89 | [self addEndGradientLayer]; 90 | } 91 | 92 | //计算cell的size 93 | self.rulerLayout = [[RulerLayout alloc] init]; 94 | self.rulerLayout.spacing = self.rulerConfig.distanceBetweenScale; 95 | self.rulerLayout.offset = self.rulerConfig.offset; 96 | if (self.rulerConfig.numberDirection == numberTop || self.rulerConfig.numberDirection == numberBottom) { 97 | //水平方向 98 | self.rulerLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; 99 | self.rulerLayout.itemSize = CGSizeMake(self.rulerConfig.scaleWidth, CGRectGetHeight(self.frame)); 100 | } else { 101 | //垂直方向 102 | self.rulerLayout.scrollDirection = UICollectionViewScrollDirectionVertical; 103 | self.rulerLayout.itemSize = CGSizeMake(CGRectGetWidth(self.frame), self.rulerConfig.scaleWidth); 104 | } 105 | 106 | self.rulerCollectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:self.rulerLayout]; 107 | self.rulerCollectionView.delegate = self; 108 | self.rulerCollectionView.dataSource = self; 109 | self.rulerCollectionView.showsVerticalScrollIndicator = NO; 110 | self.rulerCollectionView.showsHorizontalScrollIndicator = NO; 111 | self.rulerCollectionView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0]; 112 | 113 | //初始化数据 114 | [self initialData]; 115 | 116 | //前后偏移 117 | self.rulerCollectionView.contentInset = (kDirectionHorizontal ? UIEdgeInsetsMake(0, CGRectGetWidth(self.frame)/2.0, 0, CGRectGetWidth(self.frame)/2.0) : UIEdgeInsetsMake(CGRectGetHeight(self.frame)/2.0, 0, CGRectGetHeight(self.frame)/2.0, 0)); 118 | self.rulerCollectionView.bounces = NO; 119 | [self.rulerCollectionView registerClass:[RulerCollectionViewCell class] forCellWithReuseIdentifier:rulerCollectionViewCellIdentifier]; 120 | [self addSubview:self.rulerCollectionView]; 121 | 122 | //指针 123 | self.indicatorView = [[UIImageView alloc] init]; 124 | [self centerPointView]; 125 | self.indicatorView.backgroundColor = self.rulerConfig.pointColor; 126 | self.indicatorView.layer.cornerRadius = self.rulerConfig.pointSize.width/2.0; 127 | [self addSubview:self.indicatorView]; 128 | 129 | //默认选中 偏移 = 指定数值 * (cell宽 + 刻度之间的距离) - 默认偏移 + cell宽的一半 130 | double offset = 0; 131 | //初始偏移值 132 | double contentInset = (kDirectionHorizontal ? self.rulerCollectionView.contentInset.left : self.rulerCollectionView.contentInset.top); 133 | BOOL suitableNumber = NO; //默认选中值是否符合条件 134 | 135 | //默认选中值有效才能调用偏移方法 136 | double activeSelectionNumber = 0; 137 | if (self.rulerConfig.reverse) { 138 | activeSelectionNumber = self.rulerConfig.max - self.rulerConfig.defaultNumber; 139 | } else { 140 | activeSelectionNumber = self.rulerConfig.defaultNumber - self.rulerConfig.min; 141 | } 142 | 143 | if (activeSelectionNumber >= 0) { 144 | if (self.rulerConfig.isDecimal) { 145 | //偏移计算:(单个刻度尺宽度 + 刻度尺间距) * 总刻度 - 起始偏移 + 最后一个刻度宽度 / 2.0 146 | offset = activeSelectionNumber * 10 * (self.rulerConfig.scaleWidth + self.rulerConfig.distanceBetweenScale) - contentInset + (self.rulerConfig.scaleWidth / 2.0); 147 | //检测数字是否符合条件 148 | NSString *defaultValue = [NSString stringWithFormat:@"%lf", activeSelectionNumber * 10]; 149 | if ([RulerView isInt:defaultValue]) { 150 | self.selectIndex = activeSelectionNumber * 10; 151 | suitableNumber = YES; 152 | } 153 | } else { 154 | //偏移计算:(单个刻度尺宽度 + 刻度尺间距) * 总刻度 - 起始偏移 + 最后一个刻度宽度 / 2.0 155 | offset = activeSelectionNumber * (self.rulerConfig.scaleWidth + self.rulerConfig.distanceBetweenScale) - contentInset + (self.rulerConfig.scaleWidth / 2.0); 156 | self.selectIndex = activeSelectionNumber; 157 | suitableNumber = YES; 158 | } 159 | } 160 | 161 | //如果没有默认值,就初始偏移 162 | if (offset == 0) { 163 | offset = -(contentInset - self.rulerConfig.scaleWidth / 2.0); 164 | } 165 | //有效偏移才允许调用代理方法 166 | if (suitableNumber) { 167 | self.activeDelegate = YES; 168 | } 169 | 170 | //校正偏差 171 | [self correctionDeviation:offset]; 172 | //如果是循环尺 173 | if (self.rulerConfig.infiniteLoop) { 174 | NSInteger totalCount = [self.rulerCollectionView numberOfItemsInSection:0]; 175 | NSInteger factor = totalCount / self.rulerLayout.actualLength / 2; 176 | //一轮循环的总偏移量 177 | CGFloat oneRoundOffset = (self.rulerConfig.scaleWidth + self.rulerConfig.distanceBetweenScale) * self.rulerLayout.actualLength + ((self.rulerConfig.scaleWidth + self.rulerLayout.spacing) * 4 + self.rulerConfig.scaleWidth/2.0); 178 | offset = offset + factor * oneRoundOffset; 179 | } 180 | self.rulerCollectionView.contentOffset = (kDirectionHorizontal ? CGPointMake(offset, 0) : CGPointMake(0, offset)); //此方法会触发scrollViewDidScroll 181 | 182 | //默认选中(符号条件的才能默认选中) 183 | if (self.rulerConfig.selectionEnable && suitableNumber) { 184 | [self.rulerCollectionView layoutIfNeeded]; 185 | [self selectCell]; 186 | } 187 | self.activeDelegate = YES; 188 | } 189 | 190 | /** 指示视图居中 */ 191 | - (void)centerPointView { 192 | if (kDirectionHorizontal) { 193 | self.indicatorView.frame = CGRectMake((CGRectGetWidth(self.frame) - self.rulerConfig.pointSize.width)/2.0, self.rulerConfig.pointStart, self.rulerConfig.pointSize.width, self.rulerConfig.pointSize.height); 194 | } else { 195 | self.indicatorView.frame = CGRectMake(self.rulerConfig.pointStart, (CGRectGetHeight(self.frame) - self.rulerConfig.pointSize.width)/2.0, self.rulerConfig.pointSize.height, self.rulerConfig.pointSize.width); 196 | } 197 | } 198 | 199 | /** 校正偏差 */ 200 | - (void)correctionDeviation:(double)offset { 201 | //偏差校正说明:因为计算出来的偏移量会有小数,scrollview在设置偏移量时,会自动将小数偏移四舍五入 202 | //所以用指示视图的位置来校正这个偏差。当刻度尺开始滑动时,复原指示视图的位置 203 | NSInteger roundOffset = roundl(offset); 204 | double deviation = offset - roundOffset; 205 | if (kDirectionHorizontal) { 206 | CGRect frame = self.indicatorView.frame; 207 | frame.origin.x += deviation; 208 | self.indicatorView.frame = frame; 209 | } else { 210 | CGRect frame = self.indicatorView.frame; 211 | frame.origin.y += deviation; 212 | self.indicatorView.frame = frame; 213 | } 214 | } 215 | 216 | /** 初始化数据 */ 217 | - (void)initialData { 218 | //初始化数据源 219 | if (self.rulerConfig.max == 0 || self.rulerConfig.min >= self.rulerConfig.max) { 220 | //校验数据 221 | return; 222 | } else { 223 | [self.indexArray removeAllObjects]; 224 | 225 | //因为是从0开始,所以需要在最大值基础上 + 1 226 | NSInteger items = self.rulerConfig.max - self.rulerConfig.min; 227 | NSInteger totalCount = 0; 228 | if (self.rulerConfig.isDecimal) { 229 | //如果是一位小数类型,则数据扩大10倍 230 | totalCount = items * 10 + 1; 231 | } else { 232 | totalCount = items + 1; 233 | } 234 | 235 | //告诉layout数据的实际长度,以便计算每组数据之间的留白 236 | self.rulerLayout.actualLength = totalCount; 237 | NSInteger loopCount = totalCount; 238 | if (self.rulerConfig.infiniteLoop) { 239 | if (totalCount >= 1000 && totalCount <= 5000) { 240 | loopCount = totalCount * 500; 241 | } else if (totalCount < 1000) { 242 | loopCount = totalCount * 1000; 243 | } else { 244 | if (totalCount * 100 < NSIntegerMax) { 245 | loopCount = totalCount * 100; 246 | } 247 | } 248 | } 249 | 250 | for (NSInteger i=0; i= oneRoundOffset) { 287 | self.scrollLoop = offset / oneRoundOffset; 288 | } else { 289 | self.scrollLoop = 0; 290 | } 291 | 292 | offset = offset - (self.scrollLoop * oneRoundOffset); 293 | index = roundl(offset / (self.rulerConfig.scaleWidth + self.rulerConfig.distanceBetweenScale)); 294 | self.selectIndex = index; 295 | 296 | } else { 297 | index = roundl(offset / (self.rulerConfig.scaleWidth + self.rulerConfig.distanceBetweenScale)); 298 | self.selectIndex = index; 299 | } 300 | 301 | double value = 0; 302 | //判断是否是小数 303 | if (self.rulerConfig.isDecimal) { 304 | if (self.rulerConfig.reverse) { 305 | value = self.rulerConfig.max - (index * 1.0 / 10.0 + self.rulerConfig.min) + self.rulerConfig.min; 306 | } else { 307 | value = index * 1.0 / 10.0 + self.rulerConfig.min; 308 | } 309 | } else { 310 | if (self.rulerConfig.reverse) { 311 | value = self.rulerConfig.max - index; 312 | } else { 313 | value = index * 1.0 + self.rulerConfig.min; 314 | } 315 | } 316 | 317 | //保证数据在范围内 318 | if (value >= self.rulerConfig.min && value <= self.rulerConfig.max && self.activeDelegate) { 319 | if ([self.delegate respondsToSelector:@selector(rulerSelectValue:tag:)]) { 320 | [self.delegate rulerSelectValue:value tag:self.tag]; 321 | } 322 | } 323 | } 324 | 325 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 326 | [self resetCell]; 327 | //指示器视图居中显示 328 | [self centerPointView]; 329 | } 330 | 331 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 332 | BOOL scrollToScrollStop = !scrollView.tracking && !scrollView.dragging && !scrollView.decelerating; 333 | if (scrollToScrollStop) { 334 | //定位到中间那组 335 | NSInteger totalCount = [self.rulerCollectionView numberOfItemsInSection:0]; 336 | NSInteger factor = totalCount / self.rulerLayout.actualLength / 2; 337 | NSInteger indexOfLocation = self.selectIndex + factor * self.rulerLayout.actualLength; 338 | [self.rulerCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:indexOfLocation inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; 339 | 340 | //等视图更新完成才选中 341 | dispatch_async(dispatch_get_main_queue(), ^{ 342 | [self selectCell]; 343 | }); 344 | } 345 | } 346 | 347 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 348 | if (!decelerate) { 349 | BOOL dragToDragStop = scrollView.tracking && !scrollView.dragging && !scrollView.decelerating; 350 | if (dragToDragStop) { 351 | [self selectCell]; 352 | } 353 | } 354 | } 355 | 356 | - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { 357 | [self selectCell]; 358 | } 359 | 360 | - (void)selectCell { 361 | if (self.rulerConfig.selectionEnable) { 362 | NSInteger min = self.selectIndex - 5; 363 | NSInteger max = self.selectIndex + 5; 364 | 365 | for (NSInteger i=min; i<=max; i++) { 366 | if (i >= 0 && i < self.rulerLayout.actualLength) { 367 | NSInteger hiddenIndex = i + self.scrollLoop * self.rulerLayout.actualLength; 368 | RulerCollectionViewCell *cell = (RulerCollectionViewCell *)[self.rulerCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:hiddenIndex inSection:0]]; 369 | [cell makeCellHiddenText]; 370 | } 371 | } 372 | 373 | NSInteger indexOfCell = self.selectIndex + self.scrollLoop * self.rulerLayout.actualLength; 374 | RulerCollectionViewCell *cell = (RulerCollectionViewCell *)[self.rulerCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:indexOfCell inSection:0]]; 375 | [cell makeCellSelect]; 376 | } 377 | } 378 | 379 | - (void)resetCell { 380 | if (self.rulerConfig.selectionEnable) { 381 | NSInteger min = self.selectIndex - 5; 382 | NSInteger max = self.selectIndex + 5; 383 | for (NSInteger i=min; i<=max; i++) { 384 | if (i >= 0 && i < self.rulerLayout.actualLength) { 385 | NSInteger index = i + self.scrollLoop * self.rulerLayout.actualLength; 386 | RulerCollectionViewCell *cell = (RulerCollectionViewCell *)[self.rulerCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]]; 387 | [cell setNeedsLayout]; 388 | [cell layoutIfNeeded]; 389 | } 390 | } 391 | } 392 | } 393 | 394 | #pragma mark - 渐变透明层 395 | - (void)addStartGradientLayer { 396 | //初始化CAGradientlayer对象,使它的大小为UIView的大小 397 | self.startGradientLayer = [CAGradientLayer layer]; 398 | self.startGradientLayer.frame = self.bounds; 399 | 400 | //将CAGradientlayer对象添加在我们要设置背景色的视图的layer层 401 | [self.layer addSublayer:self.startGradientLayer]; 402 | 403 | //设置渐变区域的起始和终止位置(颜色渐变范围为0-1) 404 | self.startGradientLayer.startPoint = CGPointMake(0, 0); 405 | self.startGradientLayer.endPoint = CGPointMake(1, 0); 406 | 407 | //设置颜色数组 408 | self.startGradientLayer.colors = @[(__bridge id)[UIColor whiteColor].CGColor, 409 | (__bridge id)[[UIColor whiteColor]colorWithAlphaComponent:0.0f].CGColor]; 410 | 411 | //设置颜色分割点(区域渐变范围:0-1) 412 | self.startGradientLayer.locations = @[@(0.0f), @(0.3f)]; 413 | } 414 | 415 | - (void)addEndGradientLayer { 416 | //初始化CAGradientlayer对象,使它的大小为UIView的大小 417 | self.endGradientLayer = [CAGradientLayer layer]; 418 | self.endGradientLayer.frame = self.bounds; 419 | 420 | //将CAGradientlayer对象添加在我们要设置背景色的视图的layer层 421 | [self.layer addSublayer:self.endGradientLayer]; 422 | 423 | //设置渐变区域的起始和终止位置(颜色渐变范围为0-1) 424 | self.endGradientLayer.startPoint = CGPointMake(0, 0); 425 | self.endGradientLayer.endPoint = CGPointMake(1, 0); 426 | 427 | //设置颜色数组 428 | self.endGradientLayer.colors = @[(__bridge id)[[UIColor whiteColor]colorWithAlphaComponent:0.0f].CGColor, 429 | (__bridge id)[UIColor whiteColor].CGColor]; 430 | 431 | //设置颜色分割点(区域渐变范围:0-1) 432 | self.endGradientLayer.locations = @[@(0.7f), @(1.0f)]; 433 | } 434 | 435 | #pragma mark - getter 436 | - (NSMutableArray *)indexArray { 437 | if (!_indexArray) { 438 | _indexArray = [NSMutableArray array]; 439 | } 440 | return _indexArray; 441 | } 442 | 443 | #pragma mark - Tool 444 | /** 传入字符串是否是数字 */ 445 | + (BOOL)isInt:(NSString*)string { 446 | if (!string) { 447 | return false; 448 | } 449 | //string不是浮点型 450 | if (![string containsString:@"."]) { 451 | NSScanner *scan = [NSScanner scannerWithString:string]; 452 | int val; 453 | return[scan scanInt:&val] && [scan isAtEnd]; 454 | } 455 | //string是浮点型 456 | else { 457 | NSArray *numberArray = [string componentsSeparatedByString:@"."]; 458 | if (numberArray.count != 2) { 459 | return false; 460 | } 461 | else { 462 | NSString *behindNumber = numberArray[1]; 463 | //如果小数点后的数字等于0,则是整数 464 | return ([behindNumber integerValue] == 0); 465 | } 466 | } 467 | } 468 | 469 | @end 470 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CustomRulerView 4 | // 5 | // Created by apple on 2018/7/26. 6 | // Copyright © 2018年 apple. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CustomRulerView 4 | // 5 | // Created by apple on 2018/7/26. 6 | // Copyright © 2018年 apple. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "RulerView.h" 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic, strong) RulerView *numberTopRulerView; //数字居上带选中刻度尺 16 | @property (nonatomic, strong) RulerView *numberBottomRulerView; //数字居下 17 | @property (nonatomic, strong) RulerView *numberLeftRulerView; //数字居左 18 | @property (nonatomic, strong) RulerView *numberRightRulerView; //数字居右 19 | 20 | @property (nonatomic, strong) UILabel *numberBottomRulerDefaultLabel; 21 | 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | self.view.backgroundColor = [UIColor whiteColor]; 29 | 30 | [self.view addSubview:self.numberTopRulerView]; 31 | [self.view addSubview:self.numberBottomRulerView]; 32 | [self.view addSubview:self.numberLeftRulerView]; 33 | [self.view addSubview:self.numberRightRulerView]; 34 | 35 | [self.view addSubview:self.numberBottomRulerDefaultLabel]; 36 | } 37 | 38 | #pragma mark - 代理方法 39 | - (void)rulerSelectValue:(double)value tag:(NSInteger)tag { 40 | if (tag == 1) { 41 | self.numberBottomRulerDefaultLabel.text = [NSString stringWithFormat:@"选中值:%li", (long)value]; 42 | } else if (tag == 3) { 43 | 44 | } 45 | 46 | NSLog(@"value = %lf", value); 47 | } 48 | 49 | #pragma mark - getter 50 | - (RulerView *)numberTopRulerView { 51 | if (!_numberTopRulerView) { 52 | _numberTopRulerView = [[RulerView alloc] initWithFrame:CGRectMake(0, 100, SCREEN_WIDTH_RULER, 65)]; 53 | _numberTopRulerView.backgroundColor = [UIColorFromHex(0xE4E6EB) colorWithAlphaComponent:0.3f]; 54 | _numberTopRulerView.tag = 0; 55 | _numberTopRulerView.delegate = self; 56 | 57 | RulerConfig *config = [[RulerConfig alloc] init]; 58 | //刻度高度 59 | config.shortScaleLength = 7; 60 | config.longScaleLength = 11; 61 | //刻度宽度 62 | config.scaleWidth = 2; 63 | //刻度起始位置 64 | config.shortScaleStart = 58; 65 | config.longScaleStart = 54; 66 | //刻度颜色 67 | config.scaleColor = UIColorFromHex(0xdae0ed); 68 | //刻度之间的距离 69 | config.distanceBetweenScale = 4; 70 | //刻度距离数字的距离 71 | config.distanceFromScaleToNumber = 13; 72 | //指示视图属性设置 73 | config.pointSize = CGSizeMake(2, 20); 74 | config.pointColor = UIColorFromHex(0x20c6ba); 75 | config.pointStart = 45; 76 | //文字属性 77 | config.numberFont = [UIFont systemFontOfSize:11]; 78 | config.numberColor = UIColorFromHex(0x4A4A4A); 79 | //数字所在位置方向 80 | config.numberDirection = numberTop; 81 | 82 | //取值范围 83 | config.max = 200; 84 | config.min = 0; 85 | //默认值 86 | config.defaultNumber = 57; 87 | //使用小数类型 88 | config.isDecimal = YES; 89 | //选中 90 | config.selectionEnable = YES; 91 | //使用渐变背景 92 | config.useGradient = YES; 93 | config.infiniteLoop = YES; 94 | 95 | _numberTopRulerView.rulerConfig = config; 96 | } 97 | return _numberTopRulerView; 98 | } 99 | 100 | - (RulerView *)numberBottomRulerView { 101 | if (!_numberBottomRulerView) { 102 | _numberBottomRulerView = [[RulerView alloc] initWithFrame:CGRectMake(0, 220, SCREEN_WIDTH_RULER, 100)]; 103 | _numberBottomRulerView.backgroundColor = [UIColorFromHex(0xE4E6EB) colorWithAlphaComponent:0.3f]; 104 | _numberBottomRulerView.tag = 1; 105 | _numberBottomRulerView.delegate = self; 106 | 107 | RulerConfig *config = [[RulerConfig alloc] init]; 108 | //刻度高度 109 | config.shortScaleLength = 17.5; 110 | config.longScaleLength = 25; 111 | //刻度宽度 112 | config.scaleWidth = 2; 113 | //刻度起始位置 114 | config.shortScaleStart = 25; 115 | config.longScaleStart = 25; 116 | //刻度颜色 117 | config.scaleColor = UIColorFromHex(0xdae0ed); 118 | //刻度之间的距离 119 | config.distanceBetweenScale = 8; 120 | //刻度距离数字的距离 121 | config.distanceFromScaleToNumber = 35; 122 | //指示视图属性设置 123 | config.pointSize = CGSizeMake(4, 40); 124 | config.pointColor = UIColorFromHex(0x20c6ba); 125 | config.pointStart = 20; 126 | //文字属性 127 | config.numberFont = [UIFont systemFontOfSize:11]; 128 | config.numberColor = UIColorFromHex(0x617272); 129 | //数字所在位置方向 130 | config.numberDirection = numberBottom; 131 | 132 | //取值范围 133 | config.max = 50; 134 | config.min = 0; 135 | //默认值 136 | config.defaultNumber = 10; 137 | config.infiniteLoop = YES; 138 | config.offset = 5; 139 | 140 | _numberBottomRulerView.rulerConfig = config; 141 | } 142 | return _numberBottomRulerView; 143 | } 144 | 145 | - (RulerView *)numberLeftRulerView { 146 | if (!_numberLeftRulerView) { 147 | _numberLeftRulerView = [[RulerView alloc] initWithFrame:CGRectMake(20, 350, 120, SCREEN_HEIGHT_RULER - 350)]; 148 | _numberLeftRulerView.backgroundColor = [UIColorFromHex(0xE4E6EB) colorWithAlphaComponent:0.3f]; 149 | _numberLeftRulerView.tag = 2; 150 | _numberLeftRulerView.delegate = self; 151 | 152 | RulerConfig *config = [[RulerConfig alloc] init]; 153 | //刻度高度 154 | config.shortScaleLength = 12; 155 | config.longScaleLength = 16; 156 | //刻度宽度 157 | config.scaleWidth = 2; 158 | //刻度起始位置 159 | config.shortScaleStart = 78; 160 | config.longScaleStart = 74; 161 | //刻度颜色 162 | config.scaleColor = UIColorFromHex(0xdae0ed); 163 | //刻度之间的距离 164 | config.distanceBetweenScale = 4; 165 | //刻度距离数字的距离 166 | config.distanceFromScaleToNumber = 13; 167 | //指示视图属性设置 168 | config.pointSize = CGSizeMake(2, 20); 169 | config.pointColor = UIColorFromHex(0x20c6ba); 170 | config.pointStart = 65; 171 | //文字属性 172 | config.numberFont = [UIFont systemFontOfSize:11]; 173 | config.numberColor = UIColorFromHex(0x4A4A4A); 174 | //数字所在位置方向 175 | config.numberDirection = numberLeft; 176 | 177 | //取值范围 178 | config.max = 100; 179 | config.min = 0; 180 | //默认值 181 | config.defaultNumber = 25.2; 182 | //使用小数类型 183 | config.isDecimal = YES; 184 | //选中 185 | config.selectionEnable = YES; 186 | //数字顺序相反 187 | config.reverse = YES; 188 | config.infiniteLoop = YES; 189 | 190 | _numberLeftRulerView.rulerConfig = config; 191 | } 192 | return _numberLeftRulerView; 193 | } 194 | 195 | - (RulerView *)numberRightRulerView { 196 | if (!_numberRightRulerView) { 197 | CGFloat width = 150; 198 | _numberRightRulerView = [[RulerView alloc] initWithFrame:CGRectMake(SCREEN_WIDTH_RULER - 20 - width, 350, width, SCREEN_HEIGHT_RULER - 350)]; 199 | _numberRightRulerView.backgroundColor = [UIColorFromHex(0xE4E6EB) colorWithAlphaComponent:0.3f]; 200 | _numberRightRulerView.tag = 3; 201 | _numberRightRulerView.delegate = self; 202 | 203 | RulerConfig *config = [[RulerConfig alloc] init]; 204 | //刻度高度 205 | config.shortScaleLength = 17.5; 206 | config.longScaleLength = 25; 207 | //刻度宽度 208 | config.scaleWidth = 2; 209 | //刻度起始位置 210 | config.shortScaleStart = 25; 211 | config.longScaleStart = 25; 212 | //刻度颜色 213 | config.scaleColor = UIColorFromHex(0xdae0ed); 214 | //刻度之间的距离 215 | config.distanceBetweenScale = 8; 216 | //刻度距离数字的距离 217 | config.distanceFromScaleToNumber = 35; 218 | //指示视图属性设置 219 | config.pointSize = CGSizeMake(4, 40); 220 | config.pointColor = UIColorFromHex(0x20c6ba); 221 | config.pointStart = 20; 222 | //文字属性 223 | config.numberFont = [UIFont systemFontOfSize:11]; 224 | config.numberColor = UIColorFromHex(0x617272); 225 | //数字所在位置方向 226 | config.numberDirection = numberRight; 227 | 228 | //取值范围 229 | config.max = 5000; 230 | config.min = 1000; 231 | //默认值 232 | config.defaultNumber = 3686; 233 | config.selectionEnable = YES; 234 | 235 | _numberRightRulerView.rulerConfig = config; 236 | } 237 | return _numberRightRulerView; 238 | } 239 | 240 | - (UILabel *)numberBottomRulerDefaultLabel { 241 | if (!_numberBottomRulerDefaultLabel) { 242 | _numberBottomRulerDefaultLabel = [[UILabel alloc] init]; 243 | _numberBottomRulerDefaultLabel.font = [UIFont systemFontOfSize:15]; 244 | _numberBottomRulerDefaultLabel.textColor = UIColorFromHex(0x999999); 245 | CGFloat height = [UIFont systemFontOfSize:15].lineHeight; 246 | _numberBottomRulerDefaultLabel.frame = CGRectMake(0, CGRectGetMinY(_numberBottomRulerView.frame) - height - 5, SCREEN_WIDTH_RULER, height); 247 | _numberBottomRulerDefaultLabel.textAlignment = NSTextAlignmentCenter; 248 | } 249 | return _numberBottomRulerDefaultLabel; 250 | } 251 | 252 | - (void)didReceiveMemoryWarning { 253 | [super didReceiveMemoryWarning]; 254 | // Dispose of any resources that can be recreated. 255 | } 256 | 257 | 258 | @end 259 | -------------------------------------------------------------------------------- /CustomRulerView/CustomRulerView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CustomRulerView 4 | // 5 | // Created by apple on 2018/7/26. 6 | // Copyright © 2018年 apple. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CustomRulerView 2 | iOS自定义刻度尺 3 | --------------------------------------------------------------------------------