├── PSProfileStepperDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── girijeshkumar.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── girijeshkumar.xcuserdatad │ └── xcschemes │ ├── PSProfileStepperDemo.xcscheme │ └── xcschememanagement.plist ├── PSProfileStepperDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DemoVC.h ├── DemoVC.m ├── Info.plist ├── PSProfileStepper │ ├── PSProfileStepper.h │ └── PSProfileStepper.m └── main.m ├── README.md └── Simulator Screen Shot Jun 22, 2016, 10.13.57 AM.png /PSProfileStepperDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CA3443551CF8811000BE4175 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CA3443541CF8811000BE4175 /* main.m */; }; 11 | CA3443581CF8811000BE4175 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CA3443571CF8811000BE4175 /* AppDelegate.m */; }; 12 | CA34435B1CF8811000BE4175 /* DemoVC.m in Sources */ = {isa = PBXBuildFile; fileRef = CA34435A1CF8811000BE4175 /* DemoVC.m */; }; 13 | CA34435E1CF8811000BE4175 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CA34435C1CF8811000BE4175 /* Main.storyboard */; }; 14 | CA3443601CF8811000BE4175 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CA34435F1CF8811000BE4175 /* Assets.xcassets */; }; 15 | CA3443631CF8811000BE4175 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CA3443611CF8811000BE4175 /* LaunchScreen.storyboard */; }; 16 | CA34436D1CF883E400BE4175 /* PSProfileStepper.m in Sources */ = {isa = PBXBuildFile; fileRef = CA34436C1CF883E400BE4175 /* PSProfileStepper.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | CA3443501CF8811000BE4175 /* PSProfileStepperDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PSProfileStepperDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | CA3443541CF8811000BE4175 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 22 | CA3443561CF8811000BE4175 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 23 | CA3443571CF8811000BE4175 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 24 | CA3443591CF8811000BE4175 /* DemoVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoVC.h; sourceTree = ""; }; 25 | CA34435A1CF8811000BE4175 /* DemoVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoVC.m; sourceTree = ""; }; 26 | CA34435D1CF8811000BE4175 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | CA34435F1CF8811000BE4175 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | CA3443621CF8811000BE4175 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | CA3443641CF8811000BE4175 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | CA34436B1CF883E400BE4175 /* PSProfileStepper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSProfileStepper.h; sourceTree = ""; }; 31 | CA34436C1CF883E400BE4175 /* PSProfileStepper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSProfileStepper.m; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | CA34434D1CF8811000BE4175 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | CA3443471CF8811000BE4175 = { 46 | isa = PBXGroup; 47 | children = ( 48 | CA3443521CF8811000BE4175 /* PSProfileStepperDemo */, 49 | CA3443511CF8811000BE4175 /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | CA3443511CF8811000BE4175 /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | CA3443501CF8811000BE4175 /* PSProfileStepperDemo.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | CA3443521CF8811000BE4175 /* PSProfileStepperDemo */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | CA34436A1CF883E400BE4175 /* PSProfileStepper */, 65 | CA3443561CF8811000BE4175 /* AppDelegate.h */, 66 | CA3443571CF8811000BE4175 /* AppDelegate.m */, 67 | CA3443591CF8811000BE4175 /* DemoVC.h */, 68 | CA34435A1CF8811000BE4175 /* DemoVC.m */, 69 | CA34435C1CF8811000BE4175 /* Main.storyboard */, 70 | CA34435F1CF8811000BE4175 /* Assets.xcassets */, 71 | CA3443611CF8811000BE4175 /* LaunchScreen.storyboard */, 72 | CA3443641CF8811000BE4175 /* Info.plist */, 73 | CA3443531CF8811000BE4175 /* Supporting Files */, 74 | ); 75 | path = PSProfileStepperDemo; 76 | sourceTree = ""; 77 | }; 78 | CA3443531CF8811000BE4175 /* Supporting Files */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | CA3443541CF8811000BE4175 /* main.m */, 82 | ); 83 | name = "Supporting Files"; 84 | sourceTree = ""; 85 | }; 86 | CA34436A1CF883E400BE4175 /* PSProfileStepper */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | CA34436B1CF883E400BE4175 /* PSProfileStepper.h */, 90 | CA34436C1CF883E400BE4175 /* PSProfileStepper.m */, 91 | ); 92 | path = PSProfileStepper; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | CA34434F1CF8811000BE4175 /* PSProfileStepperDemo */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = CA3443671CF8811000BE4175 /* Build configuration list for PBXNativeTarget "PSProfileStepperDemo" */; 101 | buildPhases = ( 102 | CA34434C1CF8811000BE4175 /* Sources */, 103 | CA34434D1CF8811000BE4175 /* Frameworks */, 104 | CA34434E1CF8811000BE4175 /* Resources */, 105 | ); 106 | buildRules = ( 107 | ); 108 | dependencies = ( 109 | ); 110 | name = PSProfileStepperDemo; 111 | productName = PSProfileStepperDemo; 112 | productReference = CA3443501CF8811000BE4175 /* PSProfileStepperDemo.app */; 113 | productType = "com.apple.product-type.application"; 114 | }; 115 | /* End PBXNativeTarget section */ 116 | 117 | /* Begin PBXProject section */ 118 | CA3443481CF8811000BE4175 /* Project object */ = { 119 | isa = PBXProject; 120 | attributes = { 121 | LastUpgradeCheck = 0730; 122 | ORGANIZATIONNAME = Pramod; 123 | TargetAttributes = { 124 | CA34434F1CF8811000BE4175 = { 125 | CreatedOnToolsVersion = 7.3; 126 | }; 127 | }; 128 | }; 129 | buildConfigurationList = CA34434B1CF8811000BE4175 /* Build configuration list for PBXProject "PSProfileStepperDemo" */; 130 | compatibilityVersion = "Xcode 3.2"; 131 | developmentRegion = English; 132 | hasScannedForEncodings = 0; 133 | knownRegions = ( 134 | en, 135 | Base, 136 | ); 137 | mainGroup = CA3443471CF8811000BE4175; 138 | productRefGroup = CA3443511CF8811000BE4175 /* Products */; 139 | projectDirPath = ""; 140 | projectRoot = ""; 141 | targets = ( 142 | CA34434F1CF8811000BE4175 /* PSProfileStepperDemo */, 143 | ); 144 | }; 145 | /* End PBXProject section */ 146 | 147 | /* Begin PBXResourcesBuildPhase section */ 148 | CA34434E1CF8811000BE4175 /* Resources */ = { 149 | isa = PBXResourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | CA3443631CF8811000BE4175 /* LaunchScreen.storyboard in Resources */, 153 | CA3443601CF8811000BE4175 /* Assets.xcassets in Resources */, 154 | CA34435E1CF8811000BE4175 /* Main.storyboard in Resources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXResourcesBuildPhase section */ 159 | 160 | /* Begin PBXSourcesBuildPhase section */ 161 | CA34434C1CF8811000BE4175 /* Sources */ = { 162 | isa = PBXSourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | CA34436D1CF883E400BE4175 /* PSProfileStepper.m in Sources */, 166 | CA34435B1CF8811000BE4175 /* DemoVC.m in Sources */, 167 | CA3443581CF8811000BE4175 /* AppDelegate.m in Sources */, 168 | CA3443551CF8811000BE4175 /* main.m in Sources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXSourcesBuildPhase section */ 173 | 174 | /* Begin PBXVariantGroup section */ 175 | CA34435C1CF8811000BE4175 /* Main.storyboard */ = { 176 | isa = PBXVariantGroup; 177 | children = ( 178 | CA34435D1CF8811000BE4175 /* Base */, 179 | ); 180 | name = Main.storyboard; 181 | sourceTree = ""; 182 | }; 183 | CA3443611CF8811000BE4175 /* LaunchScreen.storyboard */ = { 184 | isa = PBXVariantGroup; 185 | children = ( 186 | CA3443621CF8811000BE4175 /* Base */, 187 | ); 188 | name = LaunchScreen.storyboard; 189 | sourceTree = ""; 190 | }; 191 | /* End PBXVariantGroup section */ 192 | 193 | /* Begin XCBuildConfiguration section */ 194 | CA3443651CF8811000BE4175 /* Debug */ = { 195 | isa = XCBuildConfiguration; 196 | buildSettings = { 197 | ALWAYS_SEARCH_USER_PATHS = NO; 198 | CLANG_ANALYZER_NONNULL = YES; 199 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 200 | CLANG_CXX_LIBRARY = "libc++"; 201 | CLANG_ENABLE_MODULES = YES; 202 | CLANG_ENABLE_OBJC_ARC = YES; 203 | CLANG_WARN_BOOL_CONVERSION = YES; 204 | CLANG_WARN_CONSTANT_CONVERSION = YES; 205 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 206 | CLANG_WARN_EMPTY_BODY = YES; 207 | CLANG_WARN_ENUM_CONVERSION = YES; 208 | CLANG_WARN_INT_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_UNREACHABLE_CODE = YES; 211 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 212 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 213 | COPY_PHASE_STRIP = NO; 214 | DEBUG_INFORMATION_FORMAT = dwarf; 215 | ENABLE_STRICT_OBJC_MSGSEND = YES; 216 | ENABLE_TESTABILITY = YES; 217 | GCC_C_LANGUAGE_STANDARD = gnu99; 218 | GCC_DYNAMIC_NO_PIC = NO; 219 | GCC_NO_COMMON_BLOCKS = YES; 220 | GCC_OPTIMIZATION_LEVEL = 0; 221 | GCC_PREPROCESSOR_DEFINITIONS = ( 222 | "DEBUG=1", 223 | "$(inherited)", 224 | ); 225 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 226 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 227 | GCC_WARN_UNDECLARED_SELECTOR = YES; 228 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 229 | GCC_WARN_UNUSED_FUNCTION = YES; 230 | GCC_WARN_UNUSED_VARIABLE = YES; 231 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 232 | MTL_ENABLE_DEBUG_INFO = YES; 233 | ONLY_ACTIVE_ARCH = YES; 234 | SDKROOT = iphoneos; 235 | }; 236 | name = Debug; 237 | }; 238 | CA3443661CF8811000BE4175 /* Release */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | ALWAYS_SEARCH_USER_PATHS = NO; 242 | CLANG_ANALYZER_NONNULL = YES; 243 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 244 | CLANG_CXX_LIBRARY = "libc++"; 245 | CLANG_ENABLE_MODULES = YES; 246 | CLANG_ENABLE_OBJC_ARC = YES; 247 | CLANG_WARN_BOOL_CONVERSION = YES; 248 | CLANG_WARN_CONSTANT_CONVERSION = YES; 249 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 250 | CLANG_WARN_EMPTY_BODY = YES; 251 | CLANG_WARN_ENUM_CONVERSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 254 | CLANG_WARN_UNREACHABLE_CODE = YES; 255 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 256 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 257 | COPY_PHASE_STRIP = NO; 258 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 259 | ENABLE_NS_ASSERTIONS = NO; 260 | ENABLE_STRICT_OBJC_MSGSEND = YES; 261 | GCC_C_LANGUAGE_STANDARD = gnu99; 262 | GCC_NO_COMMON_BLOCKS = YES; 263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 265 | GCC_WARN_UNDECLARED_SELECTOR = YES; 266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 267 | GCC_WARN_UNUSED_FUNCTION = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 270 | MTL_ENABLE_DEBUG_INFO = NO; 271 | SDKROOT = iphoneos; 272 | VALIDATE_PRODUCT = YES; 273 | }; 274 | name = Release; 275 | }; 276 | CA3443681CF8811000BE4175 /* Debug */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 280 | INFOPLIST_FILE = PSProfileStepperDemo/Info.plist; 281 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 282 | PRODUCT_BUNDLE_IDENTIFIER = PS.PSProfileStepperDemo; 283 | PRODUCT_NAME = "$(TARGET_NAME)"; 284 | }; 285 | name = Debug; 286 | }; 287 | CA3443691CF8811000BE4175 /* Release */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 291 | INFOPLIST_FILE = PSProfileStepperDemo/Info.plist; 292 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 293 | PRODUCT_BUNDLE_IDENTIFIER = PS.PSProfileStepperDemo; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | }; 296 | name = Release; 297 | }; 298 | /* End XCBuildConfiguration section */ 299 | 300 | /* Begin XCConfigurationList section */ 301 | CA34434B1CF8811000BE4175 /* Build configuration list for PBXProject "PSProfileStepperDemo" */ = { 302 | isa = XCConfigurationList; 303 | buildConfigurations = ( 304 | CA3443651CF8811000BE4175 /* Debug */, 305 | CA3443661CF8811000BE4175 /* Release */, 306 | ); 307 | defaultConfigurationIsVisible = 0; 308 | defaultConfigurationName = Release; 309 | }; 310 | CA3443671CF8811000BE4175 /* Build configuration list for PBXNativeTarget "PSProfileStepperDemo" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | CA3443681CF8811000BE4175 /* Debug */, 314 | CA3443691CF8811000BE4175 /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | }; 318 | /* End XCConfigurationList section */ 319 | }; 320 | rootObject = CA3443481CF8811000BE4175 /* Project object */; 321 | } 322 | -------------------------------------------------------------------------------- /PSProfileStepperDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PSProfileStepperDemo.xcodeproj/project.xcworkspace/xcuserdata/girijeshkumar.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKPranav/PSProfileStepper/7bdbca205b235b2830c1ad340fff33f12aad9c6b/PSProfileStepperDemo.xcodeproj/project.xcworkspace/xcuserdata/girijeshkumar.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /PSProfileStepperDemo.xcodeproj/xcuserdata/girijeshkumar.xcuserdatad/xcschemes/PSProfileStepperDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /PSProfileStepperDemo.xcodeproj/xcuserdata/girijeshkumar.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | PSProfileStepperDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | CA34434F1CF8811000BE4175 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PSProfileStepperDemo 4 | // 5 | // Created by Pramod Kumar Pranav on 5/27/16. 6 | // Copyright © 2016 Pramod. 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 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PSProfileStepperDemo 4 | // 5 | // Created by Pramod Kumar Pranav on 5/27/16. 6 | // Copyright © 2016 Pramod. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /PSProfileStepperDemo/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 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 84 | 94 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/DemoVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // PSProfileStepperDemo 4 | // 5 | // Created by Pramod Kumar Pranav on 5/27/16. 6 | // Copyright © 2016 Pramod. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DemoVC : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/DemoVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // PSProfileStepperDemo 4 | // 5 | // Created by Pramod Kumar Pranav on 5/27/16. 6 | // Copyright © 2016 Pramod. All rights reserved. 7 | // 8 | 9 | #import "DemoVC.h" 10 | #import "PSProfileStepper.h" 11 | 12 | @interface DemoVC () 13 | { 14 | int index; 15 | } 16 | @property (nonatomic, strong) IBOutlet PSProfileStepper *stepperView; 17 | @property (nonatomic, strong) IBOutlet UILabel *progressLabelIndex; 18 | 19 | - (IBAction)changeSliderValue:(id)sender; 20 | - (IBAction)changeByPrevIndex:(id)sender; 21 | - (IBAction)changeByNextIndex:(id)sender; 22 | 23 | @end 24 | 25 | @implementation DemoVC 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | index = 0; 30 | // Do any additional setup after loading the view, typically from a nib. 31 | [self.progressLabelIndex setText:[NSString stringWithFormat:@"%lu", (unsigned long)self.stepperView.index]]; 32 | [self.stepperView setIndex:2 animated:YES]; 33 | 34 | } 35 | 36 | - (void)didReceiveMemoryWarning { 37 | [super didReceiveMemoryWarning]; 38 | // Dispose of any resources that can be recreated. 39 | } 40 | - (IBAction)changeSliderValue:(PSProfileStepper*)sender { 41 | [self.progressLabelIndex setText:[NSString stringWithFormat:@"%lu", (unsigned long)sender.index]]; 42 | } 43 | 44 | - (IBAction)changeByPrevIndex:(id)sender 45 | { 46 | if (index >= 6 ) 47 | { 48 | index = index - 6; 49 | } 50 | 51 | 52 | [self.stepperView setIndex:index animated:YES]; 53 | [self.progressLabelIndex setText:[NSString stringWithFormat:@"%lu", (unsigned long)self.stepperView.index]]; 54 | 55 | } 56 | - (IBAction)changeByNextIndex:(id)sender 57 | { 58 | if (index <= self.stepperView.maxCount - 6) 59 | { 60 | index = index + 6; 61 | } 62 | 63 | [self.stepperView setIndex:index animated:YES]; 64 | [self.progressLabelIndex setText:[NSString stringWithFormat:@"%lu", (unsigned long)self.stepperView.index]]; 65 | } 66 | @end 67 | 68 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/PSProfileStepper/PSProfileStepper.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSProfileStepper.h 3 | // PSProfileStepper 4 | // 5 | // Created by Pramod Kumar Pranav on 5/25/16. 6 | // Copyright © 2016 AppStudioz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | IB_DESIGNABLE 12 | 13 | @interface PSProfileStepper : UIControl 14 | 15 | @property (nonatomic) NSUInteger maxCount; 16 | @property (nonatomic) IBInspectable NSUInteger index; 17 | @property (nonatomic) IBInspectable NSUInteger circleCount; 18 | 19 | @property (nonatomic) IBInspectable CGFloat lineHeight; 20 | @property (nonatomic) IBInspectable CGFloat lineCircleRadius; 21 | @property (nonatomic) IBInspectable CGFloat sliderCircleRadius; 22 | @property (nonatomic) IBInspectable CGFloat labelFontSize; 23 | 24 | @property (nonatomic, strong) IBInspectable UIColor *lineColor; 25 | @property (nonatomic, strong) IBInspectable UIColor *sliderCircleColor; 26 | 27 | @property (nonatomic, strong) IBInspectable UIColor *innerCircleColor; 28 | @property (nonatomic, strong) IBInspectable UIColor *innerCircleNormalColor; 29 | 30 | @property (nonatomic, strong) IBInspectable NSString *labelFontName; 31 | 32 | 33 | - (void)setIndex:(NSUInteger)index animated:(BOOL)animated; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/PSProfileStepper/PSProfileStepper.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSProfileStepper.m 3 | // PSProfileStepper 4 | // 5 | // Created by Pramod Kumar Pranav on 5/25/16. 6 | // Copyright © 2016 AppStudioz. All rights reserved. 7 | // 8 | 9 | #import "PSProfileStepper.h" 10 | 11 | #define GEN_SETTER(PROPERTY, TYPE, SETTER, UPDATER) \ 12 | - (void)SETTER:(TYPE)PROPERTY { \ 13 | if (_##PROPERTY != PROPERTY) { \ 14 | _##PROPERTY = PROPERTY; \ 15 | UPDATER \ 16 | [self setNeedsLayout]; \ 17 | } \ 18 | } 19 | 20 | @interface PSTextLayer : CATextLayer 21 | @end 22 | @implementation PSTextLayer 23 | 24 | -(id)init 25 | { 26 | self = [super init]; 27 | if (self) { 28 | } 29 | return self; 30 | } 31 | -(id)initWithLayer:(id)layer 32 | { 33 | self = [super initWithLayer:layer]; 34 | if (self) { 35 | } 36 | return self; 37 | } 38 | -(id)initWithCoder:(id)coder 39 | { 40 | self = [super initWithCoder:coder]; 41 | if (self) { 42 | } 43 | return self; 44 | } 45 | -(void)drawInContext:(CGContextRef)ctx 46 | { 47 | CGFloat height = self.bounds.size.height; 48 | CGFloat fontSize = self.fontSize; 49 | CGFloat yDiff = (height-fontSize)/2 - fontSize/10; 50 | CGContextSaveGState(ctx); 51 | CGContextTranslateCTM(ctx, 0.0, yDiff); 52 | [super drawInContext:ctx]; 53 | CGContextRestoreGState(ctx); 54 | } 55 | @end 56 | 57 | static NSString * const kTrackAnimation = @"kTrackAnimation"; 58 | 59 | typedef void (^withoutAnimationBlock)(void); 60 | void withoutCAAnimation(withoutAnimationBlock code) 61 | { 62 | [CATransaction begin]; 63 | [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions]; 64 | code(); 65 | [CATransaction commit]; 66 | } 67 | 68 | @interface PSProfileStepper () 69 | { 70 | CAShapeLayer *_lineLayer; 71 | CAShapeLayer *_sliderCircleLayer; 72 | 73 | NSMutableArray *_lineCirclesArray; 74 | NSMutableArray *_circleLabelsArray; 75 | 76 | BOOL animateLayouts; 77 | 78 | CGFloat maxRadius; 79 | CGFloat diff; 80 | 81 | CGPoint startTouchPosition; 82 | CGPoint startSliderPosition; 83 | } 84 | 85 | 86 | @end 87 | 88 | @implementation PSProfileStepper 89 | 90 | #pragma mark - Init 91 | 92 | - (instancetype)init 93 | { 94 | self = [super init]; 95 | if (self) { 96 | [self initialSetup]; 97 | } 98 | return self; 99 | } 100 | 101 | - (instancetype)initWithFrame:(CGRect)frame 102 | { 103 | self = [super initWithFrame:frame]; 104 | if (self) { 105 | [self initialSetup]; 106 | } 107 | return self; 108 | } 109 | 110 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 111 | { 112 | self = [super initWithCoder:aDecoder]; 113 | if (self) { 114 | _maxCount = 97; 115 | [self makeDefaultValues]; 116 | [self addLayers]; 117 | } 118 | return self; 119 | } 120 | 121 | - (void)makeDefaultValues { 122 | 123 | if(_labelFontName.length == 0) 124 | { 125 | _labelFontName = @"Helvetica-Bold"; 126 | } 127 | //Other properties... 128 | } 129 | 130 | 131 | - (void)addLayers 132 | { 133 | _lineCirclesArray = [[NSMutableArray alloc] init]; 134 | _circleLabelsArray = [[NSMutableArray alloc] init]; 135 | 136 | _lineLayer = [CAShapeLayer layer]; 137 | _sliderCircleLayer = [CAShapeLayer layer]; 138 | 139 | [self.layer addSublayer:_lineLayer]; 140 | [self.layer addSublayer:_sliderCircleLayer]; 141 | } 142 | 143 | - (void)initialSetup 144 | { 145 | [self addLayers]; 146 | 147 | 148 | _maxCount = 97; 149 | _circleCount = 2; 150 | _index = 2; 151 | _lineHeight = 4.f; 152 | _lineCircleRadius = 5.f; 153 | _sliderCircleRadius = 12.5f; 154 | _labelFontSize = 10.f; 155 | _labelFontName = @"Helvetica-Bold"; 156 | _lineColor = [UIColor colorWithWhite:0.41f alpha:1.f]; 157 | _sliderCircleColor = [UIColor whiteColor]; 158 | 159 | 160 | [self setNeedsLayout]; 161 | } 162 | 163 | - (void)layoutLayersAnimated:(BOOL)animated 164 | { 165 | NSInteger indexDiff = fabsf(roundf([self indexCalculate]) - self.index); 166 | BOOL left = (roundf([self indexCalculate]) - self.index) < 0; 167 | 168 | CGRect contentFrame = CGRectMake(maxRadius, 0.f, self.bounds.size.width - 2 * maxRadius, self.bounds.size.height); 169 | 170 | CGFloat stepWidth = contentFrame.size.width / (self.maxCount - 1); 171 | CGFloat circleFrameSide = self.lineCircleRadius * 2.f; 172 | CGFloat sliderDiameter = self.sliderCircleRadius * 2.f; 173 | CGFloat sliderFrameSide = fmaxf(self.sliderCircleRadius * 2.f, 44.f); 174 | CGRect sliderDrawRect = CGRectMake((sliderFrameSide - sliderDiameter) / 2.f, (sliderFrameSide - sliderDiameter) / 2.f, sliderDiameter, sliderDiameter); 175 | 176 | CGPoint oldPosition = _sliderCircleLayer.position; 177 | CGPathRef oldPath = _lineLayer.path; 178 | 179 | if (!animated) { 180 | [CATransaction begin]; 181 | [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions]; 182 | } 183 | 184 | _sliderCircleLayer.frame = CGRectMake(0.f, 0.f, sliderFrameSide, sliderFrameSide); 185 | _sliderCircleLayer.path = [UIBezierPath bezierPathWithRoundedRect:sliderDrawRect cornerRadius:sliderFrameSide / 2].CGPath; 186 | _sliderCircleLayer.fillColor = [self.sliderCircleColor CGColor]; 187 | _sliderCircleLayer.position = CGPointMake(contentFrame.origin.x + stepWidth * self.index , (contentFrame.size.height ) / 2.f); 188 | 189 | if (animated) { 190 | CABasicAnimation *basicSliderAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; 191 | basicSliderAnimation.duration = [CATransaction animationDuration]; 192 | basicSliderAnimation.fromValue = [NSValue valueWithCGPoint:(oldPosition)]; 193 | [_sliderCircleLayer addAnimation:basicSliderAnimation forKey:@"position"]; 194 | } 195 | 196 | _lineLayer.frame = CGRectMake(contentFrame.origin.x, 197 | (contentFrame.size.height - self.lineHeight) / 2.f, 198 | contentFrame.size.width, 199 | self.lineHeight); 200 | _lineLayer.path = [self fillingPath]; 201 | _lineLayer.backgroundColor = [self.lineColor CGColor]; 202 | _lineLayer.fillColor = [self.tintColor CGColor]; 203 | 204 | if (animated) { 205 | CABasicAnimation *basicTrackAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; 206 | basicTrackAnimation.duration = [CATransaction animationDuration]; 207 | basicTrackAnimation.fromValue = (__bridge id _Nullable)(oldPath); 208 | [_lineLayer addAnimation:basicTrackAnimation forKey:@"path"]; 209 | } 210 | 211 | 212 | if (_lineCirclesArray.count > self.maxCount) { 213 | 214 | for (NSUInteger i = self.maxCount; i < _lineCirclesArray.count; i++) { 215 | [_lineCirclesArray[i] removeFromSuperlayer]; 216 | } 217 | 218 | _lineCirclesArray = [[_lineCirclesArray subarrayWithRange:NSMakeRange(0, self.maxCount)] mutableCopy]; 219 | } 220 | 221 | if (_circleLabelsArray.count > self.maxCount) { 222 | 223 | for (NSUInteger i = self.maxCount; i < _circleLabelsArray.count; i++) { 224 | [_circleLabelsArray[i] removeFromSuperlayer]; 225 | } 226 | 227 | _circleLabelsArray = [[_circleLabelsArray subarrayWithRange:NSMakeRange(0, self.maxCount)] mutableCopy]; 228 | } 229 | 230 | 231 | 232 | NSTimeInterval animationTimeDiff = left ? [CATransaction animationDuration] / indexDiff : -[CATransaction animationDuration] / indexDiff; 233 | NSTimeInterval animationTime = left ? animationTimeDiff : [CATransaction animationDuration] + animationTimeDiff; 234 | int steperCount=0; 235 | NSLog(@"Cccc:::%lu",(_circleCount-1)); 236 | for (NSUInteger i = 0; i < self.maxCount; i++) { 237 | 238 | CAShapeLayer *lineCircle; 239 | PSTextLayer *circleLabel; 240 | 241 | if (i < _lineCirclesArray.count) { 242 | lineCircle = _lineCirclesArray[i]; 243 | } else { 244 | lineCircle = [CAShapeLayer layer]; 245 | 246 | if (i%(self.maxCount/(_circleCount-1)) == 0 ) 247 | { 248 | [self.layer addSublayer:lineCircle]; 249 | } 250 | 251 | [_lineCirclesArray addObject:lineCircle]; 252 | } 253 | 254 | if (i < _circleLabelsArray.count) { 255 | circleLabel = _circleLabelsArray[i]; 256 | } else { 257 | circleLabel = [PSTextLayer layer]; 258 | [circleLabel setFont:(__bridge CFTypeRef _Nullable)(_labelFontName)]; 259 | [circleLabel setFontSize:_labelFontSize]; 260 | [circleLabel setAlignmentMode:kCAAlignmentCenter]; 261 | circleLabel.backgroundColor = [UIColor clearColor].CGColor; 262 | if (i%(self.maxCount/(_circleCount-1)) == 0 ) 263 | { 264 | [self.layer addSublayer:circleLabel]; 265 | [circleLabel setString:[NSString stringWithFormat:@"%d",steperCount++]]; 266 | } 267 | 268 | [_circleLabelsArray addObject:circleLabel]; 269 | } 270 | 271 | lineCircle.frame = CGRectMake(0.f, 0.f, circleFrameSide, circleFrameSide); 272 | lineCircle.path = [UIBezierPath bezierPathWithRoundedRect:lineCircle.bounds cornerRadius:circleFrameSide / 2].CGPath; 273 | lineCircle.position = CGPointMake(contentFrame.origin.x + stepWidth * i, contentFrame.size.height / 2.f); 274 | 275 | circleLabel.frame = CGRectMake(0.f, 0.f, circleFrameSide, circleFrameSide); 276 | circleLabel.position = CGPointMake(contentFrame.origin.x + stepWidth * i, contentFrame.size.height / 2.f); 277 | //lineCircle fillColor 278 | if (animated) { 279 | CGColorRef newColor = [self lineInnerCircleColor:lineCircle]; 280 | CGColorRef oldColor = lineCircle.fillColor; 281 | 282 | if (!CGColorEqualToColor(newColor, lineCircle.fillColor)) { 283 | 284 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(animationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 285 | lineCircle.fillColor = newColor; 286 | 287 | CABasicAnimation *basicTrackCircleAnimation = [CABasicAnimation animationWithKeyPath:@"fillColor"]; 288 | basicTrackCircleAnimation.duration = [CATransaction animationDuration] / 2.f; 289 | basicTrackCircleAnimation.fromValue = (__bridge id _Nullable)(oldColor); 290 | [lineCircle addAnimation:basicTrackCircleAnimation forKey:@"fillColor"]; 291 | }); 292 | 293 | animationTime += animationTimeDiff; 294 | } 295 | } else { 296 | lineCircle.fillColor = [self lineInnerCircleColor:lineCircle]; 297 | } 298 | //lineCircle strokeColor 299 | if (animated) { 300 | CGColorRef newColorInner = [self lineCircleColor:lineCircle]; 301 | CGColorRef oldColorInner = lineCircle.strokeColor; 302 | 303 | if (!CGColorEqualToColor(newColorInner, lineCircle.strokeColor)) { 304 | 305 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(animationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 306 | lineCircle.strokeColor = newColorInner; 307 | lineCircle.lineWidth = 2.0f; 308 | 309 | CABasicAnimation *basicTrackCircleInnerAnimation = [CABasicAnimation animationWithKeyPath:@"strokeColor"]; 310 | basicTrackCircleInnerAnimation.duration = [CATransaction animationDuration] / 2.f; 311 | basicTrackCircleInnerAnimation.fromValue = (__bridge id _Nullable)(oldColorInner); 312 | [lineCircle addAnimation:basicTrackCircleInnerAnimation forKey:@"strokeColor"]; 313 | }); 314 | 315 | animationTime += animationTimeDiff; 316 | } 317 | } else { 318 | lineCircle.strokeColor = [self lineCircleColor:lineCircle]; 319 | lineCircle.lineWidth = 2.0f; 320 | } 321 | 322 | //TrackLabel foregroundColor 323 | if (animated) { 324 | CGColorRef newColorLabel = [self circleLabelColor:circleLabel]; 325 | CGColorRef oldColorLabel = circleLabel.foregroundColor; 326 | 327 | if (!CGColorEqualToColor(newColorLabel, circleLabel.foregroundColor)) { 328 | 329 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(animationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 330 | 331 | circleLabel.foregroundColor = newColorLabel; 332 | 333 | CABasicAnimation *basicTrackLabelAnimation = [CABasicAnimation animationWithKeyPath:@"foregroundColor"]; 334 | basicTrackLabelAnimation.duration = [CATransaction animationDuration] / 2.f; 335 | basicTrackLabelAnimation.fromValue = (__bridge id _Nullable)(oldColorLabel); 336 | [circleLabel addAnimation:basicTrackLabelAnimation forKey:@"foregroundColor"]; 337 | }); 338 | 339 | animationTime += animationTimeDiff; 340 | } 341 | } else { 342 | circleLabel.foregroundColor = [self circleLabelColor:circleLabel]; 343 | ; 344 | 345 | } 346 | 347 | } 348 | 349 | if (!animated) { 350 | [CATransaction commit]; 351 | } 352 | 353 | [_sliderCircleLayer removeFromSuperlayer]; 354 | [self.layer addSublayer:_sliderCircleLayer]; 355 | 356 | } 357 | 358 | - (void)layoutSubviews 359 | { 360 | [super layoutSubviews]; 361 | 362 | [self layoutLayersAnimated:animateLayouts]; 363 | animateLayouts = NO; 364 | } 365 | 366 | #pragma mark - Helpers Method 367 | /** 368 | Calculate distance from lineCircle center to point where circle cross line. 369 | */ 370 | - (void)updateDiff 371 | { 372 | diff = sqrtf(fmaxf(0.f, powf(self.lineCircleRadius, 2.f) - pow(self.lineHeight / 2.f, 2.f))); 373 | } 374 | 375 | - (void)updateMaxRadius 376 | { 377 | maxRadius = fmaxf(self.lineCircleRadius, self.sliderCircleRadius); 378 | } 379 | 380 | - (void)updateIndex 381 | { 382 | if (_index > (_maxCount - 1)) { 383 | _index = _maxCount - 1; 384 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 385 | } 386 | } 387 | 388 | - (CGPathRef)fillingPath 389 | { 390 | CGRect fillRect = _lineLayer.bounds; 391 | fillRect.size.width = self.sliderPosition; 392 | 393 | return [UIBezierPath bezierPathWithRect:fillRect].CGPath; 394 | } 395 | 396 | - (CGFloat)sliderPosition 397 | { 398 | return _sliderCircleLayer.position.x - maxRadius; 399 | } 400 | 401 | - (CGFloat)lineCirclePosition:(CAShapeLayer *)lineCircle 402 | { 403 | return lineCircle.position.x - maxRadius; 404 | } 405 | 406 | - (CGFloat)circleLabelPosition:(PSTextLayer *)circleLabel 407 | { 408 | return circleLabel.position.x - maxRadius; 409 | } 410 | 411 | 412 | - (CGFloat)indexCalculate 413 | { 414 | return self.sliderPosition / (_lineLayer.bounds.size.width / (self.maxCount - 1)); 415 | } 416 | 417 | - (CGColorRef)lineCircleColor:(CAShapeLayer *)lineCircle 418 | { 419 | return self.sliderPosition + diff >= [self lineCirclePosition:lineCircle] ? self.tintColor.CGColor : self.lineColor.CGColor; 420 | } 421 | 422 | - (CGColorRef)circleLabelColor:(PSTextLayer *)circleLabel 423 | { 424 | return self.sliderPosition + diff >= [self circleLabelPosition:circleLabel] ? self.tintColor.CGColor : self.lineColor.CGColor; 425 | } 426 | 427 | - (CGColorRef)lineInnerCircleColor:(CAShapeLayer *)lineCircle 428 | { 429 | return self.sliderPosition + diff >= [self lineCirclePosition:lineCircle] ? self.innerCircleColor.CGColor : self.innerCircleNormalColor.CGColor; 430 | } 431 | 432 | #pragma mark - Touches 433 | 434 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event 435 | { 436 | startTouchPosition = [touch locationInView:self]; 437 | startSliderPosition = _sliderCircleLayer.position; 438 | 439 | if (CGRectContainsPoint(_sliderCircleLayer.frame, startTouchPosition)) { 440 | return YES; 441 | } else { 442 | for (NSUInteger i = 0; i < _lineCirclesArray.count; i++) { 443 | CALayer *dot = _lineCirclesArray[i]; 444 | 445 | CGFloat dotRadiusDiff = 22 - self.lineCircleRadius; 446 | CGRect frameToCheck = dotRadiusDiff > 0 ? CGRectInset(dot.frame, -dotRadiusDiff, -dotRadiusDiff) : dot.frame; 447 | 448 | if (CGRectContainsPoint(frameToCheck, startTouchPosition)) { 449 | NSUInteger oldIndex = _index; 450 | 451 | _index = i; 452 | 453 | if (oldIndex != _index) { 454 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 455 | } 456 | animateLayouts = YES; 457 | [self setNeedsLayout]; 458 | return NO; 459 | } 460 | } 461 | } 462 | return NO; 463 | } 464 | 465 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event 466 | { 467 | CGFloat position = startSliderPosition.x - (startTouchPosition.x - [touch locationInView:self].x); 468 | CGFloat limitedPosition = fminf(fmaxf(maxRadius, position), self.bounds.size.width - maxRadius); 469 | 470 | withoutCAAnimation(^{ 471 | _sliderCircleLayer.position = CGPointMake(limitedPosition, _sliderCircleLayer.position.y); 472 | _lineLayer.path = [self fillingPath]; 473 | 474 | NSUInteger index = (self.sliderPosition + diff) / (_lineLayer.bounds.size.width / (self.maxCount - 1)); 475 | if (_index != index) { 476 | for (CAShapeLayer *lineCircle in _lineCirclesArray) { 477 | lineCircle.strokeColor = [self lineCircleColor:lineCircle]; 478 | lineCircle.lineWidth = 2.0f; 479 | lineCircle.fillColor = [self lineInnerCircleColor:lineCircle]; 480 | } 481 | 482 | for (PSTextLayer *circleLabel in _circleLabelsArray) { 483 | circleLabel.foregroundColor = [self circleLabelColor:circleLabel]; 484 | } 485 | 486 | 487 | _index = index; 488 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 489 | } 490 | 491 | 492 | }); 493 | 494 | return YES; 495 | } 496 | 497 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event 498 | { 499 | [self endTouches]; 500 | } 501 | 502 | - (void)cancelTrackingWithEvent:(UIEvent *)event 503 | { 504 | [self endTouches]; 505 | } 506 | 507 | - (void)endTouches 508 | { 509 | NSUInteger newIndex = roundf([self indexCalculate]); 510 | 511 | if (newIndex != _index) { 512 | _index = newIndex; 513 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 514 | } 515 | 516 | animateLayouts = YES; 517 | [self setNeedsLayout]; 518 | } 519 | 520 | #pragma mark - Access methods 521 | 522 | - (void)setIndex:(NSUInteger)index animated:(BOOL)animated 523 | { 524 | animateLayouts = animated; 525 | self.index = index; 526 | } 527 | 528 | - (void)setTintColor:(UIColor *)tintColor 529 | { 530 | [super setTintColor:tintColor]; 531 | [self setNeedsLayout]; 532 | } 533 | 534 | GEN_SETTER(index, NSUInteger, setIndex, [self sendActionsForControlEvents:UIControlEventValueChanged];); 535 | GEN_SETTER(maxCount, NSUInteger, setMaxCount, [self updateIndex];); 536 | GEN_SETTER(lineHeight, CGFloat, setLineHeight, [self updateDiff];); 537 | GEN_SETTER(lineCircleRadius, CGFloat, setLineCircleRadius, [self updateDiff]; [self updateMaxRadius];); 538 | GEN_SETTER(sliderCircleRadius, CGFloat, setSliderCircleRadius, [self updateMaxRadius];); 539 | GEN_SETTER(lineColor, UIColor*, setLineColor, ); 540 | GEN_SETTER(sliderCircleColor, UIColor*, setSliderCircleColor, ); 541 | 542 | @end 543 | -------------------------------------------------------------------------------- /PSProfileStepperDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PSProfileStepperDemo 4 | // 5 | // Created by Pramod Kumar Pranav on 5/27/16. 6 | // Copyright © 2016 Pramod. 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 | # PSProfileStepper 2 | 3 | Screen Shot: 4 | 5 | ![Alt text](https://cloud.githubusercontent.com/assets/3591451/16255080/ee60e056-3863-11e6-9120-35f7f6c2fa18.png "") 6 | 7 | PSProfileStepper is custom implementation of stepper such as UISlider for show their step by its value 0,1,2.. and by change the color. 8 | 9 | It is implemented by the drawing layer CAShapeLayer and CATextLayer and Stepper is managed by its indexes. 10 | 11 | You can add PSProfileStepper directly in your storybord or xib, Or You can add it from setting its frame by the code. 12 | 13 | Requirements: 14 | 15 | xcode 7+ 16 | 17 | iOS 7+ 18 | 19 | Installation: 20 | 21 | Copy PSProfileStepper folder and paste it in your project. 22 | -------------------------------------------------------------------------------- /Simulator Screen Shot Jun 22, 2016, 10.13.57 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PKPranav/PSProfileStepper/7bdbca205b235b2830c1ad340fff33f12aad9c6b/Simulator Screen Shot Jun 22, 2016, 10.13.57 AM.png --------------------------------------------------------------------------------