├── .gitignore ├── FTCTextEntryFormatting-prefix.pch ├── FTCTextEntryFormatting.podspec ├── FTCTextEntryFormatting.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── FTCTextEntryFormatting.xcscheme ├── FTCTextEntryFormattingDemo ├── FTCTextEntryFormattingDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── FTCTextEntryFormattingDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Bridging-Header.h │ ├── Data │ └── FormattingExampleItem.swift │ ├── Info.plist │ ├── PrefixHeader.pch │ └── Screens │ ├── EntryViewController.swift │ └── ViewController.swift ├── Info.plist ├── LICENSE ├── README.md ├── Src ├── Core │ ├── FTCTextEntry.h │ ├── FTCTextEntryFormatCoordinator.h │ ├── FTCTextEntryFormatCoordinator.m │ ├── FTCTextEntryFormatCoordinatorHelper.h │ ├── FTCTextEntryFormatCoordinatorHelper.m │ ├── FTCTextEntryFormattingConfig.h │ ├── FTCTextEntryFormattingConfig.m │ ├── Formatting │ │ ├── FTCNoFormattingFormatter.h │ │ ├── FTCNoFormattingFormatter.m │ │ ├── FTCTextEntryFormatter.h │ │ ├── MaskFormatter │ │ │ ├── FTCMaskFormatter.h │ │ │ ├── FTCMaskFormatter.m │ │ │ ├── FTCMaskFormatterConfig.h │ │ │ ├── FTCMaskFormatterGenericConfig.h │ │ │ └── FTCMaskFormatterGenericConfig.m │ │ └── PostfixFormatter │ │ │ ├── FTCPostfixFormatter.h │ │ │ └── FTCPostfixFormatter.m │ └── InputFiltering │ │ ├── DigitsValueFilter │ │ ├── FTCDigitsValueFilter.h │ │ └── FTCDigitsValueFilter.m │ │ ├── FTCFilteredString.h │ │ ├── FTCFilteredString.m │ │ ├── FTCNoFilteringFilter.h │ │ ├── FTCNoFilteringFilter.m │ │ ├── FTCTextEntryEditingInputFilter.h │ │ ├── FTCTextEntryNotEditingInputFilter.h │ │ ├── LimitedLengthInputFilter │ │ ├── FTCLimitedLengthInputFilter.h │ │ └── FTCLimitedLengthInputFilter.m │ │ └── ToUpperCaseInputFilter │ │ ├── FTCToUpperCaseInputFilter.h │ │ └── FTCToUpperCaseInputFilter.m ├── FTCTextEntryFormatting.h ├── FTCTextEntryFormattingConfigFactory.h ├── FTCTextEntryFormattingConfigFactory.m ├── FTCTextFieldFormatCoordinator.h ├── FTCTextFieldFormatCoordinator.m ├── FTCTextInputUtils.h ├── FTCTextInputUtils.m ├── Money │ ├── FTCIntegralMoneyAmountEntryEditingInputFilter.h │ ├── FTCIntegralMoneyAmountEntryEditingInputFilter.m │ ├── FTCIntegralMoneyAmountEntryNotEditingInputFilter.h │ ├── FTCIntegralMoneyAmountEntryNotEditingInputFilter.m │ ├── FTCMoneyEntryEditingFormatter.h │ ├── FTCMoneyEntryEditingFormatter.m │ ├── FTCMoneyEntryEditingInputFilter.h │ ├── FTCMoneyEntryEditingInputFilter.m │ ├── FTCMoneyEntryFormatUtils.h │ ├── FTCMoneyEntryFormatUtils.m │ ├── FTCMoneyEntryNotEditingFormatter.h │ ├── FTCMoneyEntryNotEditingFormatter.m │ ├── FTCMoneyEntryNotEditingInputFilter.h │ └── FTCMoneyEntryNotEditingInputFilter.m └── Tools │ ├── FTCTextEntryFormattingStringUtils.h │ ├── FTCTextEntryFormattingStringUtils.m │ ├── NSString+FTCTextEntryFormatting.h │ └── NSString+FTCTextEntryFormatting.m └── UnitTests ├── FormatCoordinator └── FormatCoordinatorTestCase.m ├── Formatters ├── FTCMaskFormatter │ └── FTCMaskFormatterTestCase.m └── FTCPostfixFormatter │ ├── FTCPostfixFormatterTestCaseWithEmptyPostfix.m │ ├── FTCPostfixFormatterTestCaseWithNilPostfix.m │ └── FTCPostfixFormatterTestCaseWithNotEmptyPostfix.m ├── Info.plist └── InputFilter ├── FTCDigitsInputFilterTestCase.m └── FTCDigitsInputFilterTestCaseWithZeroMaxLength.m /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # AppCode 3 | .idea 4 | 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData/ 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata/ 23 | FTCTextEntryFormatting.xcscmblueprint 24 | 25 | ## Other 26 | *.moved-aside 27 | *.xcuserstate 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | ## Playgrounds 36 | timeline.xctimeline 37 | playground.xcworkspace 38 | 39 | # Swift Package Manager 40 | # 41 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 42 | # Packages/ 43 | .build/ 44 | 45 | # CocoaPods 46 | # 47 | # We recommend against adding the Pods directory to your .gitignore. However 48 | # you should judge for yourself, the pros and cons are mentioned at: 49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 50 | # 51 | # Pods/ 52 | 53 | # Carthage 54 | # 55 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 56 | # Carthage/Checkouts 57 | 58 | Carthage/Build 59 | 60 | # fastlane 61 | # 62 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 63 | # screenshots whenever they are needed. 64 | # For more information about the recommended setup visit: 65 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 66 | 67 | fastlane/report.xml 68 | fastlane/Preview.html 69 | fastlane/screenshots 70 | fastlane/test_output 71 | -------------------------------------------------------------------------------- /FTCTextEntryFormatting-prefix.pch: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | -------------------------------------------------------------------------------- /FTCTextEntryFormatting.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | 3 | spec.name = 'FTCTextEntryFormatting' 4 | spec.version = '1.0.5' 5 | spec.license = { :type => 'FTC' } 6 | spec.homepage = 'https://github.com/cft-dev/FTCTextEntryFormatting' 7 | spec.authors = { 'Denis Morozov' => 'd.morozov@ftc.ru' } 8 | spec.summary = 'Text Entry Formatting.' 9 | spec.source = { :git => 'https://github.com/cft-dev/FTCTextEntryFormatting.git', :tag => '1.0.5' } 10 | 11 | spec.prefix_header_contents = '@import Foundation;' 12 | spec.source_files = 'Src/**/*.{h,m}' 13 | spec.private_header_files = 'Src/Categories/*.h', 'Src/Tools/*.h' 14 | 15 | spec.requires_arc = true 16 | 17 | spec.ios.deployment_target = '8.0' 18 | 19 | spec.test_spec 'UnitTests' do |test_spec| 20 | test_spec.source_files = 'UnitTests/**/*.{h,m}' 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /FTCTextEntryFormatting.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FTCTextEntryFormatting.xcodeproj/xcshareddata/xcschemes/FTCTextEntryFormatting.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 69 | 70 | 76 | 77 | 78 | 79 | 80 | 81 | 87 | 88 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import UIKit 22 | 23 | @UIApplicationMain 24 | class AppDelegate: UIResponder, UIApplicationDelegate { 25 | 26 | var window: UIWindow? 27 | 28 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool 29 | { 30 | return true 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/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 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/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 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/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 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #ifndef Bridging_Header_h 22 | #define Bridging_Header_h 23 | 24 | #import "FTCTextEntryFormatting.h" 25 | 26 | #endif /* Bridging_Header_h */ 27 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/Data/FormattingExampleItem.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | struct FormattingExampleItem 22 | { 23 | let text: String 24 | let detailText: String? 25 | let config: FTCTextEntryFormattingConfig 26 | let keyboard: UIKeyboardType 27 | } 28 | 29 | extension FormattingExampleItem 30 | { 31 | static func makeItems() -> [FormattingExampleItem] 32 | { 33 | 34 | return [ 35 | self.makeCellPhoneItem(), 36 | self.makeMoneyItem(), 37 | self.makeCreditCardPANItem(), 38 | self.makeDriverLicenseModernItem(), 39 | self.makeDriverLicenseOutdatedItem(), 40 | ] 41 | } 42 | 43 | static func makeCellPhoneItem() -> FormattingExampleItem 44 | { 45 | let config = FTCTextEntryFormattingConfigFactory.mobilePhoneConfig(with: "+7 (___) ___ __ __", maskChar: "_") 46 | 47 | return FormattingExampleItem(text: "Cell phone", 48 | detailText: nil, 49 | config: config, 50 | keyboard: .phonePad) 51 | } 52 | 53 | static func makeMoneyItem() -> FormattingExampleItem 54 | { 55 | let config = FTCTextEntryFormattingConfigFactory.moneyConfig(with: nil, onlyIntegral: false) 56 | 57 | return FormattingExampleItem(text: "Money", 58 | detailText: nil, 59 | config: config, 60 | keyboard: .decimalPad) 61 | } 62 | 63 | static func makeCreditCardPANItem() -> FormattingExampleItem 64 | { 65 | let maskConfig = FTCMaskFormatterGenericConfig(mask: "XXXX XXXX XXXX XXXX XXX", maskCharacter: "X") 66 | maskConfig.tailMode = .cut 67 | 68 | let maskFormatter = FTCMaskFormatter(config: maskConfig) 69 | let inputFilter = FTCDigitsValueFilter(maxLength: maskConfig.countMaskCharacters) 70 | 71 | let config = FTCTextEntryFormattingConfig(formatter: maskFormatter, inputFilter: inputFilter) 72 | 73 | return FormattingExampleItem(text: "Credit Card PAN", 74 | detailText: nil, 75 | config: config, 76 | keyboard: .numberPad) 77 | } 78 | 79 | static func makeDriverLicenseModernItem() -> FormattingExampleItem 80 | { 81 | let maskConfig = FTCMaskFormatterGenericConfig(mask: "__ __ ______", maskCharacter: "_") 82 | maskConfig.tailMode = .none 83 | 84 | let maskFormatter = FTCMaskFormatter(config: maskConfig) 85 | let inputFilter = FTCDigitsValueFilter(maxLength: maskConfig.countMaskCharacters) 86 | 87 | let config = FTCTextEntryFormattingConfig(formatter: maskFormatter, inputFilter: inputFilter) 88 | 89 | return FormattingExampleItem(text: "Driver's license", 90 | detailText: "modern", 91 | config: config, 92 | keyboard: .numberPad) 93 | } 94 | 95 | static func makeDriverLicenseOutdatedItem() -> FormattingExampleItem 96 | { 97 | let maskConfig = FTCMaskFormatterGenericConfig(mask: "__ __ № ______", maskCharacter: "_") 98 | maskConfig.tailMode = .none 99 | 100 | let maskFormatter = FTCMaskFormatter(config: maskConfig) 101 | let inputFilter = FTCToUpperCaseInputFilter() 102 | 103 | let config = FTCTextEntryFormattingConfig(formatter: maskFormatter, inputFilter: inputFilter) 104 | 105 | return FormattingExampleItem(text: "Driver's license", 106 | detailText: "outdated", 107 | config: config, 108 | keyboard: .default) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/PrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #ifndef PrefixHeader_pch 22 | #define PrefixHeader_pch 23 | 24 | @import Foundation; 25 | 26 | #endif /* PrefixHeader_pch */ 27 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/Screens/EntryViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import UIKit 22 | 23 | class EntryViewController: UIViewController 24 | { 25 | @IBOutlet var textField: UITextField! 26 | 27 | var textFieldFormatCoordinator: FTCTextFieldFormatCoordinator! 28 | 29 | var formattingConfig: FTCTextEntryFormattingConfig! 30 | var keyboard: UIKeyboardType = .default 31 | 32 | override func viewDidLoad() 33 | { 34 | super.viewDidLoad() 35 | 36 | self.textField.keyboardType = self.keyboard 37 | 38 | self.textFieldFormatCoordinator = FTCTextFieldFormatCoordinator(textField: self.textField) 39 | 40 | self.textFieldFormatCoordinator.apply(formattingConfig: self.formattingConfig) 41 | 42 | self.textFieldFormatCoordinator.didChangeValueHandler = { [unowned self] in 43 | print("rawValue: \(self.textFieldFormatCoordinator.rawValue ?? "nil")") 44 | print("formattedValue: \(self.textFieldFormatCoordinator.formattedValue)") 45 | } 46 | } 47 | 48 | @IBAction func close(_ sender: Any) 49 | { 50 | self.dismiss(animated: true, completion: nil) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /FTCTextEntryFormattingDemo/FTCTextEntryFormattingDemo/Screens/ViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import UIKit 22 | 23 | class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource 24 | { 25 | @IBOutlet var tableView: UITableView! 26 | 27 | let formattingExampleItems: [FormattingExampleItem] = FormattingExampleItem.makeItems() 28 | 29 | override func viewDidLoad() 30 | { 31 | super.viewDidLoad() 32 | } 33 | 34 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 35 | { 36 | return self.formattingExampleItems.count 37 | } 38 | 39 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 40 | { 41 | let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil) 42 | 43 | cell.textLabel?.text = formattingExampleItems[indexPath.row].text 44 | cell.detailTextLabel?.text = formattingExampleItems[indexPath.row].detailText 45 | 46 | return cell 47 | } 48 | 49 | public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 50 | { 51 | tableView.deselectRow(at: indexPath, animated: true) 52 | 53 | if let vc = self.storyboard?.instantiateViewController(withIdentifier: "EntryViewController") as? EntryViewController 54 | { 55 | vc.formattingConfig = formattingExampleItems[indexPath.row].config 56 | vc.keyboard = formattingExampleItems[indexPath.row].keyboard 57 | 58 | self.present(vc, animated: true, completion: nil) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 CFT 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Overview 2 | 3 | **FTCTextEntryFormatting** is a component for formatting input data in realtime. 4 | 5 | ### Installation 6 | 7 | ```ruby 8 | pod 'FTCTextEntryFormatting', :git => 'https://github.com/cft-dev/FTCTextEntryFormatting.git', :tag => '1.0.5' 9 | ``` 10 | 11 | ### Standalone usage 12 | 13 | Example for cell phone. See Demo project for more details. 14 | 15 | ```swift 16 | import FTCTextEntryFormatting 17 | 18 | class EntryViewController: UIViewController 19 | { 20 | @IBOutlet var textField: UITextField! 21 | 22 | // required strong stored 23 | var textFieldFormatCoordinator: FTCTextFieldFormatCoordinator! 24 | 25 | override func viewDidLoad() 26 | { 27 | super.viewDidLoad() 28 | 29 | self.textFieldFormatCoordinator = FTCTextFieldFormatCoordinator(textField: self.textField) 30 | 31 | let formattingConfig = FTCTextEntryFormattingConfigFactory.mobilePhoneConfig(with: "+7 (___) ___-__-__", 32 | maskChar: "_") 33 | self.textFieldFormatCoordinator.apply(formattingConfig: formattingConfig) 34 | } 35 | } 36 | ``` 37 | 38 | ### Custom usage 39 | 40 | 1. Create adopter of FTCTextEntryEditingInputFilter 41 | 42 | ```swift 43 | import FTCTextEntryFormatting 44 | 45 | class CustomEditingInputFilter : NSObject, FTCTextEntryEditingInputFilter 46 | { 47 | func replaceSubstring(in originalString: String, at range: NSRange, with replacement: String) -> FTCFilteredString 48 | { 49 | ... 50 | } 51 | } 52 | ``` 53 | 54 | 2. Create adopter of FTCTextEntryFormatter 55 | 56 | ```swift 57 | class CustomFormatter : NSObject, FTCTextEntryFormatter 58 | { 59 | func raw(fromFormatted formattedValue: String?) -> String 60 | { 61 | ... 62 | } 63 | 64 | func formatted(fromRaw rawValue: String?) -> String 65 | { 66 | ... 67 | } 68 | 69 | func rangeInFormattedValue(for rangeInRawValue: NSRange, inRawValue rawValue: String?) -> NSRange 70 | { 71 | ... 72 | } 73 | 74 | func rangeInRawValue(for rangeInFormattedValue: NSRange, inFormattedValue formattedValue: String?) -> NSRange 75 | { 76 | ... 77 | } 78 | } 79 | ``` 80 | 81 | 3. Create adopter of FTCTextEntryNotEditingInputFilter 82 | 83 | ```swift 84 | class CustomNotEditingInputFilter : NSObject, FTCTextEntryNotEditingInputFilter 85 | { 86 | func filterString(_ string: String) -> String 87 | { 88 | ... 89 | } 90 | } 91 | ``` 92 | 93 | 4. Use it 94 | 95 | ```swift 96 | class EntryViewController: UIViewController 97 | { 98 | @IBOutlet var textField: UITextField! 99 | 100 | // required strong stored 101 | var textFieldFormatCoordinator: FTCTextFieldFormatCoordinator! 102 | 103 | override func viewDidLoad() 104 | { 105 | super.viewDidLoad() 106 | 107 | self.textFieldFormatCoordinator = FTCTextFieldFormatCoordinator(textField: self.textField) 108 | 109 | let formattingConfig = FTCTextEntryFormattingConfig() 110 | formattingConfig.editingInputFilter = CustomEditingInputFilter() 111 | formattingConfig.editingFormatter = CustomFormatter() 112 | formattingConfig.notEditingInputFilter = CustomNotEditingInputFilter() 113 | formattingConfig.notEditingFormatter = CustomFormatter() 114 | 115 | self.textFieldFormatCoordinator.apply(formattingConfig: formattingConfig) 116 | } 117 | } 118 | ``` 119 | 120 | ### Generic input filters 121 | 122 | FTCDigitsValueFilter – Allows only digits symbols. 123 | 124 | FTCNoFilteringFilter – Allows any symbol. 125 | 126 | FTCLimitedLengthInputFilter – Allows any symbol. Constraining input by length. 127 | 128 | FTCToUpperCaseInputFilter – Allows any symbol. Turns case to upper. 129 | 130 | ### License 131 | MIT 132 | -------------------------------------------------------------------------------- /Src/Core/FTCTextEntry.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @protocol FTCTextEntry; 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @protocol FTCTextEntry 26 | 27 | @property (nonatomic, strong, nullable) NSString *text; 28 | 29 | @property (nonatomic, assign) NSRange selectedTextRange; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /Src/Core/FTCTextEntryFormatCoordinator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @class FTCTextEntryFormattingConfig; 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCTextEntryFormatCoordinator : NSObject 26 | 27 | @property (nonatomic, strong, nullable) NSString *rawValue; 28 | @property (nonatomic, readonly) NSString *formattedValue; 29 | 30 | @property (nonatomic, assign) NSRange currentSelectionRangeInFormattedValue; 31 | 32 | - (instancetype)init; 33 | 34 | - (void)userReplacedInFormattedValueSubstringAtRange:(NSRange)range withString:(NSString *)replacement; 35 | 36 | - (void)beginEditing; 37 | - (void)endEditing; 38 | 39 | - (BOOL)isEditing; 40 | 41 | - (void)applyConfig:(FTCTextEntryFormattingConfig *)config; 42 | 43 | @end 44 | 45 | NS_ASSUME_NONNULL_END 46 | -------------------------------------------------------------------------------- /Src/Core/FTCTextEntryFormatCoordinator.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatCoordinator.h" 22 | #import "FTCTextEntryFormatter.h" 23 | #import "FTCFilteredString.h" 24 | #import "FTCTextEntryEditingInputFilter.h" 25 | #import "FTCTextEntryNotEditingInputFilter.h" 26 | #import "FTCTextEntryFormattingConfig.h" 27 | 28 | @implementation FTCTextEntryFormatCoordinator 29 | { 30 | BOOL isEditing; 31 | 32 | NSRange currentSelectionRangeInRawValue; 33 | 34 | id editingFormatter; 35 | id editingInputFilter; 36 | 37 | id notEditingFormatter; 38 | id notEditingInputFilter; 39 | } 40 | 41 | - (instancetype)init 42 | { 43 | self = [super init]; 44 | 45 | [self applyDefaultConfig]; 46 | 47 | self.rawValue = @""; 48 | 49 | return self; 50 | } 51 | 52 | - (void)applyDefaultConfig 53 | { 54 | FTCTextEntryFormattingConfig *defaultConfig = [[FTCTextEntryFormattingConfig alloc] init]; 55 | [self applyConfig:defaultConfig]; 56 | } 57 | 58 | - (void)userReplacedInFormattedValueSubstringAtRange:(NSRange)range withString:(NSString *)replacement 59 | { 60 | assert( nil != replacement ); 61 | assert( isEditing ); 62 | assert( (nil != editingFormatter) && "'editingFormatter' must not be nil here." ); 63 | 64 | NSRange replacementRangeInRawValue = [editingFormatter rangeInRawValueForRange:range inFormattedValue:_formattedValue]; 65 | 66 | assert( (nil != _rawValue) || (nil == _rawValue && (replacementRangeInRawValue.location + replacementRangeInRawValue.length == 0)) ); 67 | 68 | //фикс так как voice input добавляет пробел и не вызывает replacement 69 | replacementRangeInRawValue.location = MIN(_rawValue.length, replacementRangeInRawValue.location); 70 | 71 | FTCFilteredString *filteredRawString = [editingInputFilter replaceSubstringInString:(nil != _rawValue ? _rawValue : @"") 72 | atRange:replacementRangeInRawValue 73 | withString:replacement]; 74 | 75 | assert( nil != filteredRawString.string ); 76 | 77 | _rawValue = filteredRawString.string; 78 | [self doFormatValue]; 79 | 80 | const NSRange filteredRange = filteredRawString.range; 81 | const NSUInteger lastInsertedCharacterLocation = filteredRange.location + filteredRange.length; 82 | currentSelectionRangeInRawValue = NSMakeRange(lastInsertedCharacterLocation, 0); 83 | _currentSelectionRangeInFormattedValue = [editingFormatter rangeInFormattedValueForRange:currentSelectionRangeInRawValue inRawValue:_rawValue]; 84 | } 85 | 86 | - (void)beginEditing 87 | { 88 | assert( NO == isEditing ); 89 | assert( (nil != editingFormatter) && "'editingFormatter' must not be nil here." ); 90 | 91 | isEditing = YES; 92 | 93 | [self doFormatValue]; 94 | [self moveCaretToTheEndOfRawValue]; 95 | } 96 | 97 | - (void)moveCaretToTheEndOfRawValue 98 | { 99 | currentSelectionRangeInRawValue = NSMakeRange(_rawValue.length, 0); 100 | 101 | id currentFormatter = isEditing ? editingFormatter : notEditingFormatter; 102 | assert( (nil != currentFormatter) && "'currentFormatter' must not be nil here." ); 103 | 104 | _currentSelectionRangeInFormattedValue = [currentFormatter rangeInFormattedValueForRange:currentSelectionRangeInRawValue inRawValue:_rawValue]; 105 | } 106 | 107 | - (void)endEditing 108 | { 109 | assert( isEditing ); 110 | 111 | isEditing = NO; 112 | 113 | _rawValue = [self filterValue:_rawValue]; 114 | 115 | [self doFormatValue]; 116 | } 117 | 118 | - (void)doFormatValue 119 | { 120 | id currentFormatter = isEditing ? editingFormatter : notEditingFormatter; 121 | assert( (nil != currentFormatter) && "'currentFormatter' must not be nil here." ); 122 | 123 | _formattedValue = [currentFormatter formattedFromRaw:(nil != _rawValue ? _rawValue : @"")]; 124 | } 125 | 126 | - (BOOL)isEditing 127 | { 128 | return isEditing; 129 | } 130 | 131 | - (void)setRawValue:(NSString *)rawValue 132 | { 133 | assert( NO == isEditing ); 134 | 135 | [self doSetRawValue:rawValue]; 136 | } 137 | 138 | - (void)applyConfig:(FTCTextEntryFormattingConfig *)config 139 | { 140 | assert( (nil != config) && "Argument 'config' must not be nil." ); 141 | assert( (nil != config.editingFormatter) && "'config.editingFormatter' must not be nil here." ); 142 | assert( (nil != config.editingInputFilter) && "'config.editingInputFilter' must not be nil here." ); 143 | assert( (nil != config.notEditingFormatter) && "'config.notEditingFormatter' must not be nil here." ); 144 | assert( (nil != config.notEditingInputFilter) && "'config.notEditingInputFilter' must not be nil here." ); 145 | 146 | editingFormatter = config.editingFormatter; 147 | editingInputFilter = config.editingInputFilter; 148 | 149 | notEditingFormatter = config.notEditingFormatter; 150 | notEditingInputFilter = config.notEditingInputFilter; 151 | 152 | [self doSetRawValue:_rawValue]; 153 | } 154 | 155 | - (void)doSetRawValue:(nullable NSString *)rawValue 156 | { 157 | _rawValue = [self filterValue:rawValue]; 158 | 159 | [self doFormatValue]; 160 | [self moveCaretToTheEndOfRawValue]; 161 | } 162 | 163 | - (nullable NSString *)filterValue:(nullable NSString *)value 164 | { 165 | assert( (nil != notEditingInputFilter) && "'notEditingInputFilter' must not be nil here." ); 166 | 167 | if( nil == value ) 168 | { 169 | return value; 170 | } 171 | 172 | return [notEditingInputFilter filterString:value]; 173 | } 174 | 175 | @end 176 | -------------------------------------------------------------------------------- /Src/Core/FTCTextEntryFormatCoordinatorHelper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @class FTCTextEntryFormattingConfig; 22 | @protocol FTCTextEntry; 23 | 24 | NS_ASSUME_NONNULL_BEGIN 25 | 26 | @interface FTCTextEntryFormatCoordinatorHelper : NSObject 27 | 28 | @property (nonatomic, nullable, copy) void (^didChangeValueHandler)(void); 29 | 30 | @property (nonatomic, nullable, copy) NSString *rawValue; 31 | 32 | @property (nonatomic, readonly) NSString *formattedValue; 33 | 34 | - (instancetype)initWithUI:(id)textEntryUI; 35 | 36 | - (void)beginEditing; 37 | - (void)changeCharactersInRange:(NSRange)range replacement:(NSString *)replacement; 38 | - (void)endEditing; 39 | 40 | - (void)applyFormattingConfig:(FTCTextEntryFormattingConfig *)config NS_SWIFT_NAME( apply(formattingConfig:) ); 41 | 42 | @end 43 | 44 | NS_ASSUME_NONNULL_END 45 | -------------------------------------------------------------------------------- /Src/Core/FTCTextEntryFormatCoordinatorHelper.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatCoordinatorHelper.h" 22 | #import "FTCTextEntryFormatCoordinator.h" 23 | #import "FTCTextEntryFormattingConfig.h" 24 | #import "FTCTextEntry.h" 25 | 26 | @interface FTCTextEntryFormatCoordinatorHelper () 27 | 28 | @end 29 | 30 | @implementation FTCTextEntryFormatCoordinatorHelper 31 | { 32 | id __weak entryUI; 33 | 34 | FTCTextEntryFormatCoordinator *formatCoordinator; 35 | 36 | FTCTextEntryFormattingConfig *currentConfig; 37 | } 38 | 39 | - (instancetype)initWithUI:(id)anEntryUI 40 | { 41 | assert( nil != anEntryUI ); 42 | 43 | self = [super init]; 44 | 45 | entryUI = anEntryUI; 46 | formatCoordinator = [[FTCTextEntryFormatCoordinator alloc] init]; 47 | 48 | return self; 49 | } 50 | 51 | - (NSString *)rawValue 52 | { 53 | return formatCoordinator.rawValue; 54 | } 55 | 56 | - (void)setRawValue:(NSString *)rawValue 57 | { 58 | BOOL shouldBecomeFirstResponder = NO; 59 | if( [formatCoordinator isEditing] ) 60 | { 61 | shouldBecomeFirstResponder = YES; 62 | [formatCoordinator endEditing]; 63 | } 64 | assert( NO == [formatCoordinator isEditing] ); 65 | 66 | formatCoordinator.rawValue = [rawValue copy]; 67 | entryUI.text = formatCoordinator.formattedValue; 68 | 69 | if( shouldBecomeFirstResponder ) 70 | { 71 | [formatCoordinator beginEditing]; 72 | } 73 | } 74 | 75 | - (NSString *)formattedValue 76 | { 77 | return formatCoordinator.formattedValue; 78 | } 79 | 80 | - (void)beginEditing 81 | { 82 | [self handleValueChange:^(FTCTextEntryFormatCoordinator *coordinator, id entry) 83 | { 84 | [coordinator beginEditing]; 85 | entry.text = coordinator.formattedValue; 86 | entry.selectedTextRange = coordinator.currentSelectionRangeInFormattedValue; 87 | }]; 88 | } 89 | 90 | - (void)changeCharactersInRange:(NSRange)range replacement:(NSString *)replacement 91 | { 92 | [self handleValueChange:^(FTCTextEntryFormatCoordinator *coordinator, id entry) 93 | { 94 | [coordinator userReplacedInFormattedValueSubstringAtRange:range withString:replacement]; 95 | entry.text = coordinator.formattedValue; 96 | entry.selectedTextRange = coordinator.currentSelectionRangeInFormattedValue; 97 | }]; 98 | } 99 | 100 | - (void)endEditing 101 | { 102 | [self handleValueChange:^(FTCTextEntryFormatCoordinator *coordinator, id entry) 103 | { 104 | [coordinator endEditing]; 105 | entry.text = coordinator.formattedValue; 106 | }]; 107 | } 108 | 109 | - (void)handleValueChange:(void(^)(FTCTextEntryFormatCoordinator *coordinator, id entry))valueChange 110 | { 111 | NSString *initialValue = formatCoordinator.rawValue; 112 | 113 | if( NULL != valueChange ) 114 | { 115 | valueChange(formatCoordinator, entryUI); 116 | } 117 | 118 | if( NULL == _didChangeValueHandler ) 119 | { 120 | return; 121 | } 122 | 123 | BOOL valueIsReallyChanged; 124 | if( (nil == initialValue) && (nil == formatCoordinator.rawValue) ) 125 | { 126 | valueIsReallyChanged = NO; 127 | } 128 | else 129 | { 130 | valueIsReallyChanged = (NO == [initialValue isEqualToString:formatCoordinator.rawValue]); 131 | } 132 | 133 | if( valueIsReallyChanged ) 134 | { 135 | _didChangeValueHandler(); 136 | } 137 | } 138 | 139 | - (void)applyFormattingConfig:(FTCTextEntryFormattingConfig *)config 140 | { 141 | assert( nil != config ); 142 | 143 | [formatCoordinator applyConfig:config]; 144 | 145 | NSRange currentRange = entryUI.selectedTextRange; 146 | if( (NSNotFound != currentRange.location) && (0 != currentRange.location) && [config isEqualToConfig:currentConfig] ) 147 | { 148 | formatCoordinator.currentSelectionRangeInFormattedValue = currentRange; 149 | } 150 | 151 | entryUI.text = formatCoordinator.formattedValue; 152 | 153 | entryUI.selectedTextRange = formatCoordinator.currentSelectionRangeInFormattedValue; 154 | 155 | currentConfig = config; 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /Src/Core/FTCTextEntryFormattingConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @protocol FTCTextEntryFormatter; 22 | @protocol FTCTextEntryEditingInputFilter; 23 | @protocol FTCTextEntryNotEditingInputFilter; 24 | 25 | NS_ASSUME_NONNULL_BEGIN 26 | 27 | @interface FTCTextEntryFormattingConfig : NSObject 28 | 29 | @property (nonatomic, strong) id editingFormatter; 30 | @property (nonatomic, strong) id editingInputFilter; 31 | 32 | @property (nonatomic, strong) id notEditingFormatter; 33 | @property (nonatomic, strong) id notEditingInputFilter; 34 | 35 | - (BOOL)isEqualToConfig:(FTCTextEntryFormattingConfig *)config; 36 | 37 | - (instancetype)init NS_DESIGNATED_INITIALIZER; 38 | 39 | - (instancetype)initWithFormatter:(id)formatter 40 | inputFilter:(id)inputFilter; 41 | 42 | @end 43 | 44 | NS_ASSUME_NONNULL_END 45 | -------------------------------------------------------------------------------- /Src/Core/FTCTextEntryFormattingConfig.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormattingConfig.h" 22 | #import "FTCNoFormattingFormatter.h" 23 | #import "FTCNoFilteringFilter.h" 24 | 25 | @implementation FTCTextEntryFormattingConfig 26 | 27 | - (instancetype)init 28 | { 29 | self = [super init]; 30 | 31 | _editingFormatter = [[FTCNoFormattingFormatter alloc] init]; 32 | _editingInputFilter = [[FTCNoFilteringFilter alloc] init]; 33 | 34 | _notEditingFormatter = [[FTCNoFormattingFormatter alloc] init]; 35 | _notEditingInputFilter = [[FTCNoFilteringFilter alloc] init]; 36 | 37 | return self; 38 | } 39 | 40 | - (instancetype)initWithFormatter:(id)formatter 41 | inputFilter:(id)inputFilter 42 | { 43 | self = [self init]; 44 | 45 | _editingFormatter = formatter; 46 | _editingInputFilter = inputFilter; 47 | 48 | _notEditingFormatter = formatter; 49 | _notEditingInputFilter = inputFilter; 50 | 51 | return self; 52 | } 53 | 54 | - (void)setEditingFormatter:(id)editingFormatter 55 | { 56 | assert( (nil != editingFormatter) && "Argument 'editingFormatter' must not be nil." ); 57 | 58 | _editingFormatter = editingFormatter; 59 | } 60 | 61 | - (void)setEditingInputFilter:(id)editingInputFilter 62 | { 63 | assert( (nil != editingInputFilter) && "Argument 'editingInputFilter' must not be nil." ); 64 | 65 | _editingInputFilter = editingInputFilter; 66 | } 67 | 68 | - (void)setNotEditingFormatter:(id)notEditingFormatter 69 | { 70 | assert( (nil != notEditingFormatter) && "Argument 'notEditingFormatter' must not be nil." ); 71 | 72 | _notEditingFormatter = notEditingFormatter; 73 | } 74 | 75 | - (void)setNotEditingInputFilter:(id)notEditingInputFilter 76 | { 77 | assert( (nil != notEditingInputFilter) && "Argument 'notEditingInputFilter' must not be nil." ); 78 | 79 | _notEditingInputFilter = notEditingInputFilter; 80 | } 81 | 82 | - (BOOL)isEqualToConfig:(FTCTextEntryFormattingConfig *)config 83 | { 84 | if( nil == config ) 85 | { 86 | return NO; 87 | } 88 | 89 | BOOL editingFormatterEquality = [self.editingFormatter isEqual:config.editingFormatter]; 90 | BOOL editingInputFormatterEquality = [self.editingInputFilter isEqual:config.editingInputFilter]; 91 | BOOL notEditingFormatterEquality = [self.notEditingFormatter isEqual:config.notEditingFormatter]; 92 | BOOL notEditingInputFormatterEquality = [self.notEditingInputFilter isEqual:config.notEditingInputFilter]; 93 | 94 | return editingFormatterEquality && editingInputFormatterEquality && notEditingFormatterEquality && notEditingInputFormatterEquality; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /Src/Core/Formatting/FTCNoFormattingFormatter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCNoFormattingFormatter : NSObject 26 | 27 | - (instancetype)init; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Src/Core/Formatting/FTCNoFormattingFormatter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCNoFormattingFormatter.h" 22 | 23 | @implementation FTCNoFormattingFormatter 24 | 25 | - (instancetype)init 26 | { 27 | self = [super init]; 28 | 29 | return self; 30 | } 31 | 32 | - (NSString *)rawFromFormatted:(NSString *)formattedValue 33 | { 34 | return formattedValue; 35 | } 36 | 37 | - (NSString *)formattedFromRaw:(NSString *)rawValue 38 | { 39 | return rawValue; 40 | } 41 | 42 | - (NSRange)rangeInFormattedValueForRange:(NSRange)rangeInRawValue inRawValue:(NSString *)rawValue 43 | { 44 | return rangeInRawValue; 45 | } 46 | 47 | - (NSRange)rangeInRawValueForRange:(NSRange)rangeInFormattedValue inFormattedValue:(NSString *)formattedValue 48 | { 49 | return rangeInFormattedValue; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Src/Core/Formatting/FTCTextEntryFormatter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | @protocol FTCTextEntryFormatter 24 | 25 | - (NSString *)rawFromFormatted:(nullable NSString *)formattedValue; 26 | - (NSString *)formattedFromRaw:(nullable NSString *)rawValue; 27 | 28 | - (NSRange)rangeInFormattedValueForRange:(NSRange)rangeInRawValue inRawValue:(nullable NSString *)rawValue; 29 | - (NSRange)rangeInRawValueForRange:(NSRange)rangeInFormattedValue inFormattedValue:(nullable NSString *)formattedValue; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /Src/Core/Formatting/MaskFormatter/FTCMaskFormatter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatter.h" 22 | 23 | @protocol FTCMaskFormatterConfig; 24 | 25 | NS_ASSUME_NONNULL_BEGIN 26 | 27 | @interface FTCMaskFormatter : NSObject 28 | 29 | - (instancetype)initWithConfig:(id)config NS_DESIGNATED_INITIALIZER; 30 | 31 | - (instancetype)init NS_UNAVAILABLE; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /Src/Core/Formatting/MaskFormatter/FTCMaskFormatter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMaskFormatter.h" 22 | #import "FTCMaskFormatterConfig.h" 23 | #import "FTCTextEntryFormattingStringUtils.h" 24 | 25 | @implementation FTCMaskFormatter 26 | { 27 | id config; 28 | } 29 | 30 | - (instancetype)initWithConfig:(id)aConfig 31 | { 32 | assert( nil != aConfig ); 33 | 34 | self = [super init]; 35 | 36 | config = aConfig; 37 | 38 | return self; 39 | } 40 | 41 | - (NSString *)rawFromFormatted:(NSString *)formattedValue 42 | { 43 | return [self rawValueStringFromFormattedString:formattedValue 44 | inFormattedRange:NSMakeRange(0, formattedValue.length)]; 45 | } 46 | 47 | - (NSString *)formattedFromRaw:(NSString *)rawValue 48 | { 49 | NSString * const mask = config.mask; 50 | 51 | if( (FTCMaskFormatterConfigTailModeCutAndForceHead == config.tailMode) && (rawValue.length == 0) ) 52 | { 53 | NSString *maskCharacterString = [FTCTextEntryFormattingStringUtils stringWithCharacter:config.maskCharacter]; 54 | return [config.mask substringToIndex:[config.mask rangeOfString:maskCharacterString].location]; 55 | } 56 | 57 | NSMutableString *formattedValue = [[NSMutableString alloc] initWithCapacity:mask.length]; 58 | 59 | for( NSUInteger indexInFormat = 0, indexInRawValue = 0; indexInFormat < mask.length; ++indexInFormat ) 60 | { 61 | if( ((FTCMaskFormatterConfigTailModeCutAndForceHead == config.tailMode) || (FTCMaskFormatterConfigTailModeCut == config.tailMode)) 62 | && indexInRawValue == rawValue.length ) 63 | { 64 | break; 65 | } 66 | 67 | if( config.maskCharacter != [mask characterAtIndex:indexInFormat] || indexInRawValue >= rawValue.length ) 68 | { 69 | unichar c = [mask characterAtIndex:indexInFormat]; 70 | [formattedValue appendString:[FTCTextEntryFormattingStringUtils stringWithCharacter:c]]; 71 | } 72 | else 73 | { 74 | unichar c = [rawValue characterAtIndex:indexInRawValue++]; 75 | [formattedValue appendString:[FTCTextEntryFormattingStringUtils stringWithCharacter:c]]; 76 | } 77 | } 78 | 79 | return [formattedValue copy]; 80 | } 81 | 82 | - (NSRange)rangeInFormattedValueForRange:(NSRange)rangeInRawValue inRawValue:(NSString *)rawValue 83 | { 84 | assert( (rawValue.length >= rangeInRawValue.location + rangeInRawValue.length) && "Argument 'rangeInRawValue' is out of bounds of 'rawValue'" ); 85 | 86 | if( 0 == rangeInRawValue.location ) 87 | { 88 | /* 89 | * Если начало, то ставим каретку спереди первого raw-символа 90 | */ 91 | NSUInteger numberOfFirstRawSymbolInRange = 1; 92 | NSUInteger numberOfLastRawSymbolInRange = rangeInRawValue.length + 1; 93 | 94 | NSUInteger position = [self positionOfRawSymbolInFormattedValue:numberOfFirstRawSymbolInRange]; 95 | NSUInteger positionLast = [self positionOfRawSymbolInFormattedValue:numberOfLastRawSymbolInRange]; 96 | 97 | return NSMakeRange(position, positionLast - position); 98 | } 99 | else 100 | { 101 | /* 102 | * Если не начало, то ставим каретку позади предыдущего raw-символа 103 | */ 104 | NSUInteger numberOfFirstRawSymbolInRange = rangeInRawValue.location; 105 | NSUInteger numberOfLastRawSymbolInRange = (rangeInRawValue.location + rangeInRawValue.length); 106 | 107 | NSUInteger position = [self positionOfRawSymbolInFormattedValue:numberOfFirstRawSymbolInRange] + 1; 108 | NSUInteger positionLast = [self positionOfRawSymbolInFormattedValue:numberOfLastRawSymbolInRange] + 1; 109 | 110 | return NSMakeRange(position, positionLast - position); 111 | } 112 | } 113 | 114 | - (NSRange)rangeInRawValueForRange:(NSRange)rangeInFormattedValue 115 | inFormattedValue:(NSString *)formattedValue 116 | { 117 | NSUInteger symbolsToLocation = [self countOfRawSymbolsInFormattedString:formattedValue 118 | inRange:NSMakeRange(0, rangeInFormattedValue.location)]; 119 | 120 | NSUInteger symbolsInRange = [self countOfRawSymbolsInFormattedString:formattedValue 121 | inRange:rangeInFormattedValue]; 122 | 123 | return NSMakeRange(symbolsToLocation, symbolsInRange); 124 | } 125 | 126 | - (NSUInteger)positionOfRawSymbolInFormattedValue:(const NSUInteger)rawNumber 127 | { 128 | NSUInteger position = 0; 129 | 130 | NSUInteger currentPosition = 0; 131 | 132 | for( NSUInteger i = 0; i < config.mask.length; ++i ) 133 | { 134 | if( config.maskCharacter != [config.mask characterAtIndex:i] ) 135 | { 136 | continue; 137 | } 138 | 139 | ++currentPosition; 140 | 141 | position = i; 142 | 143 | if( rawNumber == currentPosition ) 144 | { 145 | break; 146 | } 147 | } 148 | 149 | return position; 150 | } 151 | 152 | - (nonnull NSString *)rawValueStringFromFormattedString:(nullable NSString *const)formattedString 153 | inFormattedRange:(const NSRange)range 154 | { 155 | NSString * const mask = config.mask; 156 | 157 | NSMutableString *rawString = [[NSMutableString alloc] init]; 158 | 159 | for( NSUInteger i = range.location; (i < range.location + range.length) && (i < formattedString.length); ++i ) 160 | { 161 | if ( config.maskCharacter == [mask characterAtIndex:i] && config.maskCharacter != [formattedString characterAtIndex:i] ) 162 | { 163 | unichar c = [formattedString characterAtIndex:i]; 164 | [rawString appendString:[FTCTextEntryFormattingStringUtils stringWithCharacter:c]]; 165 | } 166 | } 167 | 168 | return [rawString copy]; 169 | } 170 | 171 | - (NSUInteger)countOfRawSymbolsInFormattedString:(nullable NSString *const)formattedString 172 | inRange:(const NSRange)range 173 | { 174 | NSString *rawString = [self rawValueStringFromFormattedString:formattedString 175 | inFormattedRange:range]; 176 | return rawString.length; 177 | } 178 | 179 | - (instancetype)init 180 | { 181 | assert( false && "Won't happen" ); 182 | return nil; 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /Src/Core/Formatting/MaskFormatter/FTCMaskFormatterConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | typedef NS_ENUM(NSInteger, FTCMaskFormatterConfigTailMode) 24 | { 25 | FTCMaskFormatterConfigTailModeNone = 0, 26 | FTCMaskFormatterConfigTailModeCut, 27 | FTCMaskFormatterConfigTailModeCutAndForceHead 28 | }; 29 | 30 | @protocol FTCMaskFormatterConfig 31 | 32 | @property (nonatomic, readonly) NSString *mask; 33 | @property (nonatomic, readonly) unichar maskCharacter; 34 | @property (nonatomic, readonly) NSUInteger countMaskCharacters; 35 | @property (nonatomic, readonly) FTCMaskFormatterConfigTailMode tailMode; 36 | 37 | @end 38 | 39 | NS_ASSUME_NONNULL_END 40 | -------------------------------------------------------------------------------- /Src/Core/Formatting/MaskFormatter/FTCMaskFormatterGenericConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMaskFormatterConfig.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCMaskFormatterGenericConfig : NSObject 26 | 27 | @property (nonatomic, readwrite) FTCMaskFormatterConfigTailMode tailMode; // default FTCMaskFormatterConfigTailModeNone 28 | 29 | - (instancetype)initWithMask:(NSString *)mask maskCharacter:(NSString *)maskCharacter NS_DESIGNATED_INITIALIZER; 30 | 31 | - (instancetype)init NS_UNAVAILABLE; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /Src/Core/Formatting/MaskFormatter/FTCMaskFormatterGenericConfig.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMaskFormatterGenericConfig.h" 22 | 23 | @implementation FTCMaskFormatterGenericConfig 24 | 25 | @synthesize mask = _mask; 26 | @synthesize maskCharacter = _maskCharacter; 27 | @synthesize countMaskCharacters = _countMaskCharacters; 28 | 29 | - (instancetype)initWithMask:(NSString *)mask maskCharacter:(NSString *)maskCharacter 30 | { 31 | assert( nil != mask ); 32 | assert( maskCharacter.length == 1 ); 33 | 34 | self = [super init]; 35 | 36 | _mask = mask; 37 | _maskCharacter = [maskCharacter characterAtIndex:0]; 38 | _tailMode = FTCMaskFormatterConfigTailModeNone; 39 | 40 | NSUInteger countMaskCharacters = 0; 41 | for( NSUInteger i = 0; i < _mask.length; ++i ) 42 | { 43 | if( [_mask characterAtIndex:i] == _maskCharacter ) 44 | { 45 | ++countMaskCharacters; 46 | } 47 | } 48 | 49 | _countMaskCharacters = countMaskCharacters; 50 | 51 | return self; 52 | } 53 | 54 | - (instancetype)init 55 | { 56 | assert( false && "Won't happen" ); 57 | return nil; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Src/Core/Formatting/PostfixFormatter/FTCPostfixFormatter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCPostfixFormatter : NSObject 26 | 27 | @property (nonatomic, nullable, readonly) NSString *postfix; 28 | 29 | - (instancetype)initWithPostfix:(nullable NSString *)postfix; 30 | 31 | - (instancetype)init NS_UNAVAILABLE; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /Src/Core/Formatting/PostfixFormatter/FTCPostfixFormatter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCPostfixFormatter.h" 22 | 23 | @implementation FTCPostfixFormatter 24 | 25 | - (instancetype)init 26 | { 27 | assert(false && "Won't happen"); 28 | return nil; 29 | } 30 | 31 | - (instancetype)initWithPostfix:(NSString *)postfix 32 | { 33 | self = [super init]; 34 | 35 | _postfix = postfix; 36 | 37 | return self; 38 | } 39 | 40 | - (NSString *)rawFromFormatted:(NSString *)formattedValue 41 | { 42 | if( NO == [self canShowPostfix] ) 43 | { 44 | return formattedValue; 45 | } 46 | 47 | NSString *result = formattedValue; 48 | 49 | if(result.length >= _postfix.length) 50 | { 51 | NSString *const possiblePostfix = [result substringFromIndex:result.length - _postfix.length]; 52 | 53 | if([possiblePostfix isEqualToString:_postfix]) 54 | { 55 | result = [result substringToIndex:result.length - _postfix.length]; 56 | } 57 | } 58 | 59 | return result; 60 | } 61 | 62 | - (NSString *)formattedFromRaw:(NSString *)rawValue 63 | { 64 | NSString *result = (rawValue != nil ? rawValue : @""); 65 | 66 | if( NO == [self canShowPostfix] ) 67 | { 68 | return result; 69 | } 70 | 71 | return [result stringByAppendingString:_postfix]; 72 | } 73 | 74 | - (NSRange)rangeInFormattedValueForRange:(NSRange)rangeInRawValue inRawValue:(NSString *)rawValue 75 | { 76 | assert( (rawValue.length >= rangeInRawValue.location + rangeInRawValue.length) && "Argument 'rangeInRawValue' is out of bounds of 'rawValue'" ); 77 | 78 | return rangeInRawValue; 79 | } 80 | 81 | - (NSRange)rangeInRawValueForRange:(NSRange)rangeInFormattedValue inFormattedValue:(NSString *)formattedValue 82 | { 83 | assert( (formattedValue.length >= rangeInFormattedValue.location + rangeInFormattedValue.length) && "Argument 'rangeInFormattedValue' is out of bounds of 'formattedValue'" ); 84 | 85 | NSRange rawRange = rangeInFormattedValue; 86 | 87 | if( [self canShowPostfix] ) 88 | { 89 | NSRange postfixRange = [formattedValue rangeOfString:_postfix]; 90 | if( NSNotFound != postfixRange.location ) 91 | { 92 | if( rawRange.location > postfixRange.location ) 93 | { 94 | rawRange.location = postfixRange.location; 95 | } 96 | 97 | if( (rawRange.location + rawRange.length) > postfixRange.location ) 98 | { 99 | NSInteger exceedingLength = (rawRange.location + rawRange.length) - postfixRange.location; 100 | 101 | rawRange.length = rawRange.length - exceedingLength; 102 | } 103 | } 104 | } 105 | 106 | return rawRange; 107 | } 108 | 109 | - (BOOL)canShowPostfix 110 | { 111 | if( nil == _postfix || 0 == _postfix.length ) 112 | { 113 | return NO; 114 | } 115 | 116 | return YES; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/DigitsValueFilter/FTCDigitsValueFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryEditingInputFilter.h" 22 | #import "FTCTextEntryNotEditingInputFilter.h" 23 | 24 | NS_ASSUME_NONNULL_BEGIN 25 | 26 | @interface FTCDigitsValueFilter : NSObject 27 | 28 | @property (nonatomic, readonly) NSUInteger maxLength; 29 | 30 | - (instancetype)initWithMaxLength:(NSUInteger)length;//set '0' for infinite length 31 | 32 | - (instancetype)init NS_UNAVAILABLE; 33 | 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/DigitsValueFilter/FTCDigitsValueFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCDigitsValueFilter.h" 22 | #import "FTCFilteredString.h" 23 | #import "FTCTextEntryFormattingStringUtils.h" 24 | 25 | @implementation FTCDigitsValueFilter 26 | 27 | - (instancetype)initWithMaxLength:(NSUInteger)aLength 28 | { 29 | self = [super init]; 30 | 31 | _maxLength = aLength; 32 | 33 | return self; 34 | } 35 | 36 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString atRange:(NSRange)range withString:(NSString *)replacement shouldTrim:(BOOL)shouldTrim 37 | { 38 | assert( (nil != originalString) && "Argument 'originalString' must not be nil." ); 39 | assert( (nil != replacement) && "Argument 'replacement' must not be nil." ); 40 | 41 | NSString *filteredReplacement = [FTCTextEntryFormattingStringUtils stringWithDecimalDigitsFromString:replacement]; 42 | 43 | NSString *filteredString = [originalString stringByReplacingCharactersInRange:range withString:filteredReplacement]; 44 | 45 | if( shouldTrim && (filteredString.length > _maxLength) && (0 != _maxLength) ) 46 | { 47 | filteredString = [filteredString substringToIndex:_maxLength]; 48 | } 49 | 50 | if( 0 == _maxLength || filteredString.length <= _maxLength ) 51 | { 52 | return [[FTCFilteredString alloc] initWithString:filteredString range:NSMakeRange(range.location, filteredReplacement.length)]; 53 | } 54 | else 55 | { 56 | return [[FTCFilteredString alloc] initWithString:originalString range:NSMakeRange(range.location, 0)]; 57 | } 58 | } 59 | 60 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString atRange:(NSRange)range withString:(NSString *)replacement 61 | { 62 | return [self replaceSubstringInString:originalString atRange:range withString:replacement shouldTrim:NO]; 63 | } 64 | 65 | - (NSString *)filterString:(NSString *)string 66 | { 67 | assert( nil != string ); 68 | 69 | return [self replaceSubstringInString:@"" atRange:NSMakeRange(0, 0) withString:string shouldTrim:YES].string; 70 | } 71 | 72 | - (instancetype)init 73 | { 74 | assert( NO ); 75 | return nil; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/FTCFilteredString.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | @interface FTCFilteredString : NSObject 24 | 25 | @property (nonatomic, readonly) NSString *string; 26 | @property (nonatomic, readonly) NSRange range; 27 | 28 | - (instancetype)initWithString:(NSString *)string range:(NSRange)range; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/FTCFilteredString.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCFilteredString.h" 22 | 23 | @implementation FTCFilteredString 24 | 25 | - (instancetype)initWithString:(NSString *)string range:(NSRange)range 26 | { 27 | self = [super init]; 28 | 29 | _string = string; 30 | _range = range; 31 | 32 | return self; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/FTCNoFilteringFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryEditingInputFilter.h" 22 | #import "FTCTextEntryNotEditingInputFilter.h" 23 | 24 | NS_ASSUME_NONNULL_BEGIN 25 | 26 | @interface FTCNoFilteringFilter : NSObject 27 | 28 | - (instancetype)init; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/FTCNoFilteringFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCNoFilteringFilter.h" 22 | #import "FTCFilteredString.h" 23 | 24 | @implementation FTCNoFilteringFilter 25 | 26 | - (instancetype)init 27 | { 28 | self = [super init]; 29 | 30 | return self; 31 | } 32 | 33 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString atRange:(NSRange)range withString:(NSString *)replacement 34 | { 35 | assert( (nil != originalString) && "Argument 'originalString' must not be nil." ); 36 | assert( (nil != replacement) && "Argument 'replacement' must not be nil." ); 37 | 38 | NSString *resultString = [originalString stringByReplacingCharactersInRange:range withString:replacement]; 39 | const NSRange resultRange = NSMakeRange(range.location, replacement.length); 40 | 41 | return [[FTCFilteredString alloc] initWithString:resultString range:resultRange]; 42 | } 43 | 44 | - (NSString *)filterString:(NSString *)string 45 | { 46 | return string; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/FTCTextEntryEditingInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @class FTCFilteredString; 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @protocol FTCTextEntryEditingInputFilter 26 | 27 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString 28 | atRange:(NSRange)range 29 | withString:(NSString *)replacement; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/FTCTextEntryNotEditingInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import Foundation; 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @protocol FTCTextEntryNotEditingInputFilter 26 | 27 | - (NSString *)filterString:(NSString *)string; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/LimitedLengthInputFilter/FTCLimitedLengthInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryEditingInputFilter.h" 22 | #import "FTCTextEntryNotEditingInputFilter.h" 23 | 24 | NS_ASSUME_NONNULL_BEGIN 25 | 26 | @interface FTCLimitedLengthInputFilter : NSObject 27 | 28 | - (instancetype)initWithMaximumLength:(NSUInteger)maximumLength NS_DESIGNATED_INITIALIZER; 29 | - (instancetype)init NS_UNAVAILABLE; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/LimitedLengthInputFilter/FTCLimitedLengthInputFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCLimitedLengthInputFilter.h" 22 | #import "FTCFilteredString.h" 23 | 24 | @implementation FTCLimitedLengthInputFilter 25 | { 26 | NSUInteger maxLength; 27 | } 28 | 29 | - (instancetype)init 30 | { 31 | assert( false && "Won't happen" ); 32 | return nil; 33 | } 34 | 35 | - (instancetype)initWithMaximumLength:(NSUInteger)maximumLength 36 | { 37 | self = [super init]; 38 | 39 | maxLength = maximumLength; 40 | 41 | return self; 42 | } 43 | 44 | - (NSString *)trimmedString:(NSString *)string 45 | { 46 | assert( nil != string && "String must not be nil" ); 47 | 48 | if( string.length > maxLength ) 49 | { 50 | return [string substringToIndex:maxLength]; 51 | } 52 | 53 | return string; 54 | } 55 | 56 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString atRange:(NSRange)range withString:(NSString *)replacement 57 | { 58 | assert( (nil != originalString) && "Argument 'originalString' must not be nil." ); 59 | assert( (nil != replacement) && "Argument 'replacement' must not be nil." ); 60 | 61 | NSString *replacedString = [originalString stringByReplacingCharactersInRange:range withString:replacement];; 62 | 63 | if( replacedString.length <= maxLength ) 64 | { 65 | NSRange resultRange = NSMakeRange(range.location, replacement.length); 66 | return [[FTCFilteredString alloc] initWithString:replacedString range:resultRange]; 67 | } 68 | 69 | NSString *trimmedString = [self trimmedString:originalString]; 70 | 71 | return [[FTCFilteredString alloc] initWithString:trimmedString range:NSMakeRange(range.location, 0)]; 72 | } 73 | 74 | - (NSString *)filterString:(NSString *)string 75 | { 76 | assert( nil != string ); 77 | 78 | return [self trimmedString:string]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/ToUpperCaseInputFilter/FTCToUpperCaseInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryEditingInputFilter.h" 22 | #import "FTCTextEntryNotEditingInputFilter.h" 23 | 24 | NS_ASSUME_NONNULL_BEGIN 25 | 26 | @interface FTCToUpperCaseInputFilter : NSObject 27 | 28 | - (instancetype)init; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /Src/Core/InputFiltering/ToUpperCaseInputFilter/FTCToUpperCaseInputFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCToUpperCaseInputFilter.h" 22 | #import "FTCFilteredString.h" 23 | 24 | @implementation FTCToUpperCaseInputFilter 25 | 26 | - (instancetype)init 27 | { 28 | self = [super init]; 29 | 30 | return self; 31 | } 32 | 33 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString atRange:(NSRange)range withString:(NSString *)replacement 34 | { 35 | assert( (nil != originalString) && "Argument 'originalString' must not be nil." ); 36 | assert( (nil != replacement) && "Argument 'replacement' must not be nil." ); 37 | 38 | NSString * const upperCaseReplacement = [replacement uppercaseString]; 39 | 40 | NSString * const resultString = [originalString stringByReplacingCharactersInRange:range withString:upperCaseReplacement]; 41 | const NSRange resultRange = NSMakeRange(range.location, upperCaseReplacement.length); 42 | 43 | return [[FTCFilteredString alloc] initWithString:resultString range:resultRange]; 44 | } 45 | 46 | - (NSString *)filterString:(NSString *)string 47 | { 48 | assert( nil != string ); 49 | 50 | return [string uppercaseString]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Src/FTCTextEntryFormatting.h: -------------------------------------------------------------------------------- 1 | #import "FTCMoneyEntryNotEditingInputFilter.h" 2 | #import "FTCMoneyEntryNotEditingFormatter.h" 3 | #import "FTCFilteredString.h" 4 | #import "FTCMoneyEntryFormatUtils.h" 5 | #import "FTCTextEntry.h" 6 | #import "FTCMaskFormatterGenericConfig.h" 7 | #import "FTCLimitedLengthInputFilter.h" 8 | #import "FTCMoneyEntryEditingFormatter.h" 9 | #import "FTCMaskFormatter.h" 10 | #import "FTCMoneyEntryEditingInputFilter.h" 11 | #import "FTCMaskFormatterConfig.h" 12 | #import "FTCIntegralMoneyAmountEntryNotEditingInputFilter.h" 13 | #import "FTCNoFilteringFilter.h" 14 | #import "FTCTextEntryFormattingConfig.h" 15 | #import "FTCTextEntryFormattingConfigFactory.h" 16 | #import "FTCTextEntryNotEditingInputFilter.h" 17 | #import "FTCTextInputUtils.h" 18 | #import "FTCDigitsValueFilter.h" 19 | #import "FTCNoFormattingFormatter.h" 20 | #import "FTCTextEntryEditingInputFilter.h" 21 | #import "FTCTextEntryFormatCoordinatorHelper.h" 22 | #import "FTCIntegralMoneyAmountEntryEditingInputFilter.h" 23 | #import "FTCTextEntryFormatCoordinator.h" 24 | #import "FTCTextEntryFormatting.h" 25 | #import "FTCTextEntryFormatter.h" 26 | #import "FTCPostfixFormatter.h" 27 | #import "FTCToUpperCaseInputFilter.h" 28 | #import "FTCTextFieldFormatCoordinator.h" 29 | -------------------------------------------------------------------------------- /Src/FTCTextEntryFormattingConfigFactory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatting.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @class FTCTextEntryFormattingConfig; 26 | 27 | @interface FTCTextEntryFormattingConfigFactory : NSObject 28 | 29 | + (FTCTextEntryFormattingConfig *)mobilePhoneConfigWithMask:(NSString *)mask 30 | maskChar:(NSString *)maskChar NS_SWIFT_NAME( mobilePhoneConfig(with:maskChar:) ); 31 | + (FTCTextEntryFormattingConfig *)mobilePhoneConfigWithMask:(NSString *)mask 32 | maskChar:(NSString *)maskChar 33 | tailMode:(FTCMaskFormatterConfigTailMode)tailMode NS_SWIFT_NAME( mobilePhoneConfig(with:maskChar:tailMode:) ); 34 | 35 | + (FTCTextEntryFormattingConfig *)moneyConfigWithCurrency:(nullable NSString *)currency 36 | onlyIntegral:(BOOL)onlyIntegral NS_SWIFT_NAME( moneyConfig(with:onlyIntegral:) ); 37 | 38 | - (instancetype)init NS_UNAVAILABLE; 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /Src/FTCTextEntryFormattingConfigFactory.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormattingConfigFactory.h" 22 | #import "FTCTextEntryFormattingConfig.h" 23 | #import "FTCMaskFormatterGenericConfig.h" 24 | #import "FTCMaskFormatter.h" 25 | #import "FTCDigitsValueFilter.h" 26 | #import "FTCMoneyEntryNotEditingFormatter.h" 27 | #import "FTCMoneyEntryEditingFormatter.h" 28 | #import "FTCMoneyEntryNotEditingInputFilter.h" 29 | #import "FTCMoneyEntryEditingInputFilter.h" 30 | #import "FTCIntegralMoneyAmountEntryNotEditingInputFilter.h" 31 | #import "FTCIntegralMoneyAmountEntryEditingInputFilter.h" 32 | 33 | @implementation FTCTextEntryFormattingConfigFactory 34 | 35 | + (FTCTextEntryFormattingConfig *)mobilePhoneConfigWithMask:(NSString *)mask maskChar:(NSString *)maskChar 36 | { 37 | return [self mobilePhoneConfigWithMask:mask maskChar:maskChar tailMode:FTCMaskFormatterConfigTailModeNone]; 38 | } 39 | 40 | + (FTCTextEntryFormattingConfig *)mobilePhoneConfigWithMask:(NSString *)mask maskChar:(NSString *)maskChar tailMode:(FTCMaskFormatterConfigTailMode)tailMode 41 | { 42 | __auto_type maskConfig = [[FTCMaskFormatterGenericConfig alloc] initWithMask:mask maskCharacter:maskChar]; 43 | maskConfig.tailMode = tailMode; 44 | 45 | __auto_type maskFormatter = [[FTCMaskFormatter alloc] initWithConfig:maskConfig]; 46 | __auto_type inputFilter = [[FTCDigitsValueFilter alloc] initWithMaxLength:maskConfig.countMaskCharacters]; 47 | 48 | __auto_type formattingConfig = [[FTCTextEntryFormattingConfig alloc] init]; 49 | 50 | formattingConfig.editingInputFilter = inputFilter; 51 | formattingConfig.editingFormatter = maskFormatter; 52 | formattingConfig.notEditingInputFilter = inputFilter; 53 | formattingConfig.notEditingFormatter = maskFormatter; 54 | 55 | return formattingConfig; 56 | } 57 | 58 | + (FTCTextEntryFormattingConfig *)moneyConfigWithCurrency:(nullable NSString *)currency onlyIntegral:(BOOL)onlyIntegral 59 | { 60 | __auto_type formattingConfig = [[FTCTextEntryFormattingConfig alloc] init]; 61 | 62 | formattingConfig.notEditingFormatter = [[FTCMoneyEntryNotEditingFormatter alloc] initWithCurrency:currency]; 63 | formattingConfig.editingFormatter = [[FTCMoneyEntryEditingFormatter alloc] initWithCurrency:currency]; 64 | 65 | if( onlyIntegral ) 66 | { 67 | formattingConfig.notEditingInputFilter = [[FTCIntegralMoneyAmountEntryNotEditingInputFilter alloc] init]; 68 | formattingConfig.editingInputFilter = [[FTCIntegralMoneyAmountEntryEditingInputFilter alloc] init]; 69 | } 70 | else 71 | { 72 | formattingConfig.notEditingInputFilter = [[FTCMoneyEntryNotEditingInputFilter alloc] init]; 73 | formattingConfig.editingInputFilter = [[FTCMoneyEntryEditingInputFilter alloc] init]; 74 | } 75 | 76 | return formattingConfig; 77 | } 78 | 79 | - (instancetype)init 80 | { 81 | assert( NO ); 82 | return nil; 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Src/FTCTextFieldFormatCoordinator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import UIKit; 22 | 23 | @class FTCTextEntryFormattingConfig; 24 | 25 | NS_ASSUME_NONNULL_BEGIN 26 | 27 | @interface FTCTextFieldFormatCoordinator : NSObject 28 | 29 | @property (nonatomic, weak) id textFieldDelegate; 30 | 31 | @property (nonatomic, nullable, copy) void (^didChangeValueHandler)(void); 32 | @property (nonatomic, nullable, copy) NSString *rawValue; 33 | @property (nonatomic, readonly) NSString *formattedValue; 34 | 35 | /*! 36 | * Устанавливает delegate у textField. Используйте поле textFieldDelegate если необходим delegate. 37 | * Если textField уже имеет delegate, то он будет установлен в поле textFieldDelegate. 38 | **/ 39 | - (instancetype)initWithTextField:(UITextField *)textField; 40 | 41 | - (void)applyFormattingConfig:(FTCTextEntryFormattingConfig *)config NS_SWIFT_NAME( apply(formattingConfig:) ); 42 | 43 | - (instancetype)init NS_UNAVAILABLE; 44 | 45 | @end 46 | 47 | NS_ASSUME_NONNULL_END 48 | -------------------------------------------------------------------------------- /Src/FTCTextFieldFormatCoordinator.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextFieldFormatCoordinator.h" 22 | #import "FTCTextEntry.h" 23 | #import "FTCTextInputUtils.h" 24 | #import "FTCTextEntryFormatCoordinatorHelper.h" 25 | #import "FTCTextEntryFormattingConfig.h" 26 | 27 | @interface FTCTextFieldFormatCoordinator() 28 | { 29 | UITextField * __weak textField; 30 | 31 | FTCTextEntryFormatCoordinatorHelper *formatCoordinatorHelper; 32 | } 33 | 34 | @end 35 | 36 | @implementation FTCTextFieldFormatCoordinator 37 | 38 | - (instancetype)initWithTextField:(UITextField *)aTextField 39 | { 40 | assert(aTextField != nil); 41 | 42 | self = [super init]; 43 | 44 | textField = aTextField; 45 | 46 | _textFieldDelegate = textField.delegate; 47 | textField.delegate = self; 48 | 49 | formatCoordinatorHelper = [[FTCTextEntryFormatCoordinatorHelper alloc] initWithUI:self]; 50 | 51 | return self; 52 | } 53 | 54 | // MARK: Public 55 | 56 | - (void)setDidChangeValueHandler:(void (^)(void))didChangeValueHandler 57 | { 58 | [formatCoordinatorHelper setDidChangeValueHandler: didChangeValueHandler]; 59 | } 60 | 61 | - (void (^)(void))didChangeValueHandler 62 | { 63 | return [formatCoordinatorHelper didChangeValueHandler]; 64 | } 65 | 66 | - (void)setRawValue:(NSString *)rawValue 67 | { 68 | [formatCoordinatorHelper setRawValue:rawValue]; 69 | } 70 | 71 | - (NSString *)rawValue 72 | { 73 | return [formatCoordinatorHelper rawValue]; 74 | } 75 | 76 | - (NSString *)formattedValue 77 | { 78 | return formatCoordinatorHelper.formattedValue; 79 | } 80 | 81 | - (void)applyFormattingConfig:(FTCTextEntryFormattingConfig *)config 82 | { 83 | [formatCoordinatorHelper applyFormattingConfig:config]; 84 | } 85 | 86 | // MARK: FTCTextEntry 87 | 88 | - (NSString *)text 89 | { 90 | return textField.text; 91 | } 92 | 93 | - (void)setText:(NSString *)text 94 | { 95 | textField.text = text; 96 | } 97 | 98 | - (NSRange)selectedTextRange 99 | { 100 | return [FTCTextInputUtils selectedRangeInTextInput:textField]; 101 | } 102 | 103 | - (void)setSelectedTextRange:(NSRange)selectedTextRange 104 | { 105 | [FTCTextInputUtils selectTextInTextInput:textField atRange:selectedTextRange]; 106 | } 107 | 108 | // MARK: UITextFieldDelegate 109 | 110 | - (void)textFieldDidBeginEditing:(UITextField *)aTextField 111 | { 112 | if ([self.textFieldDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) 113 | { 114 | [self.textFieldDelegate textFieldDidBeginEditing:textField]; 115 | } 116 | 117 | [formatCoordinatorHelper beginEditing]; 118 | } 119 | 120 | - (void)textFieldDidEndEditing:(UITextField *)aTextField 121 | { 122 | if ([self.textFieldDelegate respondsToSelector:@selector(textFieldDidEndEditing:)]) 123 | { 124 | [self.textFieldDelegate textFieldDidEndEditing:textField]; 125 | } 126 | 127 | [formatCoordinatorHelper endEditing]; 128 | } 129 | 130 | - (void)textFieldDidEndEditing:(UITextField *)aTextField reason:(UITextFieldDidEndEditingReason)reason NS_AVAILABLE_IOS(10_0) 131 | { 132 | if ([self.textFieldDelegate respondsToSelector:@selector(textFieldDidEndEditing:reason:)]) 133 | { 134 | return [self.textFieldDelegate textFieldDidEndEditing:textField reason:reason]; 135 | } 136 | else if ([self.textFieldDelegate respondsToSelector:@selector(textFieldDidEndEditing:)]) 137 | { 138 | [self.textFieldDelegate textFieldDidEndEditing:textField]; 139 | } 140 | 141 | [formatCoordinatorHelper endEditing]; 142 | } 143 | 144 | - (BOOL)textField:(UITextField *)aTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 145 | { 146 | BOOL shouldChange = YES; 147 | 148 | if ([self.textFieldDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) 149 | { 150 | shouldChange = [self.textFieldDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string]; 151 | } 152 | 153 | if (shouldChange) 154 | { 155 | [formatCoordinatorHelper changeCharactersInRange:range replacement:string]; 156 | } 157 | 158 | return NO; 159 | } 160 | 161 | - (BOOL)textFieldShouldBeginEditing:(UITextField *)aTextField 162 | { 163 | if ([self.textFieldDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) 164 | { 165 | return [self.textFieldDelegate textFieldShouldBeginEditing:textField]; 166 | } 167 | return YES; 168 | } 169 | 170 | - (BOOL)textFieldShouldEndEditing:(UITextField *)aTextField 171 | { 172 | if ([self.textFieldDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) 173 | { 174 | return [self.textFieldDelegate textFieldShouldEndEditing:textField]; 175 | } 176 | return YES; 177 | } 178 | 179 | - (BOOL)textFieldShouldClear:(UITextField *)aTextField 180 | { 181 | if ([self.textFieldDelegate respondsToSelector:@selector(textFieldShouldClear:)]) 182 | { 183 | return [self.textFieldDelegate textFieldShouldClear:textField]; 184 | } 185 | return YES; 186 | } 187 | 188 | - (BOOL)textFieldShouldReturn:(UITextField *)aTextField 189 | { 190 | if ([self.textFieldDelegate respondsToSelector:@selector(textFieldShouldReturn:)]) 191 | { 192 | return [self.textFieldDelegate textFieldShouldReturn:textField]; 193 | } 194 | return YES; 195 | } 196 | 197 | // MARK: Unavailable init 198 | 199 | - (instancetype)init 200 | { 201 | assert(NO); 202 | return nil; 203 | } 204 | 205 | @end 206 | -------------------------------------------------------------------------------- /Src/FTCTextInputUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import UIKit; 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCTextInputUtils : NSObject 26 | 27 | + (void)selectTextInTextInput:(id)textInput atRange:(NSRange)selectionRange NS_SWIFT_NAME( selectText(in:at:) ); 28 | + (void)selectTextInTextInput:(id)textInput fromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex NS_SWIFT_NAME( selectText(in:from:to:) ); 29 | 30 | + (void)moveCursorInTextInput:(id)textInput toPosition:(NSUInteger)position NS_SWIFT_NAME( moveCursor(in:to:) ); 31 | 32 | + (NSRange)selectedRangeInTextInput:(id)textInput NS_SWIFT_NAME( selectedRange(in:) ); 33 | 34 | - (instancetype)init NS_UNAVAILABLE; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /Src/FTCTextInputUtils.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextInputUtils.h" 22 | 23 | @implementation FTCTextInputUtils 24 | 25 | + (void)selectTextInTextInput:(id)textInput atRange:(NSRange)selectionRange 26 | { 27 | assert( nil != textInput ); 28 | 29 | const NSUInteger selectionStart = selectionRange.location; 30 | const NSUInteger selectionEnd = selectionRange.location + selectionRange.length; 31 | [FTCTextInputUtils selectTextInTextInput:textInput fromIndex:selectionStart toIndex:selectionEnd]; 32 | } 33 | 34 | + (void)selectTextInTextInput:(id)textInput fromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex 35 | { 36 | assert( nil != textInput ); 37 | assert( fromIndex <= toIndex && "'fromIndex' or 'toIndex' is out of bounds." ); 38 | 39 | UITextPosition *fromPosition = [textInput positionFromPosition:[textInput beginningOfDocument] offset:fromIndex]; 40 | UITextPosition *toPosition = [textInput positionFromPosition:[textInput beginningOfDocument] offset:toIndex]; 41 | UITextRange *selectionRange = [textInput textRangeFromPosition:fromPosition toPosition:toPosition]; 42 | [textInput setSelectedTextRange:selectionRange]; 43 | } 44 | 45 | + (void)moveCursorInTextInput:(id)textInput toPosition:(NSUInteger)position 46 | { 47 | assert( nil != textInput ); 48 | 49 | [self selectTextInTextInput:textInput fromIndex:position toIndex:position]; 50 | } 51 | 52 | + (NSRange)selectedRangeInTextInput:(id)textInput 53 | { 54 | UITextPosition *beginning = textInput.beginningOfDocument; 55 | 56 | UITextRange *selectedRange = textInput.selectedTextRange; 57 | UITextPosition *selectionStart = selectedRange.start; 58 | UITextPosition *selectionEnd = selectedRange.end; 59 | 60 | const NSInteger location = [textInput offsetFromPosition:beginning toPosition:selectionStart]; 61 | const NSInteger length = [textInput offsetFromPosition:selectionStart toPosition:selectionEnd]; 62 | 63 | assert( location >= 0 ); 64 | assert( length >= 0 ); 65 | 66 | return NSMakeRange((NSUInteger)location, (NSUInteger)length); 67 | } 68 | 69 | - (instancetype)init 70 | { 71 | assert( false && "Won't happen." ); 72 | return nil; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Src/Money/FTCIntegralMoneyAmountEntryEditingInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryEditingInputFilter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCIntegralMoneyAmountEntryEditingInputFilter : NSObject 26 | 27 | - (instancetype)init; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Src/Money/FTCIntegralMoneyAmountEntryEditingInputFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCIntegralMoneyAmountEntryEditingInputFilter.h" 22 | #import "FTCTextEntryFormattingStringUtils.h" 23 | #import "FTCFilteredString.h" 24 | #import "FTCMoneyEntryFormatUtils.h" 25 | 26 | @implementation FTCIntegralMoneyAmountEntryEditingInputFilter 27 | 28 | - (instancetype)init 29 | { 30 | self = [super init]; 31 | 32 | return self; 33 | } 34 | 35 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString atRange:(NSRange)range withString:(NSString *)replacement 36 | { 37 | assert( (nil != originalString) && "Argument 'originalString' must not be nil." ); 38 | assert( (nil != replacement) && "Argument 'replacement' must not be nil." ); 39 | 40 | NSString *filteredReplacement = [FTCMoneyEntryFormatUtils removeFractionalPartFromString:replacement]; 41 | filteredReplacement = [FTCTextEntryFormattingStringUtils removeCharactersOfSet:[FTCIntegralMoneyAmountEntryEditingInputFilter notAllowedCharacters] fromString:filteredReplacement]; 42 | 43 | NSRange filteredReplacementRange = range; 44 | filteredReplacementRange.length = filteredReplacement.length; 45 | 46 | NSString *replacedString = [originalString stringByReplacingCharactersInRange:range withString:filteredReplacement]; 47 | 48 | return [[FTCFilteredString alloc] initWithString:replacedString range:filteredReplacementRange]; 49 | } 50 | 51 | + (NSCharacterSet *)notAllowedCharacters 52 | { 53 | static NSCharacterSet *notAllowedCharacters; 54 | 55 | static dispatch_once_t predicate; 56 | dispatch_once(&predicate, ^ 57 | { 58 | NSCharacterSet *numericCharSet = [NSCharacterSet decimalDigitCharacterSet]; 59 | 60 | notAllowedCharacters = [numericCharSet invertedSet]; 61 | }); 62 | 63 | return notAllowedCharacters; 64 | } 65 | 66 | - (BOOL)isEqual:(id)object 67 | { 68 | return ( (self == object) || [object isMemberOfClass:self.class] ); 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Src/Money/FTCIntegralMoneyAmountEntryNotEditingInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryNotEditingInputFilter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCIntegralMoneyAmountEntryNotEditingInputFilter : NSObject 26 | 27 | - (instancetype)init; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Src/Money/FTCIntegralMoneyAmountEntryNotEditingInputFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCIntegralMoneyAmountEntryNotEditingInputFilter.h" 22 | #import "FTCMoneyEntryFormatUtils.h" 23 | 24 | @implementation FTCIntegralMoneyAmountEntryNotEditingInputFilter 25 | 26 | - (instancetype)init 27 | { 28 | self = [super init]; 29 | 30 | return self; 31 | } 32 | 33 | - (NSString *)filterString:(NSString *)string 34 | { 35 | assert( nil != string ); 36 | 37 | if( 0 == string.length ) 38 | { 39 | return [FTCMoneyEntryFormatUtils emptyString]; 40 | } 41 | 42 | string = [FTCMoneyEntryFormatUtils removeFractionalPartFromString:string]; 43 | string = [FTCMoneyEntryFormatUtils trimZeroHeadFromString:string]; 44 | string = [FTCMoneyEntryFormatUtils removeNonMoneyEntryCharactersFromString:string]; 45 | 46 | return string; 47 | } 48 | 49 | - (BOOL)isEqual:(id)object 50 | { 51 | return ( (self == object) || [object isMemberOfClass:self.class] ); 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryEditingFormatter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCMoneyEntryEditingFormatter : NSObject 26 | 27 | - (instancetype)init NS_UNAVAILABLE; 28 | 29 | - (instancetype)initWithCurrency:(nullable NSString *)aCurrency; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryEditingFormatter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMoneyEntryEditingFormatter.h" 22 | #import "FTCPostfixFormatter.h" 23 | 24 | @implementation FTCMoneyEntryEditingFormatter 25 | { 26 | FTCPostfixFormatter *postfixFormatter; 27 | NSString *currency; 28 | } 29 | 30 | - (instancetype)init 31 | { 32 | assert(NO && "Won't happen"); 33 | return nil; 34 | } 35 | 36 | - (instancetype)initWithCurrency:(NSString *)aCurrency 37 | { 38 | self = [super init]; 39 | 40 | currency = aCurrency; 41 | 42 | NSString *amountPostfix = @""; 43 | if( nil != aCurrency ) 44 | { 45 | amountPostfix = [NSString stringWithFormat:@" %@", currency]; 46 | } 47 | postfixFormatter = [[FTCPostfixFormatter alloc] initWithPostfix:amountPostfix]; 48 | 49 | return self; 50 | } 51 | 52 | - (NSString *)rawFromFormatted:(NSString *)formattedValue 53 | { 54 | return [postfixFormatter rawFromFormatted:formattedValue]; 55 | } 56 | 57 | - (NSString *)formattedFromRaw:(NSString *)rawValue 58 | { 59 | assert( nil != rawValue ); 60 | return [postfixFormatter formattedFromRaw:rawValue]; 61 | } 62 | 63 | - (NSRange)rangeInFormattedValueForRange:(NSRange)rangeInRawValue inRawValue:(NSString *)rawValue 64 | { 65 | return [postfixFormatter rangeInFormattedValueForRange:rangeInRawValue inRawValue:rawValue]; 66 | } 67 | 68 | - (NSRange)rangeInRawValueForRange:(NSRange)rangeInFormattedValue inFormattedValue:(NSString *)formattedValue 69 | { 70 | return [postfixFormatter rangeInRawValueForRange:rangeInFormattedValue inFormattedValue:formattedValue]; 71 | } 72 | 73 | - (BOOL)isEqual:(id)object 74 | { 75 | if( nil == object ) 76 | { 77 | return NO; 78 | } 79 | 80 | if( self == object ) 81 | { 82 | return YES; 83 | } 84 | 85 | if( NO == [object isMemberOfClass:self.class] ) 86 | { 87 | return NO; 88 | } 89 | 90 | return [self isEqualToFormatter:object]; 91 | } 92 | 93 | - (BOOL)isEqualToFormatter:(FTCMoneyEntryEditingFormatter *)formatter 94 | { 95 | if( nil == currency ) 96 | { 97 | return (nil == formatter->currency); 98 | } 99 | return [currency isEqualToString:formatter->currency]; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryEditingInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryEditingInputFilter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCMoneyEntryEditingInputFilter : NSObject 26 | 27 | - (instancetype)init; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryEditingInputFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMoneyEntryEditingInputFilter.h" 22 | #import "FTCFilteredString.h" 23 | #import "FTCTextEntryFormattingStringUtils.h" 24 | #import "FTCMoneyEntryFormatUtils.h" 25 | 26 | static const int MAX_FRACTIONAL_DIGITS = 2; 27 | 28 | @implementation FTCMoneyEntryEditingInputFilter 29 | 30 | - (instancetype)init 31 | { 32 | self = [super init]; 33 | 34 | return self; 35 | } 36 | 37 | - (FTCFilteredString *)replaceSubstringInString:(NSString *)originalString atRange:(NSRange)range withString:(NSString *)replacement 38 | { 39 | assert( nil != originalString ); 40 | assert( nil != replacement ); 41 | 42 | NSString *filteredReplacement = [FTCMoneyEntryFormatUtils removeNonMoneyEntryCharactersFromString:replacement]; 43 | 44 | const NSUInteger originalStringSeparatorLocation = [originalString rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]].location; 45 | 46 | if(NSNotFound != originalStringSeparatorLocation) 47 | { 48 | filteredReplacement = [FTCTextEntryFormattingStringUtils removeCharactersOfSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet] fromString:filteredReplacement]; 49 | } 50 | else 51 | { 52 | filteredReplacement = [self filterExtraSeparatorsFromString:filteredReplacement]; 53 | } 54 | 55 | NSRange filteredReplacementRange = range; 56 | filteredReplacementRange.length = filteredReplacement.length; 57 | 58 | NSString *replacedString = [originalString stringByReplacingCharactersInRange:range withString:filteredReplacement]; 59 | 60 | if( [self shouldUseReplacementResult:replacedString] ) 61 | { 62 | return [[FTCFilteredString alloc] initWithString:replacedString range:filteredReplacementRange]; 63 | } 64 | 65 | return [[FTCFilteredString alloc] initWithString:originalString range:NSMakeRange(filteredReplacementRange.location, 0)]; 66 | } 67 | 68 | - (BOOL)shouldUseReplacementResult:(nonnull NSString *)replacementResult 69 | { 70 | const NSUInteger separatorLocation = [replacementResult rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]].location; 71 | 72 | if( NSNotFound != separatorLocation ) 73 | { 74 | NSString *fractionalPart = [FTCMoneyEntryFormatUtils removeIntegralPartFromString:replacementResult]; 75 | if ( fractionalPart.length > MAX_FRACTIONAL_DIGITS ) 76 | { 77 | return NO; 78 | } 79 | } 80 | 81 | return YES; 82 | } 83 | 84 | - (NSString *)filterExtraSeparatorsFromString:(NSString * const)string 85 | { 86 | NSString *result = string; 87 | 88 | const NSUInteger separatorLocation = [result rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]].location; 89 | 90 | if(NSNotFound != separatorLocation) 91 | { 92 | NSString *singleSeparator = [result substringWithRange:NSMakeRange(separatorLocation, 1)]; 93 | 94 | result = [FTCTextEntryFormattingStringUtils removeCharactersOfSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet] fromString:result]; 95 | result = [result stringByReplacingCharactersInRange:NSMakeRange(separatorLocation, 0) withString:singleSeparator]; 96 | } 97 | 98 | return result; 99 | } 100 | 101 | - (BOOL)isEqual:(id)object 102 | { 103 | return ( (self == object) || [object isMemberOfClass:self.class] ); 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryFormatUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | @interface FTCMoneyEntryFormatUtils : NSObject 24 | 25 | + (NSString *)trimZeroHeadFromString:(NSString *const)string; 26 | + (NSString *)removeNonMoneyEntryCharactersFromString:(NSString *const)string; 27 | + (NSString *)removeFractionalPartFromString:(NSString *)string; 28 | + (NSString *)removeIntegralPartFromString:(NSString *)string; 29 | 30 | + (NSString *)emptyString; 31 | + (NSString *)zeroString; 32 | 33 | + (NSCharacterSet *)decimalSeparatorsCharacterSet; 34 | + (NSString *)preferredDecimalSeparator; 35 | 36 | - (instancetype)init NS_UNAVAILABLE; 37 | 38 | @end 39 | 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryFormatUtils.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMoneyEntryFormatUtils.h" 22 | #import "FTCTextEntryFormattingStringUtils.h" 23 | 24 | static NSString * const EMPTY_STRING = @""; 25 | static NSString * const ZERO_STRING = @"0"; 26 | static NSString * const DECIMAL_SEPARATORS = @".,"; 27 | 28 | @implementation FTCMoneyEntryFormatUtils 29 | 30 | + (NSString *)trimZeroHeadFromString:(NSString * const)string 31 | { 32 | const NSUInteger zeroHeadLength = [FTCMoneyEntryFormatUtils zeroHeadRangeForString:string].length; 33 | 34 | return [string substringFromIndex:zeroHeadLength]; 35 | } 36 | 37 | + (NSRange)zeroHeadRangeForString:(nonnull NSString * const)string 38 | { 39 | NSRange zeroHeadRange = NSMakeRange(NSNotFound, 0); 40 | 41 | if( 0 == string.length ) 42 | { 43 | return zeroHeadRange; 44 | } 45 | 46 | NSString *substring = nil; 47 | for(NSUInteger i = 0; i < string.length; i++) 48 | { 49 | substring = [string substringWithRange:NSMakeRange(i, 1)]; 50 | if( [ZERO_STRING isEqualToString:substring] ) 51 | { 52 | zeroHeadRange.length++; 53 | zeroHeadRange.location = 0; 54 | } 55 | else 56 | { 57 | break; 58 | } 59 | } 60 | 61 | // Обрабатываем случаи @"00", @"00.1", чтобы после триминга иметь @"0", @"0.1" соответственно, иначе 62 | // если этого не делать, то после триминга будем иметь @"", @".1" соответственно. 63 | if( zeroHeadRange.length > 0 ) 64 | { 65 | BOOL reachedDecimalSeparator = (substring != nil && 0 != [self.decimalSeparators rangeOfString:substring].length); 66 | BOOL reachedEndOfString = (string.length == zeroHeadRange.length); 67 | 68 | if( reachedDecimalSeparator || reachedEndOfString ) 69 | { 70 | zeroHeadRange.length--; 71 | } 72 | } 73 | 74 | return zeroHeadRange; 75 | } 76 | 77 | + (NSString *)removeNonMoneyEntryCharactersFromString:(NSString * const)string 78 | { 79 | return [FTCTextEntryFormattingStringUtils removeCharactersOfSet:[FTCMoneyEntryFormatUtils nonMoneyEntryCharacters] fromString:string]; 80 | } 81 | 82 | + (nonnull NSCharacterSet *)nonMoneyEntryCharacters 83 | { 84 | static NSCharacterSet *notAllowedCharacters; 85 | 86 | static dispatch_once_t predicate; 87 | dispatch_once(&predicate, ^ 88 | { 89 | NSMutableCharacterSet *numericCharSet = [NSMutableCharacterSet decimalDigitCharacterSet]; 90 | [numericCharSet addCharactersInString:DECIMAL_SEPARATORS]; 91 | 92 | notAllowedCharacters = [numericCharSet invertedSet]; 93 | }); 94 | 95 | return notAllowedCharacters; 96 | } 97 | 98 | + (NSString *)removeFractionalPartFromString:(NSString *)string 99 | { 100 | const NSUInteger separatorLocation = [string rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]].location; 101 | 102 | if(NSNotFound != separatorLocation) 103 | { 104 | string = [string substringToIndex:separatorLocation]; 105 | } 106 | 107 | return string; 108 | } 109 | 110 | + (NSString *)removeIntegralPartFromString:(NSString *)string 111 | { 112 | const NSUInteger separatorLocation = [string rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]].location; 113 | 114 | if(NSNotFound != separatorLocation) 115 | { 116 | string = [string substringFromIndex:separatorLocation + 1]; 117 | } 118 | 119 | return string; 120 | } 121 | 122 | + (NSString *)emptyString 123 | { 124 | return EMPTY_STRING; 125 | } 126 | 127 | + (NSString *)zeroString 128 | { 129 | return ZERO_STRING; 130 | } 131 | 132 | + (NSString *)decimalSeparators 133 | { 134 | return DECIMAL_SEPARATORS; 135 | } 136 | 137 | + (NSCharacterSet *)decimalSeparatorsCharacterSet 138 | { 139 | static NSCharacterSet *separatingCharSet; 140 | 141 | static dispatch_once_t predicate; 142 | dispatch_once(&predicate, ^ 143 | { 144 | separatingCharSet = [NSCharacterSet characterSetWithCharactersInString:[FTCMoneyEntryFormatUtils decimalSeparators]]; 145 | }); 146 | 147 | return separatingCharSet; 148 | } 149 | 150 | + (NSString *)preferredDecimalSeparator 151 | { 152 | static NSString *decimalSeparator; 153 | 154 | static dispatch_once_t predicate; 155 | dispatch_once(&predicate, ^ 156 | { 157 | NSLocale *russianLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"ru_RU"]; 158 | decimalSeparator = [russianLocale objectForKey:NSLocaleDecimalSeparator]; 159 | }); 160 | 161 | return decimalSeparator; 162 | } 163 | 164 | - (instancetype)init 165 | { 166 | assert( NO ); 167 | return nil; 168 | } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryNotEditingFormatter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormatter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCMoneyEntryNotEditingFormatter : NSObject 26 | 27 | - (instancetype)init NS_UNAVAILABLE; 28 | 29 | - (instancetype)initWithCurrency:(nullable NSString *)currency; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryNotEditingFormatter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMoneyEntryNotEditingFormatter.h" 22 | #import "FTCPostfixFormatter.h" 23 | 24 | @implementation FTCMoneyEntryNotEditingFormatter 25 | { 26 | FTCPostfixFormatter *postfixFormatter; 27 | 28 | NSString *currency; 29 | } 30 | 31 | - (instancetype)init 32 | { 33 | assert(NO && "Won't happen"); 34 | return nil; 35 | } 36 | 37 | - (instancetype)initWithCurrency:(NSString *)aCurrency 38 | { 39 | self = [super init]; 40 | 41 | currency = aCurrency; 42 | 43 | NSString *amountPostfix = @""; 44 | if( nil != currency ) 45 | { 46 | amountPostfix = [NSString stringWithFormat:@" %@", currency]; 47 | } 48 | postfixFormatter = [[FTCPostfixFormatter alloc] initWithPostfix:amountPostfix]; 49 | 50 | return self; 51 | } 52 | 53 | - (NSString *)rawFromFormatted:(NSString *)formattedValue 54 | { 55 | if( NO == [self canShowPostfixForString:formattedValue] ) 56 | { 57 | return formattedValue; 58 | } 59 | 60 | return [postfixFormatter rawFromFormatted:formattedValue]; 61 | } 62 | 63 | - (NSString *)formattedFromRaw:(NSString *)rawValue 64 | { 65 | if( NO == [self canShowPostfixForString:rawValue] ) 66 | { 67 | return (rawValue != nil ? rawValue : @""); 68 | } 69 | 70 | return [postfixFormatter formattedFromRaw:rawValue]; 71 | } 72 | 73 | - (NSRange)rangeInFormattedValueForRange:(NSRange)rangeInRawValue inRawValue:(NSString *)rawValue 74 | { 75 | return [postfixFormatter rangeInFormattedValueForRange:rangeInRawValue inRawValue:rawValue]; 76 | } 77 | 78 | - (NSRange)rangeInRawValueForRange:(NSRange)rangeInFormattedValue inFormattedValue:(NSString *)formattedValue 79 | { 80 | if( [self canShowPostfixForString:formattedValue] ) 81 | { 82 | return [postfixFormatter rangeInRawValueForRange:rangeInFormattedValue inFormattedValue:formattedValue]; 83 | } 84 | 85 | return rangeInFormattedValue; 86 | } 87 | 88 | - (BOOL)canShowPostfixForString:(nullable NSString *)string 89 | { 90 | if(0 == string.length) 91 | { 92 | return NO; 93 | } 94 | 95 | return YES; 96 | } 97 | 98 | - (BOOL)isEqual:(id)object 99 | { 100 | if( nil == object ) 101 | { 102 | return NO; 103 | } 104 | 105 | if( self == object ) 106 | { 107 | return YES; 108 | } 109 | 110 | if( NO == [object isMemberOfClass:self.class] ) 111 | { 112 | return NO; 113 | } 114 | 115 | return [self isEqualToFormatter:object]; 116 | } 117 | 118 | - (BOOL)isEqualToFormatter:(FTCMoneyEntryNotEditingFormatter *)object 119 | { 120 | if( nil == currency ) 121 | { 122 | return (nil == object->currency); 123 | } 124 | return [currency isEqualToString:object->currency]; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryNotEditingInputFilter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryNotEditingInputFilter.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface FTCMoneyEntryNotEditingInputFilter : NSObject 26 | 27 | - (instancetype)init; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Src/Money/FTCMoneyEntryNotEditingInputFilter.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCMoneyEntryNotEditingInputFilter.h" 22 | #import "FTCMoneyEntryFormatUtils.h" 23 | 24 | static const NSInteger PREFERRED_NUMBER_OF_FRACTIONAL_DIGITS = 2; 25 | 26 | @implementation FTCMoneyEntryNotEditingInputFilter 27 | 28 | - (instancetype)init 29 | { 30 | self = [super init]; 31 | 32 | return self; 33 | } 34 | 35 | - (NSString *)filterString:(NSString *)string 36 | { 37 | assert( nil != string ); 38 | 39 | if( 0 == string.length ) 40 | { 41 | return [FTCMoneyEntryFormatUtils emptyString]; 42 | } 43 | 44 | NSString *result = [FTCMoneyEntryFormatUtils removeNonMoneyEntryCharactersFromString:string]; 45 | result = [FTCMoneyEntryFormatUtils trimZeroHeadFromString:result]; 46 | result = [self trimZeroTailFromString:result]; 47 | result = [self trimEndingSeparatorInString:result]; 48 | 49 | if( 0 == result.length ) 50 | { 51 | result = [FTCMoneyEntryFormatUtils zeroString]; 52 | } 53 | 54 | result = [self fixNotExistingIntegerPartInString:result]; 55 | result = [self appendMissingZeroInFractionalPartToString:result]; 56 | 57 | return result; 58 | } 59 | 60 | - (NSString *)trimZeroTailFromString:(NSString * const)string 61 | { 62 | NSString *result = string; 63 | 64 | const NSUInteger separatorLocation = [string rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]].location; 65 | 66 | if(NSNotFound != separatorLocation) 67 | { 68 | const NSUInteger zeroTailLocation = [self zeroTailRangeForString:result].location; 69 | 70 | if(NSNotFound != zeroTailLocation) 71 | { 72 | result = [result substringToIndex:zeroTailLocation]; 73 | } 74 | } 75 | 76 | return result; 77 | } 78 | 79 | - (NSString *)trimEndingSeparatorInString:(NSString * const)string 80 | { 81 | NSString *result = string; 82 | 83 | if(0 < result.length) 84 | { 85 | const NSRange trimmedStringSeparatorRange = [result rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]]; 86 | 87 | if(NSNotFound != trimmedStringSeparatorRange.location) 88 | { 89 | if (result.length == trimmedStringSeparatorRange.location + 1) 90 | { 91 | result = [result substringToIndex:trimmedStringSeparatorRange.location]; 92 | } 93 | } 94 | } 95 | 96 | return result; 97 | } 98 | 99 | - (NSString *)fixNotExistingIntegerPartInString:(NSString * const)string 100 | { 101 | NSString *result = string; 102 | 103 | if(0 < result.length) 104 | { 105 | const NSRange trimmedStringSeparatorRange = [result rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]]; 106 | 107 | if(0 == trimmedStringSeparatorRange.location) 108 | { 109 | result = [[FTCMoneyEntryFormatUtils zeroString] stringByAppendingString:result]; 110 | } 111 | } 112 | 113 | return result; 114 | } 115 | 116 | - (NSString *)appendMissingZeroInFractionalPartToString:(NSString * const)string 117 | { 118 | NSString *result = string; 119 | 120 | NSRange separatorRange = [string rangeOfCharacterFromSet:[FTCMoneyEntryFormatUtils decimalSeparatorsCharacterSet]]; 121 | 122 | if( NSNotFound != separatorRange.location ) 123 | { 124 | const NSInteger numberOfFractionalDigits = string.length - (separatorRange.location + separatorRange.length); 125 | const NSInteger fractionalDigitsToAdd = PREFERRED_NUMBER_OF_FRACTIONAL_DIGITS - numberOfFractionalDigits; 126 | 127 | if( fractionalDigitsToAdd > 0 ) 128 | { 129 | for( NSInteger index = 0; index < fractionalDigitsToAdd; index++ ) 130 | { 131 | result = [result stringByAppendingString:[FTCMoneyEntryFormatUtils zeroString]]; 132 | } 133 | } 134 | } 135 | else 136 | { 137 | NSString *separator = [FTCMoneyEntryFormatUtils preferredDecimalSeparator]; 138 | NSString *zeroString = [FTCMoneyEntryFormatUtils zeroString]; 139 | NSString *zeroTail = [zeroString stringByAppendingString:zeroString]; 140 | NSString *tail = [separator stringByAppendingString:zeroTail]; 141 | result = [result stringByAppendingString:tail]; 142 | } 143 | 144 | return result; 145 | } 146 | 147 | - (NSRange)zeroTailRangeForString:(NSString * const)string 148 | { 149 | NSRange zeroHeadRange = NSMakeRange(NSNotFound, 0); 150 | 151 | if( 0 == string.length ) 152 | { 153 | return zeroHeadRange; 154 | } 155 | 156 | for(NSInteger i = string.length - 1; i >= 0; i--) 157 | { 158 | NSString *substring = [string substringWithRange:NSMakeRange((NSUInteger)i, 1)]; 159 | 160 | if( [[FTCMoneyEntryFormatUtils zeroString] isEqualToString:substring] ) 161 | { 162 | zeroHeadRange.length++; 163 | zeroHeadRange.location = (NSUInteger)i; 164 | } 165 | else 166 | { 167 | break; 168 | } 169 | } 170 | 171 | return zeroHeadRange; 172 | } 173 | 174 | - (BOOL)isEqual:(id)object 175 | { 176 | return ( (self == object) || [object isMemberOfClass:self.class] ); 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /Src/Tools/FTCTextEntryFormattingStringUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | @interface FTCTextEntryFormattingStringUtils : NSObject 24 | 25 | + (NSString *)removeCharactersOfSet:(NSCharacterSet *)characterSet fromString:(NSString *)string; 26 | + (nullable NSString *)stringWithDecimalDigitsFromString:(nullable NSString *)string; 27 | 28 | + (NSString *)stringWithCharacter:(unichar)character; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /Src/Tools/FTCTextEntryFormattingStringUtils.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "FTCTextEntryFormattingStringUtils.h" 22 | 23 | @implementation FTCTextEntryFormattingStringUtils 24 | 25 | + (NSString *)joinStringsFromArray:(NSArray *)arrayToJoinStringsFrom 26 | { 27 | #if defined(DEBUG) 28 | for( id obj in arrayToJoinStringsFrom ) 29 | { 30 | assert( [obj isKindOfClass:[NSString class]] && "'obj' is of unexpected class." ); 31 | } 32 | #endif // defined(DEBUG) 33 | 34 | return [arrayToJoinStringsFrom componentsJoinedByString:@""]; 35 | } 36 | 37 | + (NSString *)removeCharactersOfSet:(NSCharacterSet *)characterSet fromString:(NSString *)string 38 | { 39 | return [self joinStringsFromArray:[string componentsSeparatedByCharactersInSet:characterSet]]; 40 | } 41 | 42 | + (NSString *)stringWithDecimalDigitsFromString:(NSString *)string 43 | { 44 | if (nil == string || 0 == [string length]) 45 | { 46 | return string; 47 | } 48 | 49 | NSCharacterSet *bannedCharacters = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; 50 | NSString *normalString = [self removeCharactersOfSet:bannedCharacters fromString:string]; 51 | return normalString; 52 | } 53 | 54 | + (NSString *)stringWithCharacter:(unichar)character 55 | { 56 | return [NSString stringWithCharacters:&character length:1]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Src/Tools/NSString+FTCTextEntryFormatting.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @interface NSString (FTCTextEntryFormatting) 22 | 23 | - (BOOL)textEntryFormatting_isEmpty; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Src/Tools/NSString+FTCTextEntryFormatting.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "NSString+FTCTextEntryFormatting.h" 22 | 23 | @implementation NSString (FTCTextEntryFormatting) 24 | 25 | - (BOOL)textEntryFormatting_isEmpty 26 | { 27 | return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /UnitTests/FormatCoordinator/FormatCoordinatorTestCase.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import XCTest; 22 | @import FTCTextEntryFormatting; 23 | 24 | @interface FormattingCoordinatorTestCase : XCTestCase 25 | @end 26 | 27 | @implementation FormattingCoordinatorTestCase 28 | { 29 | FTCTextEntryFormatCoordinator *coordinator; 30 | } 31 | 32 | - (void)setUp 33 | { 34 | [super setUp]; 35 | 36 | coordinator = [[FTCTextEntryFormatCoordinator alloc] init]; 37 | } 38 | 39 | - (void)tearDown 40 | { 41 | [super tearDown]; 42 | } 43 | 44 | - (void)test_set_nil_rawValue 45 | { 46 | coordinator.rawValue = nil; 47 | 48 | XCTAssert( nil == coordinator.rawValue ); 49 | } 50 | 51 | - (void)test_set_empty_rawValue 52 | { 53 | [self checkRawValueAfterSetRawValue:@""]; 54 | } 55 | 56 | - (void)test_set_not_empty_rawValue 57 | { 58 | [self checkRawValueAfterSetRawValue:@"value"]; 59 | } 60 | 61 | - (void)test_formattedValue_for_nil_rawValue 62 | { 63 | [self checkFormattedValue:@"" afterSetRawValue:nil]; 64 | } 65 | 66 | - (void)test_empty_formattedValue 67 | { 68 | [self checkFormattedValue:@"" afterSetRawValue:@""]; 69 | } 70 | 71 | - (void)test_not_empty_formattedValue 72 | { 73 | [self checkFormattedValue:@"value" afterSetRawValue:@"value"]; 74 | } 75 | 76 | - (void)test_append_data_to_nil_rawValue 77 | { 78 | NSString *replacement = @"value"; 79 | NSString *etalonRawValue = @"value"; 80 | 81 | coordinator.rawValue = nil; 82 | 83 | [coordinator beginEditing]; 84 | 85 | [coordinator userReplacedInFormattedValueSubstringAtRange:NSMakeRange(0, 0) withString:replacement]; 86 | 87 | [coordinator endEditing]; 88 | 89 | XCTAssertEqualObjects(etalonRawValue, coordinator.rawValue); 90 | } 91 | 92 | - (void)test_insert_to_rawValue 93 | { 94 | [self checkRawValue:@"1value2" 95 | replaceInRawValue:@"12" 96 | atRange:NSMakeRange(1, 0) 97 | withReplacement:@"value"]; 98 | } 99 | 100 | - (void)test_replace_in_rawValue 101 | { 102 | [self checkRawValue:@"1aaaa2" 103 | replaceInRawValue:@"1value2" 104 | atRange:NSMakeRange(1, 5) 105 | withReplacement:@"aaaa"]; 106 | } 107 | 108 | - (void)test_remove_in_rawValue 109 | { 110 | [self checkRawValue:@"12" 111 | replaceInRawValue:@"1value2" 112 | atRange:NSMakeRange(1, 5) 113 | withReplacement:@""]; 114 | } 115 | 116 | - (void)test_insert_to_formattedValue 117 | { 118 | [self checkFormattedValue:@"1value2" 119 | replaceInRawValue:@"12" 120 | atRange:NSMakeRange(1, 0) 121 | withReplacement:@"value"]; 122 | } 123 | 124 | - (void)test_replace_in_formattedValue 125 | { 126 | [self checkFormattedValue:@"1aaaa2" 127 | replaceInRawValue:@"1value2" 128 | atRange:NSMakeRange(1, 5) 129 | withReplacement:@"aaaa"]; 130 | } 131 | 132 | - (void)test_remove_in_formattedValue 133 | { 134 | [self checkFormattedValue:@"12" 135 | replaceInRawValue:@"1value2" 136 | atRange:NSMakeRange(1, 5) 137 | withReplacement:@""]; 138 | } 139 | 140 | - (void)test_selection 141 | { 142 | NSString *rawValue = @"1value2"; 143 | NSRange range = NSMakeRange(1, 5); 144 | NSString *replacement = @"aaaa"; 145 | NSRange etalonSelection = NSMakeRange(5, 0); 146 | 147 | [self doReplaceInRawValue:rawValue atRange:range withReplacement:replacement]; 148 | 149 | XCTAssert( NSEqualRanges(etalonSelection, coordinator.currentSelectionRangeInFormattedValue) ); 150 | } 151 | 152 | // MARK: Helpers 153 | 154 | - (void)checkRawValueAfterSetRawValue:(NSString *)rawValue 155 | { 156 | coordinator.rawValue = rawValue; 157 | 158 | XCTAssertEqualObjects(rawValue, coordinator.rawValue); 159 | } 160 | 161 | - (void)checkFormattedValue:(NSString *)etalonFormattedValue afterSetRawValue:(NSString *)rawValue 162 | { 163 | coordinator.rawValue = rawValue; 164 | 165 | XCTAssertEqualObjects(etalonFormattedValue, coordinator.formattedValue); 166 | } 167 | 168 | - (void)checkRawValue:(NSString *)etalonRawValue 169 | replaceInRawValue:(NSString *)rawValue 170 | atRange:(NSRange)range 171 | withReplacement:(NSString *)replacement 172 | { 173 | [self doReplaceInRawValue:rawValue atRange:range withReplacement:replacement]; 174 | 175 | XCTAssertEqualObjects(etalonRawValue, coordinator.rawValue, 176 | @"\n resultRawValue: '%@'\n etalonRawValue: '%@'\n rawValue: '%@'\n range: '%@'\n replacement: '%@'", 177 | coordinator.rawValue, etalonRawValue, rawValue, NSStringFromRange(range), replacement); 178 | } 179 | 180 | - (void)checkFormattedValue:(NSString *)etalonFormattedValue 181 | replaceInRawValue:(NSString *)rawValue 182 | atRange:(NSRange)range 183 | withReplacement:(NSString *)replacement 184 | { 185 | [self doReplaceInRawValue:rawValue atRange:range withReplacement:replacement]; 186 | 187 | XCTAssertEqualObjects(etalonFormattedValue, coordinator.formattedValue, 188 | @"\n formattedValue: '%@'\n etalonFormattedValue: '%@'\n rawValue: '%@'\n range: '%@'\n replacement: '%@'", 189 | coordinator.formattedValue, etalonFormattedValue, rawValue, NSStringFromRange(range), replacement); 190 | } 191 | 192 | - (void)doReplaceInRawValue:(NSString *)rawValue atRange:(NSRange)range withReplacement:(NSString *)replacement 193 | { 194 | coordinator.rawValue = rawValue; 195 | 196 | [coordinator beginEditing]; 197 | 198 | [coordinator userReplacedInFormattedValueSubstringAtRange:range withString:replacement]; 199 | 200 | [coordinator endEditing]; 201 | } 202 | 203 | @end 204 | -------------------------------------------------------------------------------- /UnitTests/Formatters/FTCMaskFormatter/FTCMaskFormatterTestCase.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import XCTest; 22 | @import FTCTextEntryFormatting; 23 | 24 | @interface FTCMaskFormatterTestCase : XCTestCase 25 | { 26 | FTCMaskFormatter *formatterWithCutTail; 27 | FTCMaskFormatter *formatterWithoutCutTail; 28 | } 29 | 30 | @end 31 | 32 | @implementation FTCMaskFormatterTestCase 33 | 34 | - (void)setUp 35 | { 36 | [super setUp]; 37 | 38 | FTCMaskFormatterGenericConfig *configWithCutTail = [[FTCMaskFormatterGenericConfig alloc] initWithMask:@"_ _ _ _ _" maskCharacter:@"_"]; 39 | configWithCutTail.tailMode = FTCMaskFormatterConfigTailModeCut; 40 | 41 | formatterWithCutTail = [[FTCMaskFormatter alloc] initWithConfig:configWithCutTail]; 42 | 43 | FTCMaskFormatterGenericConfig *configWithoutCutTail = [[FTCMaskFormatterGenericConfig alloc] initWithMask:@"_ _ _ _ _" maskCharacter:@"_"]; 44 | configWithoutCutTail.tailMode = FTCMaskFormatterConfigTailModeNone; 45 | 46 | formatterWithoutCutTail = [[FTCMaskFormatter alloc] initWithConfig:configWithoutCutTail]; 47 | } 48 | 49 | - (void)test_raw_from_formatted 50 | { 51 | XCTAssertEqualObjects([formatterWithCutTail rawFromFormatted:@""], @""); 52 | XCTAssertEqualObjects([formatterWithCutTail rawFromFormatted:@"1 2 3"], @"123"); 53 | 54 | XCTAssertEqualObjects([formatterWithoutCutTail rawFromFormatted:@""], @""); 55 | XCTAssertEqualObjects([formatterWithoutCutTail rawFromFormatted:@"1 2 3"], @"123"); 56 | } 57 | 58 | - (void)test_formatted_from_raw 59 | { 60 | XCTAssertEqualObjects([formatterWithCutTail formattedFromRaw:@""], @""); 61 | XCTAssertEqualObjects([formatterWithCutTail formattedFromRaw:@"123"], @"1 2 3"); 62 | 63 | XCTAssertEqualObjects([formatterWithoutCutTail formattedFromRaw:@""], @"_ _ _ _ _"); 64 | XCTAssertEqualObjects([formatterWithoutCutTail formattedFromRaw:@"123"], @"1 2 3 _ _"); 65 | } 66 | 67 | - (void)test_rangeInFormattedValueForRange_inRawValue 68 | { 69 | NSRange rangeInFormatted; 70 | 71 | 72 | rangeInFormatted = [formatterWithCutTail rangeInFormattedValueForRange:NSMakeRange(0, 3) inRawValue:@"123"]; 73 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(0, 6))); 74 | 75 | rangeInFormatted = [formatterWithCutTail rangeInFormattedValueForRange:NSMakeRange(1, 2) inRawValue:@"123"]; 76 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(1, 4))); 77 | 78 | 79 | rangeInFormatted = [formatterWithoutCutTail rangeInFormattedValueForRange:NSMakeRange(0, 3) inRawValue:@"123"]; 80 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(0, 6))); 81 | 82 | rangeInFormatted = [formatterWithoutCutTail rangeInFormattedValueForRange:NSMakeRange(1, 2) inRawValue:@"123"]; 83 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(1, 4))); 84 | } 85 | 86 | - (void)test_rangeInRawValueForRange_inFormattedValue 87 | { 88 | NSRange rangeInRaw; 89 | 90 | 91 | rangeInRaw = [formatterWithCutTail rangeInRawValueForRange:NSMakeRange(0, 3) inFormattedValue:@"1 2 3"]; 92 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(0, 2))); 93 | 94 | rangeInRaw = [formatterWithCutTail rangeInRawValueForRange:NSMakeRange(1, 2) inFormattedValue:@"1 2 3"]; 95 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(1, 1))); 96 | 97 | 98 | rangeInRaw = [formatterWithoutCutTail rangeInRawValueForRange:NSMakeRange(0, 3) inFormattedValue:@"1 2 3 _ _"]; 99 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(0, 2))); 100 | 101 | rangeInRaw = [formatterWithoutCutTail rangeInRawValueForRange:NSMakeRange(1, 2) inFormattedValue:@"1 2 3 _ _"]; 102 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(1, 1))); 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /UnitTests/Formatters/FTCPostfixFormatter/FTCPostfixFormatterTestCaseWithEmptyPostfix.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import XCTest; 22 | @import FTCTextEntryFormatting; 23 | 24 | @interface FTCPostfixFormatterTestCaseWithEmptyPostfix : XCTestCase 25 | { 26 | FTCPostfixFormatter *formatter; 27 | } 28 | 29 | @end 30 | 31 | @implementation FTCPostfixFormatterTestCaseWithEmptyPostfix 32 | 33 | - (void)setUp 34 | { 35 | [super setUp]; 36 | 37 | formatter = [[FTCPostfixFormatter alloc] initWithPostfix:@""]; 38 | } 39 | 40 | - (void)test_raw_from_formatted 41 | { 42 | XCTAssertEqualObjects([formatter rawFromFormatted:@""], @""); 43 | XCTAssertEqualObjects([formatter rawFromFormatted:@"123"], @"123"); 44 | } 45 | 46 | - (void)test_formatted_from_raw 47 | { 48 | XCTAssertEqualObjects([formatter formattedFromRaw:@""], @""); 49 | XCTAssertEqualObjects([formatter formattedFromRaw:@"123"], @"123"); 50 | } 51 | 52 | - (void)test_rangeInFormattedValueForRange_inRawValue 53 | { 54 | NSRange rangeInFormatted; 55 | 56 | rangeInFormatted = [formatter rangeInFormattedValueForRange:NSMakeRange(0, 3) inRawValue:@"123"]; 57 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(0, 3))); 58 | 59 | rangeInFormatted = [formatter rangeInFormattedValueForRange:NSMakeRange(1, 2) inRawValue:@"123"]; 60 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(1, 2))); 61 | } 62 | 63 | - (void)test_rangeInRawValueForRange_inFormattedValue 64 | { 65 | NSRange rangeInRaw; 66 | 67 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(0, 3) inFormattedValue:@"123"]; 68 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(0, 3))); 69 | 70 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(1, 2) inFormattedValue:@"123"]; 71 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(1, 2))); 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /UnitTests/Formatters/FTCPostfixFormatter/FTCPostfixFormatterTestCaseWithNilPostfix.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import XCTest; 22 | @import FTCTextEntryFormatting; 23 | 24 | @interface FTCPostfixFormatterTestCaseWithNilPostfix : XCTestCase 25 | { 26 | FTCPostfixFormatter *formatter; 27 | } 28 | 29 | @end 30 | 31 | @implementation FTCPostfixFormatterTestCaseWithNilPostfix 32 | 33 | - (void)setUp 34 | { 35 | [super setUp]; 36 | 37 | formatter = [[FTCPostfixFormatter alloc] initWithPostfix:@""]; 38 | } 39 | 40 | - (void)test_raw_from_formatted 41 | { 42 | XCTAssertEqualObjects([formatter rawFromFormatted:@""], @""); 43 | XCTAssertEqualObjects([formatter rawFromFormatted:@"123"], @"123"); 44 | } 45 | 46 | - (void)test_formatted_from_raw 47 | { 48 | XCTAssertEqualObjects([formatter formattedFromRaw:@""], @""); 49 | XCTAssertEqualObjects([formatter formattedFromRaw:@"123"], @"123"); 50 | } 51 | 52 | - (void)test_rangeInFormattedValueForRange_inRawValue 53 | { 54 | NSRange rangeInFormatted; 55 | 56 | rangeInFormatted = [formatter rangeInFormattedValueForRange:NSMakeRange(0, 3) inRawValue:@"123"]; 57 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(0, 3))); 58 | 59 | rangeInFormatted = [formatter rangeInFormattedValueForRange:NSMakeRange(1, 2) inRawValue:@"123"]; 60 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(1, 2))); 61 | } 62 | 63 | - (void)test_rangeInRawValueForRange_inFormattedValue 64 | { 65 | NSRange rangeInRaw; 66 | 67 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(0, 3) inFormattedValue:@"123"]; 68 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(0, 3))); 69 | 70 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(1, 2) inFormattedValue:@"123"]; 71 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(1, 2))); 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /UnitTests/Formatters/FTCPostfixFormatter/FTCPostfixFormatterTestCaseWithNotEmptyPostfix.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import XCTest; 22 | @import FTCTextEntryFormatting; 23 | 24 | @interface FTCPostfixFormatterTestCaseWithNotEmptyPostfix : XCTestCase 25 | { 26 | FTCPostfixFormatter *formatter; 27 | } 28 | 29 | @end 30 | 31 | @implementation FTCPostfixFormatterTestCaseWithNotEmptyPostfix 32 | 33 | - (void)setUp 34 | { 35 | [super setUp]; 36 | 37 | formatter = [[FTCPostfixFormatter alloc] initWithPostfix:@" rub"]; 38 | } 39 | 40 | - (void)test_raw_from_formatted 41 | { 42 | XCTAssertEqualObjects([formatter rawFromFormatted:@" rub"], @""); 43 | XCTAssertEqualObjects([formatter rawFromFormatted:@"123 rub"], @"123"); 44 | } 45 | 46 | - (void)test_formatted_from_raw 47 | { 48 | XCTAssertEqualObjects([formatter formattedFromRaw:@""], @" rub"); 49 | XCTAssertEqualObjects([formatter formattedFromRaw:@"123"], @"123 rub"); 50 | } 51 | 52 | - (void)test_rangeInFormattedValueForRange_inRawValue 53 | { 54 | NSRange rangeInFormatted; 55 | 56 | rangeInFormatted = [formatter rangeInFormattedValueForRange:NSMakeRange(0, 3) inRawValue:@"123"]; 57 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(0, 3))); 58 | 59 | rangeInFormatted = [formatter rangeInFormattedValueForRange:NSMakeRange(1, 2) inRawValue:@"123"]; 60 | XCTAssertTrue(NSEqualRanges(rangeInFormatted, NSMakeRange(1, 2))); 61 | } 62 | 63 | - (void)test_rangeInRawValueForRange_inFormattedValue 64 | { 65 | NSRange rangeInRaw; 66 | 67 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(0, 3) inFormattedValue:@"123 rub"]; 68 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(0, 3))); 69 | 70 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(1, 2) inFormattedValue:@"123 rub"]; 71 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(1, 2))); 72 | 73 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(2, 3) inFormattedValue:@"123 rub"]; 74 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(2, 1))); 75 | 76 | rangeInRaw = [formatter rangeInRawValueForRange:NSMakeRange(4, 1) inFormattedValue:@"123 rub"]; 77 | XCTAssertTrue(NSEqualRanges(rangeInRaw, NSMakeRange(3, 0))); 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /UnitTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /UnitTests/InputFilter/FTCDigitsInputFilterTestCase.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import XCTest; 22 | @import FTCTextEntryFormatting; 23 | 24 | static const NSUInteger MAX_LENGTH = 5; 25 | 26 | @interface FTCDigitsInputFilterTestCase : XCTestCase 27 | @end 28 | 29 | @implementation FTCDigitsInputFilterTestCase 30 | { 31 | FTCDigitsValueFilter *filter; 32 | } 33 | 34 | - (void)setUp 35 | { 36 | [super setUp]; 37 | 38 | filter = [[FTCDigitsValueFilter alloc] initWithMaxLength:MAX_LENGTH]; 39 | } 40 | 41 | - (void)test_filter_should_trim_empty_string 42 | { 43 | NSString *string = @""; 44 | NSString *filteredString = [filter filterString:string]; 45 | XCTAssertEqual(filteredString.length, 0); 46 | } 47 | 48 | - (void)test_filter_trim_string_case 49 | { 50 | NSString *string = @"123456"; 51 | NSString *filteredString = [filter filterString:string]; 52 | XCTAssertEqual(filteredString.length, MAX_LENGTH); 53 | } 54 | 55 | - (void)test_filter_empty_string 56 | { 57 | NSString *string = @""; 58 | NSString *filteredString = [filter filterString:string]; 59 | 60 | XCTAssertEqualObjects(string, filteredString); 61 | } 62 | 63 | - (void)test_filter_non_empty_digits_only_string 64 | { 65 | NSString *string = @"233223"; 66 | NSString *etalon = @"23322"; 67 | NSString *filteredString = [filter filterString:string]; 68 | 69 | XCTAssertEqualObjects(etalon, filteredString); 70 | } 71 | 72 | - (void)test_filter_non_empty_string 73 | { 74 | NSString *string = @"asdsdf33223dd"; 75 | NSString *etalon = @"33223"; 76 | NSString *filteredString = [filter filterString:string]; 77 | 78 | XCTAssertEqualObjects(etalon, filteredString); 79 | XCTAssertEqual(filteredString.length, MAX_LENGTH); 80 | } 81 | 82 | - (void)test_filter_digits_string_with_comma_delemiter 83 | { 84 | NSString *string = @"332,23"; 85 | NSString *etalon = @"33223"; 86 | NSString *filteredString = [filter filterString:string]; 87 | 88 | XCTAssertEqualObjects(etalon, filteredString); 89 | XCTAssertEqual(filteredString.length, MAX_LENGTH); 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /UnitTests/InputFilter/FTCDigitsInputFilterTestCaseWithZeroMaxLength.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 CFT 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @import XCTest; 22 | @import FTCTextEntryFormatting; 23 | 24 | 25 | @interface FTCDigitsInputFilterTestCaseWithZeroMaxLength : XCTestCase 26 | @end 27 | 28 | @implementation FTCDigitsInputFilterTestCaseWithZeroMaxLength 29 | { 30 | FTCDigitsValueFilter *filter; 31 | } 32 | 33 | - (void)setUp 34 | { 35 | [super setUp]; 36 | 37 | filter = [[FTCDigitsValueFilter alloc] initWithMaxLength:0]; 38 | } 39 | 40 | - (void)test_filter_empty_string 41 | { 42 | NSString *string = @""; 43 | NSString *filteredString = [filter filterString:string]; 44 | 45 | XCTAssertEqualObjects(string, filteredString); 46 | } 47 | 48 | - (void)test_filter_non_empty_digits_only_string 49 | { 50 | NSString *string = @"233223"; 51 | NSString *filteredString = [filter filterString:string]; 52 | 53 | XCTAssertEqualObjects(string, filteredString); 54 | } 55 | 56 | - (void)test_filter_non_empty_string 57 | { 58 | NSString *string = @"asdsdf33223dd"; 59 | NSString *etalon = @"33223"; 60 | NSString *filteredString = [filter filterString:string]; 61 | 62 | XCTAssertEqualObjects(etalon, filteredString); 63 | } 64 | 65 | - (void)test_filter_digits_string_with_comma_delemiter 66 | { 67 | NSString *string = @"332,23"; 68 | NSString *etalon = @"33223"; 69 | NSString *filteredString = [filter filterString:string]; 70 | 71 | XCTAssertEqualObjects(etalon, filteredString); 72 | } 73 | 74 | @end 75 | --------------------------------------------------------------------------------