├── Demo └── SGCodeTextField-Demo │ ├── SGCodeTextField-Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── SGCodeTextField-Demo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── LICENSE ├── Package.swift ├── README.md ├── SGCodeTextField.podspec ├── SGCodeTextField.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── Sources └── SGCodeTextField ├── Info.plist ├── SGCodeTextField.h ├── SGCodeTextField.swift └── SGCodeTextFieldConfiguration.swift /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B00D749521E4EFB500C65E01 /* SGCodeTextField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B00D748D21E4EF5900C65E01 /* SGCodeTextField.framework */; }; 11 | B00D749621E4EFB500C65E01 /* SGCodeTextField.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B00D748D21E4EF5900C65E01 /* SGCodeTextField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12 | B0E6DF1721C8E5400016F4F5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E6DF1621C8E5400016F4F5 /* AppDelegate.swift */; }; 13 | B0E6DF1921C8E5400016F4F5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E6DF1821C8E5400016F4F5 /* ViewController.swift */; }; 14 | B0E6DF1C21C8E5400016F4F5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E6DF1A21C8E5400016F4F5 /* Main.storyboard */; }; 15 | B0E6DF1E21C8E5420016F4F5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B0E6DF1D21C8E5420016F4F5 /* Assets.xcassets */; }; 16 | B0E6DF2121C8E5420016F4F5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E6DF1F21C8E5420016F4F5 /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | B00D748C21E4EF5900C65E01 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = B00D748821E4EF5900C65E01 /* SGCodeTextField.xcodeproj */; 23 | proxyType = 2; 24 | remoteGlobalIDString = B0E6DEFF21C8E4400016F4F5; 25 | remoteInfo = SGCodeTextField; 26 | }; 27 | B00D749721E4EFB500C65E01 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = B00D748821E4EF5900C65E01 /* SGCodeTextField.xcodeproj */; 30 | proxyType = 1; 31 | remoteGlobalIDString = B0E6DEFE21C8E4400016F4F5; 32 | remoteInfo = SGCodeTextField; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXCopyFilesBuildPhase section */ 37 | B00D749921E4EFB500C65E01 /* Embed Frameworks */ = { 38 | isa = PBXCopyFilesBuildPhase; 39 | buildActionMask = 2147483647; 40 | dstPath = ""; 41 | dstSubfolderSpec = 10; 42 | files = ( 43 | B00D749621E4EFB500C65E01 /* SGCodeTextField.framework in Embed Frameworks */, 44 | ); 45 | name = "Embed Frameworks"; 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXCopyFilesBuildPhase section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | B00D748821E4EF5900C65E01 /* SGCodeTextField.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SGCodeTextField.xcodeproj; path = ../../SGCodeTextField.xcodeproj; sourceTree = ""; }; 52 | B0E6DF1321C8E5400016F4F5 /* SGCodeTextField-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SGCodeTextField-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | B0E6DF1621C8E5400016F4F5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 54 | B0E6DF1821C8E5400016F4F5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 55 | B0E6DF1B21C8E5400016F4F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 56 | B0E6DF1D21C8E5420016F4F5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 57 | B0E6DF2021C8E5420016F4F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 58 | B0E6DF2221C8E5420016F4F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | B0E6DF1021C8E5400016F4F5 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | B00D749521E4EFB500C65E01 /* SGCodeTextField.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | B00D748921E4EF5900C65E01 /* Products */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | B00D748D21E4EF5900C65E01 /* SGCodeTextField.framework */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | B00D748E21E4EF7600C65E01 /* Frameworks */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | ); 85 | name = Frameworks; 86 | sourceTree = ""; 87 | }; 88 | B0E6DF0A21C8E5400016F4F5 = { 89 | isa = PBXGroup; 90 | children = ( 91 | B00D748821E4EF5900C65E01 /* SGCodeTextField.xcodeproj */, 92 | B0E6DF1521C8E5400016F4F5 /* SGCodeTextField-Demo */, 93 | B0E6DF1421C8E5400016F4F5 /* Products */, 94 | B00D748E21E4EF7600C65E01 /* Frameworks */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | B0E6DF1421C8E5400016F4F5 /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | B0E6DF1321C8E5400016F4F5 /* SGCodeTextField-Demo.app */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | B0E6DF1521C8E5400016F4F5 /* SGCodeTextField-Demo */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | B0E6DF1621C8E5400016F4F5 /* AppDelegate.swift */, 110 | B0E6DF1821C8E5400016F4F5 /* ViewController.swift */, 111 | B0E6DF1A21C8E5400016F4F5 /* Main.storyboard */, 112 | B0E6DF1D21C8E5420016F4F5 /* Assets.xcassets */, 113 | B0E6DF1F21C8E5420016F4F5 /* LaunchScreen.storyboard */, 114 | B0E6DF2221C8E5420016F4F5 /* Info.plist */, 115 | ); 116 | path = "SGCodeTextField-Demo"; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXNativeTarget section */ 122 | B0E6DF1221C8E5400016F4F5 /* SGCodeTextField-Demo */ = { 123 | isa = PBXNativeTarget; 124 | buildConfigurationList = B0E6DF2521C8E5420016F4F5 /* Build configuration list for PBXNativeTarget "SGCodeTextField-Demo" */; 125 | buildPhases = ( 126 | B0E6DF0F21C8E5400016F4F5 /* Sources */, 127 | B0E6DF1021C8E5400016F4F5 /* Frameworks */, 128 | B0E6DF1121C8E5400016F4F5 /* Resources */, 129 | B00D749921E4EFB500C65E01 /* Embed Frameworks */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | B00D749821E4EFB500C65E01 /* PBXTargetDependency */, 135 | ); 136 | name = "SGCodeTextField-Demo"; 137 | productName = "SGCodeTextField-Demo"; 138 | productReference = B0E6DF1321C8E5400016F4F5 /* SGCodeTextField-Demo.app */; 139 | productType = "com.apple.product-type.application"; 140 | }; 141 | /* End PBXNativeTarget section */ 142 | 143 | /* Begin PBXProject section */ 144 | B0E6DF0B21C8E5400016F4F5 /* Project object */ = { 145 | isa = PBXProject; 146 | attributes = { 147 | LastSwiftUpdateCheck = 1010; 148 | LastUpgradeCheck = 1010; 149 | ORGANIZATIONNAME = "Sergiu Grigoriev"; 150 | TargetAttributes = { 151 | B0E6DF1221C8E5400016F4F5 = { 152 | CreatedOnToolsVersion = 10.1; 153 | }; 154 | }; 155 | }; 156 | buildConfigurationList = B0E6DF0E21C8E5400016F4F5 /* Build configuration list for PBXProject "SGCodeTextField-Demo" */; 157 | compatibilityVersion = "Xcode 9.3"; 158 | developmentRegion = en; 159 | hasScannedForEncodings = 0; 160 | knownRegions = ( 161 | en, 162 | Base, 163 | ); 164 | mainGroup = B0E6DF0A21C8E5400016F4F5; 165 | productRefGroup = B0E6DF1421C8E5400016F4F5 /* Products */; 166 | projectDirPath = ""; 167 | projectReferences = ( 168 | { 169 | ProductGroup = B00D748921E4EF5900C65E01 /* Products */; 170 | ProjectRef = B00D748821E4EF5900C65E01 /* SGCodeTextField.xcodeproj */; 171 | }, 172 | ); 173 | projectRoot = ""; 174 | targets = ( 175 | B0E6DF1221C8E5400016F4F5 /* SGCodeTextField-Demo */, 176 | ); 177 | }; 178 | /* End PBXProject section */ 179 | 180 | /* Begin PBXReferenceProxy section */ 181 | B00D748D21E4EF5900C65E01 /* SGCodeTextField.framework */ = { 182 | isa = PBXReferenceProxy; 183 | fileType = wrapper.framework; 184 | path = SGCodeTextField.framework; 185 | remoteRef = B00D748C21E4EF5900C65E01 /* PBXContainerItemProxy */; 186 | sourceTree = BUILT_PRODUCTS_DIR; 187 | }; 188 | /* End PBXReferenceProxy section */ 189 | 190 | /* Begin PBXResourcesBuildPhase section */ 191 | B0E6DF1121C8E5400016F4F5 /* Resources */ = { 192 | isa = PBXResourcesBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | B0E6DF2121C8E5420016F4F5 /* LaunchScreen.storyboard in Resources */, 196 | B0E6DF1E21C8E5420016F4F5 /* Assets.xcassets in Resources */, 197 | B0E6DF1C21C8E5400016F4F5 /* Main.storyboard in Resources */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | /* End PBXResourcesBuildPhase section */ 202 | 203 | /* Begin PBXSourcesBuildPhase section */ 204 | B0E6DF0F21C8E5400016F4F5 /* Sources */ = { 205 | isa = PBXSourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | B0E6DF1921C8E5400016F4F5 /* ViewController.swift in Sources */, 209 | B0E6DF1721C8E5400016F4F5 /* AppDelegate.swift in Sources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXSourcesBuildPhase section */ 214 | 215 | /* Begin PBXTargetDependency section */ 216 | B00D749821E4EFB500C65E01 /* PBXTargetDependency */ = { 217 | isa = PBXTargetDependency; 218 | name = SGCodeTextField; 219 | targetProxy = B00D749721E4EFB500C65E01 /* PBXContainerItemProxy */; 220 | }; 221 | /* End PBXTargetDependency section */ 222 | 223 | /* Begin PBXVariantGroup section */ 224 | B0E6DF1A21C8E5400016F4F5 /* Main.storyboard */ = { 225 | isa = PBXVariantGroup; 226 | children = ( 227 | B0E6DF1B21C8E5400016F4F5 /* Base */, 228 | ); 229 | name = Main.storyboard; 230 | sourceTree = ""; 231 | }; 232 | B0E6DF1F21C8E5420016F4F5 /* LaunchScreen.storyboard */ = { 233 | isa = PBXVariantGroup; 234 | children = ( 235 | B0E6DF2021C8E5420016F4F5 /* Base */, 236 | ); 237 | name = LaunchScreen.storyboard; 238 | sourceTree = ""; 239 | }; 240 | /* End PBXVariantGroup section */ 241 | 242 | /* Begin XCBuildConfiguration section */ 243 | B0E6DF2321C8E5420016F4F5 /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_ENABLE_OBJC_WEAK = YES; 254 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 255 | CLANG_WARN_BOOL_CONVERSION = YES; 256 | CLANG_WARN_COMMA = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 261 | CLANG_WARN_EMPTY_BODY = YES; 262 | CLANG_WARN_ENUM_CONVERSION = YES; 263 | CLANG_WARN_INFINITE_RECURSION = YES; 264 | CLANG_WARN_INT_CONVERSION = YES; 265 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 266 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 270 | CLANG_WARN_STRICT_PROTOTYPES = YES; 271 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 272 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | CODE_SIGN_IDENTITY = "iPhone Developer"; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = dwarf; 278 | ENABLE_STRICT_OBJC_MSGSEND = YES; 279 | ENABLE_TESTABILITY = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu11; 281 | GCC_DYNAMIC_NO_PIC = NO; 282 | GCC_NO_COMMON_BLOCKS = YES; 283 | GCC_OPTIMIZATION_LEVEL = 0; 284 | GCC_PREPROCESSOR_DEFINITIONS = ( 285 | "DEBUG=1", 286 | "$(inherited)", 287 | ); 288 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 289 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 290 | GCC_WARN_UNDECLARED_SELECTOR = YES; 291 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 292 | GCC_WARN_UNUSED_FUNCTION = YES; 293 | GCC_WARN_UNUSED_VARIABLE = YES; 294 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 295 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 296 | MTL_FAST_MATH = YES; 297 | ONLY_ACTIVE_ARCH = YES; 298 | SDKROOT = iphoneos; 299 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 300 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 301 | }; 302 | name = Debug; 303 | }; 304 | B0E6DF2421C8E5420016F4F5 /* Release */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | ALWAYS_SEARCH_USER_PATHS = NO; 308 | CLANG_ANALYZER_NONNULL = YES; 309 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_ENABLE_OBJC_WEAK = YES; 315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 316 | CLANG_WARN_BOOL_CONVERSION = YES; 317 | CLANG_WARN_COMMA = YES; 318 | CLANG_WARN_CONSTANT_CONVERSION = YES; 319 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 321 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 322 | CLANG_WARN_EMPTY_BODY = YES; 323 | CLANG_WARN_ENUM_CONVERSION = YES; 324 | CLANG_WARN_INFINITE_RECURSION = YES; 325 | CLANG_WARN_INT_CONVERSION = YES; 326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 331 | CLANG_WARN_STRICT_PROTOTYPES = YES; 332 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 333 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | CODE_SIGN_IDENTITY = "iPhone Developer"; 337 | COPY_PHASE_STRIP = NO; 338 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 339 | ENABLE_NS_ASSERTIONS = NO; 340 | ENABLE_STRICT_OBJC_MSGSEND = YES; 341 | GCC_C_LANGUAGE_STANDARD = gnu11; 342 | GCC_NO_COMMON_BLOCKS = YES; 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 350 | MTL_ENABLE_DEBUG_INFO = NO; 351 | MTL_FAST_MATH = YES; 352 | SDKROOT = iphoneos; 353 | SWIFT_COMPILATION_MODE = wholemodule; 354 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | B0E6DF2621C8E5420016F4F5 /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | CODE_SIGN_STYLE = Manual; 365 | DEVELOPMENT_TEAM = ""; 366 | INFOPLIST_FILE = "SGCodeTextField-Demo/Info.plist"; 367 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 368 | LD_RUNPATH_SEARCH_PATHS = ( 369 | "$(inherited)", 370 | "@executable_path/Frameworks", 371 | ); 372 | PRODUCT_BUNDLE_IDENTIFIER = "com.grigorievs.SGCodeTextField-Demo"; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | PROVISIONING_PROFILE_SPECIFIER = ""; 375 | SWIFT_VERSION = 4.2; 376 | TARGETED_DEVICE_FAMILY = "1,2"; 377 | }; 378 | name = Debug; 379 | }; 380 | B0E6DF2721C8E5420016F4F5 /* Release */ = { 381 | isa = XCBuildConfiguration; 382 | buildSettings = { 383 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 385 | CODE_SIGN_STYLE = Manual; 386 | DEVELOPMENT_TEAM = ""; 387 | INFOPLIST_FILE = "SGCodeTextField-Demo/Info.plist"; 388 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 389 | LD_RUNPATH_SEARCH_PATHS = ( 390 | "$(inherited)", 391 | "@executable_path/Frameworks", 392 | ); 393 | PRODUCT_BUNDLE_IDENTIFIER = "com.grigorievs.SGCodeTextField-Demo"; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | PROVISIONING_PROFILE_SPECIFIER = ""; 396 | SWIFT_VERSION = 4.2; 397 | TARGETED_DEVICE_FAMILY = "1,2"; 398 | }; 399 | name = Release; 400 | }; 401 | /* End XCBuildConfiguration section */ 402 | 403 | /* Begin XCConfigurationList section */ 404 | B0E6DF0E21C8E5400016F4F5 /* Build configuration list for PBXProject "SGCodeTextField-Demo" */ = { 405 | isa = XCConfigurationList; 406 | buildConfigurations = ( 407 | B0E6DF2321C8E5420016F4F5 /* Debug */, 408 | B0E6DF2421C8E5420016F4F5 /* Release */, 409 | ); 410 | defaultConfigurationIsVisible = 0; 411 | defaultConfigurationName = Release; 412 | }; 413 | B0E6DF2521C8E5420016F4F5 /* Build configuration list for PBXNativeTarget "SGCodeTextField-Demo" */ = { 414 | isa = XCConfigurationList; 415 | buildConfigurations = ( 416 | B0E6DF2621C8E5420016F4F5 /* Debug */, 417 | B0E6DF2721C8E5420016F4F5 /* Release */, 418 | ); 419 | defaultConfigurationIsVisible = 0; 420 | defaultConfigurationName = Release; 421 | }; 422 | /* End XCConfigurationList section */ 423 | }; 424 | rootObject = B0E6DF0B21C8E5400016F4F5 /* Project object */; 425 | } 426 | -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SGCodeTextField-Demo 4 | // 5 | // Copyright (c) 2019 Sergiu Grigoriev 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | 28 | @UIApplicationMain 29 | class AppDelegate: UIResponder, UIApplicationDelegate { 30 | 31 | var window: UIWindow? 32 | 33 | 34 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 35 | // Override point for customization after application launch. 36 | return true 37 | } 38 | 39 | func applicationWillResignActive(_ application: UIApplication) { 40 | // 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. 41 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 42 | } 43 | 44 | func applicationDidEnterBackground(_ application: UIApplication) { 45 | // 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. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | func applicationWillEnterForeground(_ application: UIApplication) { 50 | // 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. 51 | } 52 | 53 | func applicationDidBecomeActive(_ application: UIApplication) { 54 | // 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. 55 | } 56 | 57 | func applicationWillTerminate(_ application: UIApplication) { 58 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 59 | } 60 | 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo/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 | } -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo/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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo/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 | 32 | 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 | -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo/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 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Demo/SGCodeTextField-Demo/SGCodeTextField-Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SGCodeTextField-Demo 4 | // 5 | // Copyright (c) 2019 Sergiu Grigoriev 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import SGCodeTextField 28 | 29 | class ViewController: UIViewController { 30 | 31 | // outlets 32 | @IBOutlet weak var codeTextField: SGCodeTextField! 33 | @IBOutlet weak var doneButton: UIButton! 34 | 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | 38 | self.customize() 39 | self.addGestures() 40 | } 41 | 42 | func customize() { 43 | self.codeTextField.count = 4 44 | self.codeTextField.placeholder = "*" 45 | self.codeTextField.textColorFocused = UIColor.brown 46 | self.codeTextField.refreshUI() 47 | 48 | // self.codeTextField.text = "1234" 49 | 50 | self.codeTextField.textChangeHandler = { text, completed in 51 | self.doneButton.isSelected = completed 52 | print(text ?? "") 53 | } 54 | 55 | } 56 | 57 | func addGestures() { 58 | let tap = UITapGestureRecognizer(target: self, action: #selector(onTap)) 59 | self.view.addGestureRecognizer(tap) 60 | } 61 | 62 | // MARK: - Actions 63 | 64 | @objc func onTap() { 65 | let _ = self.codeTextField.resignFirstResponder() 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sergiu Grigoriev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "SGCodeTextField", 8 | platforms: [.iOS(.v9), .tvOS(.v9)], 9 | products: [ 10 | .library( 11 | name: "SGCodeTextField", 12 | targets: ["SGCodeTextField"] 13 | ), 14 | ], 15 | targets: [ 16 | .target(name: "SGCodeTextField") 17 | ] 18 | ) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # SGCodeTextField 4 | 5 | Simple pin code textfield for iOS and tvOS. Customizable via Interface Builder or code. 6 | 7 | # Wiki 8 | 9 | ## Installation 10 | via CocoaPods 11 | 12 | pod "SGCodeTextField" 13 | 14 | 15 | via SwiftPackageManager 16 | 17 | dependencies: [ 18 | .package(url: "https://github.com/grigorievs/SGCodeTextField.git", .upToNextMajor(from: "0.1.4")) 19 | ] 20 | 21 | ## Customisable properties 22 | 23 | - count 24 | - placeholder 25 | - autocapitalization 26 | - font 27 | - digitCornerRadius 28 | - digitBackgroundColor 29 | - digitBackgroundColorFocused 30 | - digitBackgroundColorEmpty 31 | - digitBorderColor 32 | - digitBorderColorFocused 33 | - digitBorderColorEmpty 34 | - digitBorderWidth 35 | - digitSpacing 36 | - textColor 37 | - textColorFocused 38 | - placeholderColor 39 | - keyboardType 40 | - isSecureTextEntry 41 | - secureTextEntryMaskCharacter 42 | 43 | Customizing via code: 44 | 45 | self.codeTextField.count = 4 46 | 47 | self.codeTextField.placeholder = "*" 48 | 49 | self.codeTextField.textColorFocused = UIColor.brown 50 | 51 | self.codeTextField.refreshUI() 52 | 53 | 54 | ## Usage 55 | 56 | Interface Builder 57 | 58 | Drag and drop a 'UIView' on to the desired view and change class to 'SGCodeTextField'. 59 | 60 | Code 61 | 62 | let codeTextField = SGCodeTextField() 63 | 64 | Getting text: 65 | 66 | import SGCodeTextField 67 | 68 | let value = self.codeTextField.text 69 | 70 | Setting text: 71 | 72 | self.codeTextField.text = "1234" 73 | 74 | Observing text change events: 75 | 76 | self.codeTextField.textChangeHandler = { text, completed in 77 | 78 | self.doneButton.isSelected = completed 79 | 80 | print(text ?? "") 81 | 82 | } 83 | 84 | # Screenshots 85 | 86 | Editing mode 87 | Default mode 88 | 89 | 90 | ### Other customisations 91 | 92 | Editing mode 93 | -------------------------------------------------------------------------------- /SGCodeTextField.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint SGCodeTextField.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'SGCodeTextField' 11 | s.version = '0.1.4' 12 | s.summary = 'Simple pin code textfield for iOS and tvOS.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | Simple pin code textfield. Customizable via Interface Builder or code. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/grigorievs/SGCodeTextField' 25 | s.screenshots = 'https://i.imgur.com/lu3lVkD.png', 'https://i.imgur.com/ZCy3YLV.png' 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { 'Sergiu Grigoriev' => 'sergiugrig@gmail.com' } 28 | s.source = { :git => 'https://github.com/grigorievs/SGCodeTextField.git', :tag => s.version} 29 | s.social_media_url = 'https://twitter.com/grigorievs' 30 | 31 | s.swift_versions = ['4.0', '4.2', '5.0', '5.1', '5.2'] 32 | 33 | s.ios.deployment_target = '9.0' 34 | s.tvos.deployment_target = '9.0' 35 | 36 | s.source_files = 'Sources/SGCodeTextField/*.swift' 37 | s.frameworks = 'UIKit' 38 | 39 | end 40 | -------------------------------------------------------------------------------- /SGCodeTextField.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B005337B25152A0D008D9334 /* SGCodeTextFieldConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005337725152A0D008D9334 /* SGCodeTextFieldConfiguration.swift */; }; 11 | B005337D25152A0D008D9334 /* SGCodeTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005337925152A0D008D9334 /* SGCodeTextField.swift */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | B005337725152A0D008D9334 /* SGCodeTextFieldConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SGCodeTextFieldConfiguration.swift; sourceTree = ""; }; 16 | B005337825152A0D008D9334 /* SGCodeTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SGCodeTextField.h; sourceTree = ""; }; 17 | B005337925152A0D008D9334 /* SGCodeTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SGCodeTextField.swift; sourceTree = ""; }; 18 | B005337A25152A0D008D9334 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 19 | B0E6DEFF21C8E4400016F4F5 /* SGCodeTextField.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SGCodeTextField.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | /* End PBXFileReference section */ 21 | 22 | /* Begin PBXFrameworksBuildPhase section */ 23 | B0E6DEFC21C8E4400016F4F5 /* Frameworks */ = { 24 | isa = PBXFrameworksBuildPhase; 25 | buildActionMask = 2147483647; 26 | files = ( 27 | ); 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXFrameworksBuildPhase section */ 31 | 32 | /* Begin PBXGroup section */ 33 | B005337625152A0D008D9334 /* SGCodeTextField */ = { 34 | isa = PBXGroup; 35 | children = ( 36 | B005337725152A0D008D9334 /* SGCodeTextFieldConfiguration.swift */, 37 | B005337825152A0D008D9334 /* SGCodeTextField.h */, 38 | B005337925152A0D008D9334 /* SGCodeTextField.swift */, 39 | B005337A25152A0D008D9334 /* Info.plist */, 40 | ); 41 | name = SGCodeTextField; 42 | path = Sources/SGCodeTextField; 43 | sourceTree = ""; 44 | }; 45 | B0E6DEF521C8E4400016F4F5 = { 46 | isa = PBXGroup; 47 | children = ( 48 | B005337625152A0D008D9334 /* SGCodeTextField */, 49 | B0E6DF0021C8E4400016F4F5 /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | B0E6DF0021C8E4400016F4F5 /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | B0E6DEFF21C8E4400016F4F5 /* SGCodeTextField.framework */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | /* End PBXGroup section */ 62 | 63 | /* Begin PBXHeadersBuildPhase section */ 64 | B0E6DEFA21C8E4400016F4F5 /* Headers */ = { 65 | isa = PBXHeadersBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXHeadersBuildPhase section */ 72 | 73 | /* Begin PBXNativeTarget section */ 74 | B0E6DEFE21C8E4400016F4F5 /* SGCodeTextField */ = { 75 | isa = PBXNativeTarget; 76 | buildConfigurationList = B0E6DF0721C8E4400016F4F5 /* Build configuration list for PBXNativeTarget "SGCodeTextField" */; 77 | buildPhases = ( 78 | B0E6DEFA21C8E4400016F4F5 /* Headers */, 79 | B0E6DEFB21C8E4400016F4F5 /* Sources */, 80 | B0E6DEFC21C8E4400016F4F5 /* Frameworks */, 81 | B0E6DEFD21C8E4400016F4F5 /* Resources */, 82 | ); 83 | buildRules = ( 84 | ); 85 | dependencies = ( 86 | ); 87 | name = SGCodeTextField; 88 | productName = SGCodeTextField; 89 | productReference = B0E6DEFF21C8E4400016F4F5 /* SGCodeTextField.framework */; 90 | productType = "com.apple.product-type.framework"; 91 | }; 92 | /* End PBXNativeTarget section */ 93 | 94 | /* Begin PBXProject section */ 95 | B0E6DEF621C8E4400016F4F5 /* Project object */ = { 96 | isa = PBXProject; 97 | attributes = { 98 | CLASSPREFIX = SG; 99 | LastUpgradeCheck = 1010; 100 | ORGANIZATIONNAME = "Sergiu Grigoriev"; 101 | TargetAttributes = { 102 | B0E6DEFE21C8E4400016F4F5 = { 103 | CreatedOnToolsVersion = 10.1; 104 | LastSwiftMigration = 1010; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = B0E6DEF921C8E4400016F4F5 /* Build configuration list for PBXProject "SGCodeTextField" */; 109 | compatibilityVersion = "Xcode 9.3"; 110 | developmentRegion = en; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | ); 115 | mainGroup = B0E6DEF521C8E4400016F4F5; 116 | productRefGroup = B0E6DF0021C8E4400016F4F5 /* Products */; 117 | projectDirPath = ""; 118 | projectRoot = ""; 119 | targets = ( 120 | B0E6DEFE21C8E4400016F4F5 /* SGCodeTextField */, 121 | ); 122 | }; 123 | /* End PBXProject section */ 124 | 125 | /* Begin PBXResourcesBuildPhase section */ 126 | B0E6DEFD21C8E4400016F4F5 /* Resources */ = { 127 | isa = PBXResourcesBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | ); 131 | runOnlyForDeploymentPostprocessing = 0; 132 | }; 133 | /* End PBXResourcesBuildPhase section */ 134 | 135 | /* Begin PBXSourcesBuildPhase section */ 136 | B0E6DEFB21C8E4400016F4F5 /* Sources */ = { 137 | isa = PBXSourcesBuildPhase; 138 | buildActionMask = 2147483647; 139 | files = ( 140 | B005337B25152A0D008D9334 /* SGCodeTextFieldConfiguration.swift in Sources */, 141 | B005337D25152A0D008D9334 /* SGCodeTextField.swift in Sources */, 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | /* End PBXSourcesBuildPhase section */ 146 | 147 | /* Begin XCBuildConfiguration section */ 148 | B0E6DF0521C8E4400016F4F5 /* Debug */ = { 149 | isa = XCBuildConfiguration; 150 | buildSettings = { 151 | ALWAYS_SEARCH_USER_PATHS = NO; 152 | CLANG_ANALYZER_NONNULL = YES; 153 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 154 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 155 | CLANG_CXX_LIBRARY = "libc++"; 156 | CLANG_ENABLE_MODULES = YES; 157 | CLANG_ENABLE_OBJC_ARC = YES; 158 | CLANG_ENABLE_OBJC_WEAK = YES; 159 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 160 | CLANG_WARN_BOOL_CONVERSION = YES; 161 | CLANG_WARN_COMMA = YES; 162 | CLANG_WARN_CONSTANT_CONVERSION = YES; 163 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 164 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 165 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 166 | CLANG_WARN_EMPTY_BODY = YES; 167 | CLANG_WARN_ENUM_CONVERSION = YES; 168 | CLANG_WARN_INFINITE_RECURSION = YES; 169 | CLANG_WARN_INT_CONVERSION = YES; 170 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 171 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 172 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 173 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 174 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 175 | CLANG_WARN_STRICT_PROTOTYPES = YES; 176 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 177 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 178 | CLANG_WARN_UNREACHABLE_CODE = YES; 179 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 180 | CODE_SIGN_IDENTITY = "iPhone Developer"; 181 | COPY_PHASE_STRIP = NO; 182 | CURRENT_PROJECT_VERSION = 1; 183 | DEBUG_INFORMATION_FORMAT = dwarf; 184 | ENABLE_STRICT_OBJC_MSGSEND = YES; 185 | ENABLE_TESTABILITY = YES; 186 | GCC_C_LANGUAGE_STANDARD = gnu11; 187 | GCC_DYNAMIC_NO_PIC = NO; 188 | GCC_NO_COMMON_BLOCKS = YES; 189 | GCC_OPTIMIZATION_LEVEL = 0; 190 | GCC_PREPROCESSOR_DEFINITIONS = ( 191 | "DEBUG=1", 192 | "$(inherited)", 193 | ); 194 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 195 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 196 | GCC_WARN_UNDECLARED_SELECTOR = YES; 197 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 198 | GCC_WARN_UNUSED_FUNCTION = YES; 199 | GCC_WARN_UNUSED_VARIABLE = YES; 200 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 201 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 202 | MTL_FAST_MATH = YES; 203 | ONLY_ACTIVE_ARCH = YES; 204 | SDKROOT = iphoneos; 205 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 206 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 207 | VERSIONING_SYSTEM = "apple-generic"; 208 | VERSION_INFO_PREFIX = ""; 209 | }; 210 | name = Debug; 211 | }; 212 | B0E6DF0621C8E4400016F4F5 /* Release */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | ALWAYS_SEARCH_USER_PATHS = NO; 216 | CLANG_ANALYZER_NONNULL = YES; 217 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_ENABLE_OBJC_WEAK = YES; 223 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 224 | CLANG_WARN_BOOL_CONVERSION = YES; 225 | CLANG_WARN_COMMA = YES; 226 | CLANG_WARN_CONSTANT_CONVERSION = YES; 227 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 230 | CLANG_WARN_EMPTY_BODY = YES; 231 | CLANG_WARN_ENUM_CONVERSION = YES; 232 | CLANG_WARN_INFINITE_RECURSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 236 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 238 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 239 | CLANG_WARN_STRICT_PROTOTYPES = YES; 240 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 241 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 242 | CLANG_WARN_UNREACHABLE_CODE = YES; 243 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 244 | CODE_SIGN_IDENTITY = "iPhone Developer"; 245 | COPY_PHASE_STRIP = NO; 246 | CURRENT_PROJECT_VERSION = 1; 247 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 248 | ENABLE_NS_ASSERTIONS = NO; 249 | ENABLE_STRICT_OBJC_MSGSEND = YES; 250 | GCC_C_LANGUAGE_STANDARD = gnu11; 251 | GCC_NO_COMMON_BLOCKS = YES; 252 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 253 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 254 | GCC_WARN_UNDECLARED_SELECTOR = YES; 255 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 256 | GCC_WARN_UNUSED_FUNCTION = YES; 257 | GCC_WARN_UNUSED_VARIABLE = YES; 258 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 259 | MTL_ENABLE_DEBUG_INFO = NO; 260 | MTL_FAST_MATH = YES; 261 | SDKROOT = iphoneos; 262 | SWIFT_COMPILATION_MODE = wholemodule; 263 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 264 | VALIDATE_PRODUCT = YES; 265 | VERSIONING_SYSTEM = "apple-generic"; 266 | VERSION_INFO_PREFIX = ""; 267 | }; 268 | name = Release; 269 | }; 270 | B0E6DF0821C8E4400016F4F5 /* Debug */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | CLANG_ENABLE_MODULES = YES; 274 | CODE_SIGN_IDENTITY = ""; 275 | CODE_SIGN_STYLE = Manual; 276 | DEFINES_MODULE = YES; 277 | DEVELOPMENT_TEAM = ""; 278 | DYLIB_COMPATIBILITY_VERSION = 1; 279 | DYLIB_CURRENT_VERSION = 1; 280 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 281 | INFOPLIST_FILE = Sources/SGCodeTextField/Info.plist; 282 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 283 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 284 | LD_RUNPATH_SEARCH_PATHS = ( 285 | "$(inherited)", 286 | "@executable_path/Frameworks", 287 | "@loader_path/Frameworks", 288 | ); 289 | MARKETING_VERSION = 0.1.4; 290 | PRODUCT_BUNDLE_IDENTIFIER = com.grigorievs.SGCodeTextField; 291 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 292 | PROVISIONING_PROFILE_SPECIFIER = ""; 293 | SKIP_INSTALL = YES; 294 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 295 | SWIFT_VERSION = 4.2; 296 | TARGETED_DEVICE_FAMILY = "1,2"; 297 | }; 298 | name = Debug; 299 | }; 300 | B0E6DF0921C8E4400016F4F5 /* Release */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | CLANG_ENABLE_MODULES = YES; 304 | CODE_SIGN_IDENTITY = ""; 305 | CODE_SIGN_STYLE = Manual; 306 | DEFINES_MODULE = YES; 307 | DEVELOPMENT_TEAM = ""; 308 | DYLIB_COMPATIBILITY_VERSION = 1; 309 | DYLIB_CURRENT_VERSION = 1; 310 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 311 | INFOPLIST_FILE = Sources/SGCodeTextField/Info.plist; 312 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 313 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 314 | LD_RUNPATH_SEARCH_PATHS = ( 315 | "$(inherited)", 316 | "@executable_path/Frameworks", 317 | "@loader_path/Frameworks", 318 | ); 319 | MARKETING_VERSION = 0.1.4; 320 | PRODUCT_BUNDLE_IDENTIFIER = com.grigorievs.SGCodeTextField; 321 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 322 | PROVISIONING_PROFILE_SPECIFIER = ""; 323 | SKIP_INSTALL = YES; 324 | SWIFT_VERSION = 4.2; 325 | TARGETED_DEVICE_FAMILY = "1,2"; 326 | }; 327 | name = Release; 328 | }; 329 | /* End XCBuildConfiguration section */ 330 | 331 | /* Begin XCConfigurationList section */ 332 | B0E6DEF921C8E4400016F4F5 /* Build configuration list for PBXProject "SGCodeTextField" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | B0E6DF0521C8E4400016F4F5 /* Debug */, 336 | B0E6DF0621C8E4400016F4F5 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | B0E6DF0721C8E4400016F4F5 /* Build configuration list for PBXNativeTarget "SGCodeTextField" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | B0E6DF0821C8E4400016F4F5 /* Debug */, 345 | B0E6DF0921C8E4400016F4F5 /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | /* End XCConfigurationList section */ 351 | }; 352 | rootObject = B0E6DEF621C8E4400016F4F5 /* Project object */; 353 | } 354 | -------------------------------------------------------------------------------- /SGCodeTextField.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SGCodeTextField.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/SGCodeTextField/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 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sources/SGCodeTextField/SGCodeTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // SGCodeTextField.h 3 | // SGCodeTextField 4 | // 5 | // Copyright (c) 2019 Sergiu Grigoriev 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #import 27 | 28 | //! Project version number for SGCodeTextField. 29 | FOUNDATION_EXPORT double SGCodeTextFieldVersionNumber; 30 | 31 | //! Project version string for SGCodeTextField. 32 | FOUNDATION_EXPORT const unsigned char SGCodeTextFieldVersionString[]; 33 | 34 | // In this header, you should import all the public headers of your framework using statements like #import 35 | 36 | 37 | -------------------------------------------------------------------------------- /Sources/SGCodeTextField/SGCodeTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SGCodeTextField.swift 3 | // SGCodeTextField 4 | // 5 | // Copyright (c) 2019 Sergiu Grigoriev 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | import UIKit 28 | 29 | @IBDesignable 30 | public class SGCodeTextField: UIControl { 31 | 32 | @IBInspectable public var count: Int = 6 { 33 | didSet { 34 | if count < 1 { count = 1} 35 | refreshUI() 36 | } 37 | } 38 | @IBInspectable public var placeholder: String = "-" { 39 | didSet { 40 | refreshUI() 41 | } 42 | } 43 | @IBInspectable public var autocapitalization: Bool = true { 44 | didSet { 45 | refreshUI() 46 | } 47 | } 48 | @IBInspectable public var font: UIFont = UIFont.systemFont(ofSize: 18.0) { 49 | didSet { 50 | refreshUI() 51 | } 52 | } 53 | @IBInspectable public var digitCornerRadius: CGFloat = 5.0 { 54 | didSet { 55 | refreshUI() 56 | } 57 | } 58 | @IBInspectable public var digitBackgroundColor: UIColor = UIColor.white { 59 | didSet { 60 | refreshUI() 61 | } 62 | } 63 | @IBInspectable public var digitBackgroundColorFocused: UIColor = UIColor.lightGray { 64 | didSet { 65 | refreshUI() 66 | } 67 | } 68 | @IBInspectable public var digitBackgroundColorEmpty: UIColor = UIColor.white { 69 | didSet { 70 | refreshUI() 71 | } 72 | } 73 | @IBInspectable public var digitBorderColor: UIColor = UIColor.gray { 74 | didSet { 75 | refreshUI() 76 | } 77 | } 78 | @IBInspectable public var digitBorderColorFocused: UIColor = UIColor.black { 79 | didSet { 80 | refreshUI() 81 | } 82 | } 83 | @IBInspectable public var digitBorderColorEmpty: UIColor = UIColor.lightGray { 84 | didSet { 85 | refreshUI() 86 | } 87 | } 88 | @IBInspectable public var digitBorderWidth: CGFloat = 1.0 { 89 | didSet { 90 | refreshUI() 91 | } 92 | } 93 | @IBInspectable public var digitSpacing: CGFloat = 5.0 { 94 | didSet { 95 | refreshUI() 96 | } 97 | } 98 | @IBInspectable public var textColor: UIColor = UIColor.black { 99 | didSet { 100 | refreshUI() 101 | } 102 | } 103 | @IBInspectable public var textColorFocused: UIColor = UIColor.black { 104 | didSet { 105 | refreshUI() 106 | } 107 | } 108 | @IBInspectable public var placeholderColor: UIColor = UIColor.lightGray { 109 | didSet { 110 | refreshUI() 111 | } 112 | } 113 | @IBInspectable public var isSecureTextEntry: Bool = false { 114 | didSet { 115 | refreshUI() 116 | } 117 | } 118 | @IBInspectable public var secureTextEntryMaskCharacter: String = "*" { 119 | didSet { 120 | refreshUI() 121 | } 122 | } 123 | 124 | public var textChangeHandler: ((_ text: String?, _ completed: Bool) -> Void)? 125 | public var keyboardType: UIKeyboardType { 126 | set { 127 | self.textField.keyboardType = newValue 128 | } 129 | get { 130 | return self.textField.keyboardType 131 | } 132 | } 133 | 134 | public var text: String? { 135 | set { 136 | if (newValue?.count ?? 0) <= self.count { 137 | self.textField.text = newValue 138 | self.updateLabels() 139 | } 140 | } 141 | get { 142 | return self.textField.text 143 | } 144 | } 145 | 146 | override public var isFirstResponder: Bool { 147 | return self.textField.isFirstResponder 148 | } 149 | 150 | // Private 151 | private var stackView = UIStackView() 152 | private var textField = UITextField() 153 | 154 | init() { 155 | super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 40)) 156 | 157 | self.setup() 158 | } 159 | 160 | override init(frame: CGRect) { 161 | super.init(frame: frame) 162 | 163 | self.setup() 164 | } 165 | 166 | required init?(coder aDecoder: NSCoder) { 167 | super.init(coder: aDecoder) 168 | 169 | self.setup() 170 | } 171 | 172 | private func setup() { 173 | self.customize() 174 | self.addGestures() 175 | 176 | self.textField.addTarget(self, action: #selector(textDidChange(sender:)), for: .editingChanged) 177 | self.textField.addTarget(self, action: #selector(editingDidBegin(sender:)), for: .editingDidBegin) 178 | self.textField.addTarget(self, action: #selector(editingDidEnd(sender:)), for: .editingDidEnd) 179 | } 180 | 181 | private func customize() { 182 | self.isUserInteractionEnabled = true 183 | 184 | self.stackView.axis = .horizontal 185 | self.stackView.distribution = .fillEqually 186 | self.stackView.spacing = self.digitSpacing 187 | 188 | self.addSubview(self.stackView) 189 | self.stackView.translatesAutoresizingMaskIntoConstraints = false 190 | 191 | let viewsDict = ["subView" : self.stackView] 192 | 193 | let constraint_POS_V = NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subView]-0-|", options: [], metrics: nil, views: viewsDict) 194 | let constraint_POS_H = NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subView]-0-|", options: [], metrics: nil, views: viewsDict) 195 | 196 | self.addConstraints(constraint_POS_V) 197 | self.addConstraints(constraint_POS_H) 198 | 199 | self.addLabels() 200 | self.updateLabels() 201 | 202 | self.insertSubview(self.textField, at: 0) 203 | self.textField.keyboardType = .numberPad 204 | self.textField.isHidden = true 205 | self.textField.delegate = self 206 | 207 | /// Note: Received message must containt one of the following words for the auto-complete to work: passcode, code 208 | if #available(iOS 12, tvOS 12, *) { 209 | self.textField.textContentType = .oneTimeCode 210 | } 211 | } 212 | 213 | private func addGestures() { 214 | let tap = UITapGestureRecognizer(target: self, action: #selector(onTap)) 215 | self.addGestureRecognizer(tap) 216 | } 217 | 218 | private func addLabels() { 219 | self.stackView.subviews.forEach({ $0.removeFromSuperview() }) 220 | 221 | for i in 0.. Bool { 238 | return self.textField.becomeFirstResponder() 239 | } 240 | 241 | @discardableResult 242 | override public func resignFirstResponder() -> Bool { 243 | return self.textField.resignFirstResponder() 244 | } 245 | 246 | // call it after all properties were set for better performance 247 | public func refreshUI() { 248 | self.addLabels() 249 | self.updateLabels() 250 | 251 | self.stackView.spacing = self.digitSpacing 252 | } 253 | 254 | // MARK: - Actions 255 | 256 | @objc private func textDidChange(sender: UITextField) { 257 | self.updateLabels() 258 | self.textChangeHandler?(self.text, self.text?.count == self.count) 259 | sendActions(for: .editingChanged) 260 | } 261 | 262 | @objc private func editingDidBegin(sender: UITextField) { 263 | self.updateLabels() 264 | sendActions(for: .editingDidBegin) 265 | } 266 | 267 | @objc private func editingDidEnd(sender: UITextField) { 268 | self.updateLabels() 269 | sendActions(for: .editingDidEnd) 270 | } 271 | 272 | // MARK: - UI 273 | 274 | private func updateLabels() { 275 | if (self.text?.count ?? 0) <= self.stackView.subviews.count { 276 | 277 | var lastIndex = -1 278 | 279 | for i in 0..<(self.text?.count ?? 0) { 280 | let str = self.text ?? "" 281 | let index = str.index(str.startIndex, offsetBy: i) 282 | let lbl = self.stackView.subviews[i] as! UILabel 283 | let character = self.isSecureTextEntry ? self.secureTextEntryMaskCharacter : String(str[index]) 284 | 285 | lbl.backgroundColor = self.digitBackgroundColor 286 | lbl.textColor = self.textColor 287 | lbl.text = self.autocapitalization ? character.uppercased() : character 288 | lbl.layer.borderColor = self.digitBorderColor.cgColor 289 | 290 | lastIndex = i 291 | } 292 | 293 | // empty labels 294 | if lastIndex < self.count { 295 | for i in (lastIndex+1).. Bool { 322 | let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string) 323 | 324 | return newString.count <= self.count 325 | } 326 | } 327 | 328 | -------------------------------------------------------------------------------- /Sources/SGCodeTextField/SGCodeTextFieldConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SGCodeTextFieldConfiguration.swift 3 | // SGCodeTextField 4 | // 5 | // Copyright (c) 2019 Sergiu Grigoriev 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | import UIKit 28 | 29 | struct SGCodeTextFieldConfiguration { 30 | var keyboardType: UIKeyboardType = .numberPad 31 | var font: UIFont = UIFont.systemFont(ofSize: 18.0) 32 | var digitCornerRadius: CGFloat = 5.0 33 | var digitBackgroundColor: UIColor = UIColor.white 34 | var digitBackgroundColorFocused: UIColor = UIColor.lightGray 35 | var digitBorderColor: UIColor = UIColor.gray 36 | var digitBorderColorFocused: UIColor = UIColor.green 37 | var digitBorderWidth: CGFloat = 1.0 38 | } 39 | --------------------------------------------------------------------------------