├── .gitignore ├── Example ├── .idea │ ├── Example.iml │ ├── modules.xml │ ├── workspace.xml │ └── xcode.xml ├── Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── Example │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── ic_turtle.imageset │ │ ├── Contents.json │ │ ├── ic_turtle.png │ │ ├── ic_turtle@2x.png │ │ └── ic_turtle@3x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Cells │ ├── ButtonCell.h │ ├── ButtonCell.m │ ├── MultiTextCell.h │ ├── MultiTextCell.m │ ├── SegmentedCell.h │ ├── SegmentedCell.m │ ├── SliderCell.h │ ├── SliderCell.m │ ├── StepperCell.h │ ├── StepperCell.m │ ├── SwitchCell.h │ ├── SwitchCell.m │ ├── TextCell.h │ └── TextCell.m │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ ├── form.json │ └── main.m ├── LICENSE ├── PureForm.podspec ├── PureForm ├── Categories │ ├── NSString+PureForm.h │ ├── NSString+PureForm.m │ ├── PFInputView+Validation.h │ ├── PFInputView+Validation.m │ ├── PFValidator+FailureReason.h │ ├── PFValidator+FailureReason.m │ ├── UIColor+PureForm.h │ ├── UIColor+PureForm.m │ ├── UISlider+PureForm.h │ ├── UISlider+PureForm.m │ ├── UITableViewCell+PureForm.h │ ├── UITableViewCell+PureForm.m │ ├── UIView+PureForm.h │ └── UIView+PureForm.m ├── Common │ ├── PFConstants.h │ ├── PFEnums.h │ ├── PFEnums.m │ ├── PFMacros.h │ └── PFProtocols.h ├── Delegates │ ├── PFFormDelegate.h │ ├── PFInputViewDelegate.h │ ├── PFInputViewDelegate.m │ └── PFViewDelegate.h ├── Models │ ├── PFError.h │ ├── PFError.m │ ├── PFInputView.h │ ├── PFInputView.m │ ├── PFModel.h │ ├── PFModel.m │ ├── PFSettings.h │ ├── PFSettings.m │ ├── PFValidator.h │ └── PFValidator.m ├── PFFormController.h ├── PFFormController.m ├── PFJSONSerialization.h ├── PFJSONSerialization.m ├── PFTableViewDataSource.h ├── PFTableViewDataSource.m └── PureForm.h ├── README.md └── Screenshots ├── centered_text.png ├── clean_form.png ├── filled_form.png └── filled_form_error.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /Example/.idea/Example.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Example/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 495379E0E7B8260F290BF8BC /* form.json in Resources */ = {isa = PBXBuildFile; fileRef = 49537A582DA49F05CD69AAA0 /* form.json */; }; 11 | E920F5AA1D6B189200F98334 /* UISlider+PureForm.m in Sources */ = {isa = PBXBuildFile; fileRef = E920F5A91D6B189200F98334 /* UISlider+PureForm.m */; }; 12 | E920F5B01D6BB3B900F98334 /* UIColor+PureForm.m in Sources */ = {isa = PBXBuildFile; fileRef = E920F5AF1D6BB3B900F98334 /* UIColor+PureForm.m */; }; 13 | E92B23DE1D6E261A001FDC7F /* PFError.m in Sources */ = {isa = PBXBuildFile; fileRef = E92B23D91D6E261A001FDC7F /* PFError.m */; }; 14 | E92B23DF1D6E261A001FDC7F /* PFModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E92B23DB1D6E261A001FDC7F /* PFModel.m */; }; 15 | E92B23E01D6E261A001FDC7F /* PFSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = E92B23DD1D6E261A001FDC7F /* PFSettings.m */; }; 16 | E92B23E31D6E2630001FDC7F /* PFInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = E92B23E21D6E2630001FDC7F /* PFInputView.m */; }; 17 | E92B23E61D6E2641001FDC7F /* PFValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = E92B23E51D6E2641001FDC7F /* PFValidator.m */; }; 18 | E959E1D41D687D3800D5461B /* SwitchCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E959E1D31D687D3800D5461B /* SwitchCell.m */; }; 19 | E959E1D71D68A09D00D5461B /* StepperCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E959E1D61D68A09D00D5461B /* StepperCell.m */; }; 20 | E959E1DA1D68F01200D5461B /* SliderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E959E1D91D68F01200D5461B /* SliderCell.m */; }; 21 | E965BDE81D5F36E400D05E14 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BDE71D5F36E400D05E14 /* main.m */; }; 22 | E965BDEB1D5F36E400D05E14 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BDEA1D5F36E400D05E14 /* AppDelegate.m */; }; 23 | E965BDEE1D5F36E400D05E14 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BDED1D5F36E400D05E14 /* ViewController.m */; }; 24 | E965BDF11D5F36E400D05E14 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E965BDEF1D5F36E400D05E14 /* Main.storyboard */; }; 25 | E965BDF31D5F36E400D05E14 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E965BDF21D5F36E400D05E14 /* Assets.xcassets */; }; 26 | E965BDF61D5F36E400D05E14 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E965BDF41D5F36E400D05E14 /* LaunchScreen.storyboard */; }; 27 | E965BE011D5F46B200D05E14 /* PFJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BE001D5F46B200D05E14 /* PFJSONSerialization.m */; }; 28 | E965BE041D5F4DE400D05E14 /* PFFormController.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BE031D5F4DE400D05E14 /* PFFormController.m */; }; 29 | E965BE121D5F652300D05E14 /* NSString+PureForm.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BE111D5F652300D05E14 /* NSString+PureForm.m */; }; 30 | E965BE211D608A3700D05E14 /* PFEnums.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BE1F1D608A3700D05E14 /* PFEnums.m */; }; 31 | E965BE261D608E4A00D05E14 /* PFTableViewDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BE251D608E4A00D05E14 /* PFTableViewDataSource.m */; }; 32 | E965BE2A1D60963600D05E14 /* TextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BE291D60963600D05E14 /* TextCell.m */; }; 33 | E965BE331D62417F00D05E14 /* UITableViewCell+PureForm.m in Sources */ = {isa = PBXBuildFile; fileRef = E965BE321D62417F00D05E14 /* UITableViewCell+PureForm.m */; }; 34 | E981C5B81D635B28001C25EF /* PFInputViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E981C5B71D635B28001C25EF /* PFInputViewDelegate.m */; }; 35 | E981C5BB1D6370BC001C25EF /* PFInputView+Validation.m in Sources */ = {isa = PBXBuildFile; fileRef = E981C5BA1D6370BC001C25EF /* PFInputView+Validation.m */; }; 36 | E981C5BE1D648737001C25EF /* PFValidator+FailureReason.m in Sources */ = {isa = PBXBuildFile; fileRef = E981C5BD1D648737001C25EF /* PFValidator+FailureReason.m */; }; 37 | E981C5C11D649782001C25EF /* ButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E981C5C01D649782001C25EF /* ButtonCell.m */; }; 38 | E981C5C41D64CFD1001C25EF /* UIView+PureForm.m in Sources */ = {isa = PBXBuildFile; fileRef = E981C5C31D64CFD1001C25EF /* UIView+PureForm.m */; }; 39 | E981C5CA1D6600DC001C25EF /* MultiTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E981C5C91D6600DC001C25EF /* MultiTextCell.m */; }; 40 | E9F2FEDF1D672A8100657744 /* SegmentedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F2FEDE1D672A8100657744 /* SegmentedCell.m */; }; 41 | /* End PBXBuildFile section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 49537A582DA49F05CD69AAA0 /* form.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = form.json; sourceTree = ""; }; 45 | E920F5A81D6B189200F98334 /* UISlider+PureForm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISlider+PureForm.h"; sourceTree = ""; }; 46 | E920F5A91D6B189200F98334 /* UISlider+PureForm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISlider+PureForm.m"; sourceTree = ""; }; 47 | E920F5AB1D6B486500F98334 /* PureForm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PureForm.h; sourceTree = ""; }; 48 | E920F5AE1D6BB3B900F98334 /* UIColor+PureForm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+PureForm.h"; sourceTree = ""; }; 49 | E920F5AF1D6BB3B900F98334 /* UIColor+PureForm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+PureForm.m"; sourceTree = ""; }; 50 | E92B23D81D6E261A001FDC7F /* PFError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFError.h; sourceTree = ""; }; 51 | E92B23D91D6E261A001FDC7F /* PFError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFError.m; sourceTree = ""; }; 52 | E92B23DA1D6E261A001FDC7F /* PFModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFModel.h; sourceTree = ""; }; 53 | E92B23DB1D6E261A001FDC7F /* PFModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFModel.m; sourceTree = ""; }; 54 | E92B23DC1D6E261A001FDC7F /* PFSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFSettings.h; sourceTree = ""; }; 55 | E92B23DD1D6E261A001FDC7F /* PFSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFSettings.m; sourceTree = ""; }; 56 | E92B23E11D6E2630001FDC7F /* PFInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFInputView.h; sourceTree = ""; }; 57 | E92B23E21D6E2630001FDC7F /* PFInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFInputView.m; sourceTree = ""; }; 58 | E92B23E41D6E2641001FDC7F /* PFValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFValidator.h; sourceTree = ""; }; 59 | E92B23E51D6E2641001FDC7F /* PFValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFValidator.m; sourceTree = ""; }; 60 | E959E1D11D68651900D5461B /* PFProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFProtocols.h; sourceTree = ""; }; 61 | E959E1D21D687D3800D5461B /* SwitchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwitchCell.h; sourceTree = ""; }; 62 | E959E1D31D687D3800D5461B /* SwitchCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwitchCell.m; sourceTree = ""; }; 63 | E959E1D51D68A09D00D5461B /* StepperCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StepperCell.h; sourceTree = ""; }; 64 | E959E1D61D68A09D00D5461B /* StepperCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StepperCell.m; sourceTree = ""; }; 65 | E959E1D81D68F01200D5461B /* SliderCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SliderCell.h; sourceTree = ""; }; 66 | E959E1D91D68F01200D5461B /* SliderCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SliderCell.m; sourceTree = ""; }; 67 | E965BDE31D5F36E400D05E14 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | E965BDE71D5F36E400D05E14 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 69 | E965BDE91D5F36E400D05E14 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 70 | E965BDEA1D5F36E400D05E14 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 71 | E965BDEC1D5F36E400D05E14 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 72 | E965BDED1D5F36E400D05E14 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 73 | E965BDF01D5F36E400D05E14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 74 | E965BDF21D5F36E400D05E14 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 75 | E965BDF51D5F36E400D05E14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 76 | E965BDF71D5F36E400D05E14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 77 | E965BDFF1D5F46B200D05E14 /* PFJSONSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFJSONSerialization.h; sourceTree = ""; }; 78 | E965BE001D5F46B200D05E14 /* PFJSONSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFJSONSerialization.m; sourceTree = ""; }; 79 | E965BE021D5F4DE400D05E14 /* PFFormController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFFormController.h; sourceTree = ""; }; 80 | E965BE031D5F4DE400D05E14 /* PFFormController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFFormController.m; sourceTree = ""; }; 81 | E965BE101D5F652300D05E14 /* NSString+PureForm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+PureForm.h"; sourceTree = ""; }; 82 | E965BE111D5F652300D05E14 /* NSString+PureForm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+PureForm.m"; sourceTree = ""; }; 83 | E965BE1D1D608A3700D05E14 /* PFConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFConstants.h; sourceTree = ""; }; 84 | E965BE1E1D608A3700D05E14 /* PFEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFEnums.h; sourceTree = ""; }; 85 | E965BE1F1D608A3700D05E14 /* PFEnums.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFEnums.m; sourceTree = ""; }; 86 | E965BE201D608A3700D05E14 /* PFMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFMacros.h; sourceTree = ""; }; 87 | E965BE231D608BAA00D05E14 /* PFFormDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFFormDelegate.h; sourceTree = ""; }; 88 | E965BE241D608E4A00D05E14 /* PFTableViewDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFTableViewDataSource.h; sourceTree = ""; }; 89 | E965BE251D608E4A00D05E14 /* PFTableViewDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFTableViewDataSource.m; sourceTree = ""; }; 90 | E965BE281D60963600D05E14 /* TextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCell.h; sourceTree = ""; }; 91 | E965BE291D60963600D05E14 /* TextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextCell.m; sourceTree = ""; }; 92 | E965BE311D62417F00D05E14 /* UITableViewCell+PureForm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+PureForm.h"; sourceTree = ""; }; 93 | E965BE321D62417F00D05E14 /* UITableViewCell+PureForm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+PureForm.m"; sourceTree = ""; }; 94 | E981C5B71D635B28001C25EF /* PFInputViewDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFInputViewDelegate.m; sourceTree = ""; }; 95 | E981C5B91D6370BC001C25EF /* PFInputView+Validation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PFInputView+Validation.h"; sourceTree = ""; }; 96 | E981C5BA1D6370BC001C25EF /* PFInputView+Validation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PFInputView+Validation.m"; sourceTree = ""; }; 97 | E981C5BC1D648737001C25EF /* PFValidator+FailureReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PFValidator+FailureReason.h"; sourceTree = ""; }; 98 | E981C5BD1D648737001C25EF /* PFValidator+FailureReason.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PFValidator+FailureReason.m"; sourceTree = ""; }; 99 | E981C5BF1D649782001C25EF /* ButtonCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ButtonCell.h; sourceTree = ""; }; 100 | E981C5C01D649782001C25EF /* ButtonCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ButtonCell.m; sourceTree = ""; }; 101 | E981C5C21D64CFD1001C25EF /* UIView+PureForm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+PureForm.h"; sourceTree = ""; }; 102 | E981C5C31D64CFD1001C25EF /* UIView+PureForm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+PureForm.m"; sourceTree = ""; }; 103 | E981C5C81D6600DC001C25EF /* MultiTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiTextCell.h; sourceTree = ""; }; 104 | E981C5C91D6600DC001C25EF /* MultiTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiTextCell.m; sourceTree = ""; }; 105 | E9F2FEDD1D672A8100657744 /* SegmentedCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SegmentedCell.h; sourceTree = ""; }; 106 | E9F2FEDE1D672A8100657744 /* SegmentedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SegmentedCell.m; sourceTree = ""; }; 107 | E9F2FEE11D6739E900657744 /* PFInputViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PFInputViewDelegate.h; sourceTree = ""; }; 108 | E9F2FEE21D673A1000657744 /* PFViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFViewDelegate.h; sourceTree = ""; }; 109 | /* End PBXFileReference section */ 110 | 111 | /* Begin PBXFrameworksBuildPhase section */ 112 | E965BDE01D5F36E400D05E14 /* Frameworks */ = { 113 | isa = PBXFrameworksBuildPhase; 114 | buildActionMask = 2147483647; 115 | files = ( 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | /* End PBXFrameworksBuildPhase section */ 120 | 121 | /* Begin PBXGroup section */ 122 | E92B23D71D6E261A001FDC7F /* Models */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | E92B23D81D6E261A001FDC7F /* PFError.h */, 126 | E92B23D91D6E261A001FDC7F /* PFError.m */, 127 | E92B23DA1D6E261A001FDC7F /* PFModel.h */, 128 | E92B23DB1D6E261A001FDC7F /* PFModel.m */, 129 | E92B23DC1D6E261A001FDC7F /* PFSettings.h */, 130 | E92B23DD1D6E261A001FDC7F /* PFSettings.m */, 131 | E92B23E11D6E2630001FDC7F /* PFInputView.h */, 132 | E92B23E21D6E2630001FDC7F /* PFInputView.m */, 133 | E92B23E41D6E2641001FDC7F /* PFValidator.h */, 134 | E92B23E51D6E2641001FDC7F /* PFValidator.m */, 135 | ); 136 | path = Models; 137 | sourceTree = ""; 138 | }; 139 | E965BDDA1D5F36E400D05E14 = { 140 | isa = PBXGroup; 141 | children = ( 142 | E965BDFE1D5F380100D05E14 /* PureForm */, 143 | E965BDE51D5F36E400D05E14 /* Example */, 144 | E965BDE41D5F36E400D05E14 /* Products */, 145 | ); 146 | sourceTree = ""; 147 | }; 148 | E965BDE41D5F36E400D05E14 /* Products */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | E965BDE31D5F36E400D05E14 /* Example.app */, 152 | ); 153 | name = Products; 154 | sourceTree = ""; 155 | }; 156 | E965BDE51D5F36E400D05E14 /* Example */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | E965BE271D60961C00D05E14 /* Cells */, 160 | E965BDE91D5F36E400D05E14 /* AppDelegate.h */, 161 | E965BDEA1D5F36E400D05E14 /* AppDelegate.m */, 162 | E965BDEC1D5F36E400D05E14 /* ViewController.h */, 163 | E965BDED1D5F36E400D05E14 /* ViewController.m */, 164 | E965BDEF1D5F36E400D05E14 /* Main.storyboard */, 165 | E965BDF21D5F36E400D05E14 /* Assets.xcassets */, 166 | E965BDF41D5F36E400D05E14 /* LaunchScreen.storyboard */, 167 | E965BDF71D5F36E400D05E14 /* Info.plist */, 168 | E965BDE61D5F36E400D05E14 /* Supporting Files */, 169 | 49537A582DA49F05CD69AAA0 /* form.json */, 170 | ); 171 | path = Example; 172 | sourceTree = ""; 173 | }; 174 | E965BDE61D5F36E400D05E14 /* Supporting Files */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | E965BDE71D5F36E400D05E14 /* main.m */, 178 | ); 179 | name = "Supporting Files"; 180 | sourceTree = ""; 181 | }; 182 | E965BDFE1D5F380100D05E14 /* PureForm */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | E965BE0C1D5F614000D05E14 /* Categories */, 186 | E965BE1C1D608A3700D05E14 /* Common */, 187 | E965BE221D608A4800D05E14 /* Delegates */, 188 | E92B23D71D6E261A001FDC7F /* Models */, 189 | E965BDFF1D5F46B200D05E14 /* PFJSONSerialization.h */, 190 | E965BE001D5F46B200D05E14 /* PFJSONSerialization.m */, 191 | E965BE021D5F4DE400D05E14 /* PFFormController.h */, 192 | E965BE031D5F4DE400D05E14 /* PFFormController.m */, 193 | E965BE241D608E4A00D05E14 /* PFTableViewDataSource.h */, 194 | E965BE251D608E4A00D05E14 /* PFTableViewDataSource.m */, 195 | E920F5AB1D6B486500F98334 /* PureForm.h */, 196 | ); 197 | name = PureForm; 198 | path = ../PureForm; 199 | sourceTree = ""; 200 | }; 201 | E965BE0C1D5F614000D05E14 /* Categories */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | E965BE101D5F652300D05E14 /* NSString+PureForm.h */, 205 | E965BE111D5F652300D05E14 /* NSString+PureForm.m */, 206 | E965BE311D62417F00D05E14 /* UITableViewCell+PureForm.h */, 207 | E965BE321D62417F00D05E14 /* UITableViewCell+PureForm.m */, 208 | E981C5B91D6370BC001C25EF /* PFInputView+Validation.h */, 209 | E981C5BA1D6370BC001C25EF /* PFInputView+Validation.m */, 210 | E981C5BC1D648737001C25EF /* PFValidator+FailureReason.h */, 211 | E981C5BD1D648737001C25EF /* PFValidator+FailureReason.m */, 212 | E981C5C21D64CFD1001C25EF /* UIView+PureForm.h */, 213 | E981C5C31D64CFD1001C25EF /* UIView+PureForm.m */, 214 | E920F5A81D6B189200F98334 /* UISlider+PureForm.h */, 215 | E920F5A91D6B189200F98334 /* UISlider+PureForm.m */, 216 | E920F5AE1D6BB3B900F98334 /* UIColor+PureForm.h */, 217 | E920F5AF1D6BB3B900F98334 /* UIColor+PureForm.m */, 218 | ); 219 | path = Categories; 220 | sourceTree = ""; 221 | }; 222 | E965BE1C1D608A3700D05E14 /* Common */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | E965BE1D1D608A3700D05E14 /* PFConstants.h */, 226 | E965BE1E1D608A3700D05E14 /* PFEnums.h */, 227 | E965BE1F1D608A3700D05E14 /* PFEnums.m */, 228 | E965BE201D608A3700D05E14 /* PFMacros.h */, 229 | E959E1D11D68651900D5461B /* PFProtocols.h */, 230 | ); 231 | path = Common; 232 | sourceTree = ""; 233 | }; 234 | E965BE221D608A4800D05E14 /* Delegates */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | E965BE231D608BAA00D05E14 /* PFFormDelegate.h */, 238 | E9F2FEE11D6739E900657744 /* PFInputViewDelegate.h */, 239 | E981C5B71D635B28001C25EF /* PFInputViewDelegate.m */, 240 | E9F2FEE21D673A1000657744 /* PFViewDelegate.h */, 241 | ); 242 | path = Delegates; 243 | sourceTree = ""; 244 | }; 245 | E965BE271D60961C00D05E14 /* Cells */ = { 246 | isa = PBXGroup; 247 | children = ( 248 | E965BE281D60963600D05E14 /* TextCell.h */, 249 | E965BE291D60963600D05E14 /* TextCell.m */, 250 | E981C5BF1D649782001C25EF /* ButtonCell.h */, 251 | E981C5C01D649782001C25EF /* ButtonCell.m */, 252 | E981C5C81D6600DC001C25EF /* MultiTextCell.h */, 253 | E981C5C91D6600DC001C25EF /* MultiTextCell.m */, 254 | E9F2FEDD1D672A8100657744 /* SegmentedCell.h */, 255 | E9F2FEDE1D672A8100657744 /* SegmentedCell.m */, 256 | E959E1D21D687D3800D5461B /* SwitchCell.h */, 257 | E959E1D31D687D3800D5461B /* SwitchCell.m */, 258 | E959E1D51D68A09D00D5461B /* StepperCell.h */, 259 | E959E1D61D68A09D00D5461B /* StepperCell.m */, 260 | E959E1D81D68F01200D5461B /* SliderCell.h */, 261 | E959E1D91D68F01200D5461B /* SliderCell.m */, 262 | ); 263 | path = Cells; 264 | sourceTree = ""; 265 | }; 266 | /* End PBXGroup section */ 267 | 268 | /* Begin PBXNativeTarget section */ 269 | E965BDE21D5F36E400D05E14 /* Example */ = { 270 | isa = PBXNativeTarget; 271 | buildConfigurationList = E965BDFA1D5F36E400D05E14 /* Build configuration list for PBXNativeTarget "Example" */; 272 | buildPhases = ( 273 | E965BDDF1D5F36E400D05E14 /* Sources */, 274 | E965BDE01D5F36E400D05E14 /* Frameworks */, 275 | E965BDE11D5F36E400D05E14 /* Resources */, 276 | ); 277 | buildRules = ( 278 | ); 279 | dependencies = ( 280 | ); 281 | name = Example; 282 | productName = Example; 283 | productReference = E965BDE31D5F36E400D05E14 /* Example.app */; 284 | productType = "com.apple.product-type.application"; 285 | }; 286 | /* End PBXNativeTarget section */ 287 | 288 | /* Begin PBXProject section */ 289 | E965BDDB1D5F36E400D05E14 /* Project object */ = { 290 | isa = PBXProject; 291 | attributes = { 292 | LastUpgradeCheck = 0730; 293 | ORGANIZATIONNAME = "Puzyrev Pavel"; 294 | TargetAttributes = { 295 | E965BDE21D5F36E400D05E14 = { 296 | CreatedOnToolsVersion = 7.3.1; 297 | DevelopmentTeam = 22BDH96YAS; 298 | }; 299 | }; 300 | }; 301 | buildConfigurationList = E965BDDE1D5F36E400D05E14 /* Build configuration list for PBXProject "Example" */; 302 | compatibilityVersion = "Xcode 3.2"; 303 | developmentRegion = English; 304 | hasScannedForEncodings = 0; 305 | knownRegions = ( 306 | en, 307 | Base, 308 | ); 309 | mainGroup = E965BDDA1D5F36E400D05E14; 310 | productRefGroup = E965BDE41D5F36E400D05E14 /* Products */; 311 | projectDirPath = ""; 312 | projectRoot = ""; 313 | targets = ( 314 | E965BDE21D5F36E400D05E14 /* Example */, 315 | ); 316 | }; 317 | /* End PBXProject section */ 318 | 319 | /* Begin PBXResourcesBuildPhase section */ 320 | E965BDE11D5F36E400D05E14 /* Resources */ = { 321 | isa = PBXResourcesBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | E965BDF61D5F36E400D05E14 /* LaunchScreen.storyboard in Resources */, 325 | E965BDF31D5F36E400D05E14 /* Assets.xcassets in Resources */, 326 | E965BDF11D5F36E400D05E14 /* Main.storyboard in Resources */, 327 | 495379E0E7B8260F290BF8BC /* form.json in Resources */, 328 | ); 329 | runOnlyForDeploymentPostprocessing = 0; 330 | }; 331 | /* End PBXResourcesBuildPhase section */ 332 | 333 | /* Begin PBXSourcesBuildPhase section */ 334 | E965BDDF1D5F36E400D05E14 /* Sources */ = { 335 | isa = PBXSourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | E965BE211D608A3700D05E14 /* PFEnums.m in Sources */, 339 | E981C5B81D635B28001C25EF /* PFInputViewDelegate.m in Sources */, 340 | E965BE331D62417F00D05E14 /* UITableViewCell+PureForm.m in Sources */, 341 | E959E1D41D687D3800D5461B /* SwitchCell.m in Sources */, 342 | E981C5CA1D6600DC001C25EF /* MultiTextCell.m in Sources */, 343 | E92B23E31D6E2630001FDC7F /* PFInputView.m in Sources */, 344 | E965BE2A1D60963600D05E14 /* TextCell.m in Sources */, 345 | E92B23E61D6E2641001FDC7F /* PFValidator.m in Sources */, 346 | E965BE041D5F4DE400D05E14 /* PFFormController.m in Sources */, 347 | E9F2FEDF1D672A8100657744 /* SegmentedCell.m in Sources */, 348 | E965BE121D5F652300D05E14 /* NSString+PureForm.m in Sources */, 349 | E92B23DF1D6E261A001FDC7F /* PFModel.m in Sources */, 350 | E920F5AA1D6B189200F98334 /* UISlider+PureForm.m in Sources */, 351 | E965BE261D608E4A00D05E14 /* PFTableViewDataSource.m in Sources */, 352 | E92B23E01D6E261A001FDC7F /* PFSettings.m in Sources */, 353 | E959E1DA1D68F01200D5461B /* SliderCell.m in Sources */, 354 | E920F5B01D6BB3B900F98334 /* UIColor+PureForm.m in Sources */, 355 | E965BDEE1D5F36E400D05E14 /* ViewController.m in Sources */, 356 | E965BDEB1D5F36E400D05E14 /* AppDelegate.m in Sources */, 357 | E965BE011D5F46B200D05E14 /* PFJSONSerialization.m in Sources */, 358 | E965BDE81D5F36E400D05E14 /* main.m in Sources */, 359 | E92B23DE1D6E261A001FDC7F /* PFError.m in Sources */, 360 | E981C5BE1D648737001C25EF /* PFValidator+FailureReason.m in Sources */, 361 | E981C5C41D64CFD1001C25EF /* UIView+PureForm.m in Sources */, 362 | E981C5BB1D6370BC001C25EF /* PFInputView+Validation.m in Sources */, 363 | E981C5C11D649782001C25EF /* ButtonCell.m in Sources */, 364 | E959E1D71D68A09D00D5461B /* StepperCell.m in Sources */, 365 | ); 366 | runOnlyForDeploymentPostprocessing = 0; 367 | }; 368 | /* End PBXSourcesBuildPhase section */ 369 | 370 | /* Begin PBXVariantGroup section */ 371 | E965BDEF1D5F36E400D05E14 /* Main.storyboard */ = { 372 | isa = PBXVariantGroup; 373 | children = ( 374 | E965BDF01D5F36E400D05E14 /* Base */, 375 | ); 376 | name = Main.storyboard; 377 | sourceTree = ""; 378 | }; 379 | E965BDF41D5F36E400D05E14 /* LaunchScreen.storyboard */ = { 380 | isa = PBXVariantGroup; 381 | children = ( 382 | E965BDF51D5F36E400D05E14 /* Base */, 383 | ); 384 | name = LaunchScreen.storyboard; 385 | sourceTree = ""; 386 | }; 387 | /* End PBXVariantGroup section */ 388 | 389 | /* Begin XCBuildConfiguration section */ 390 | E965BDF81D5F36E400D05E14 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ALWAYS_SEARCH_USER_PATHS = NO; 394 | CLANG_ANALYZER_NONNULL = YES; 395 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 396 | CLANG_CXX_LIBRARY = "libc++"; 397 | CLANG_ENABLE_MODULES = YES; 398 | CLANG_ENABLE_OBJC_ARC = YES; 399 | CLANG_WARN_BOOL_CONVERSION = YES; 400 | CLANG_WARN_CONSTANT_CONVERSION = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INT_CONVERSION = YES; 405 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 406 | CLANG_WARN_UNREACHABLE_CODE = YES; 407 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 408 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 409 | COPY_PHASE_STRIP = NO; 410 | DEBUG_INFORMATION_FORMAT = dwarf; 411 | ENABLE_STRICT_OBJC_MSGSEND = YES; 412 | ENABLE_TESTABILITY = YES; 413 | GCC_C_LANGUAGE_STANDARD = gnu99; 414 | GCC_DYNAMIC_NO_PIC = NO; 415 | GCC_NO_COMMON_BLOCKS = YES; 416 | GCC_OPTIMIZATION_LEVEL = 0; 417 | GCC_PREPROCESSOR_DEFINITIONS = ( 418 | "DEBUG=1", 419 | "$(inherited)", 420 | ); 421 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 422 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 423 | GCC_WARN_UNDECLARED_SELECTOR = YES; 424 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 425 | GCC_WARN_UNUSED_FUNCTION = YES; 426 | GCC_WARN_UNUSED_VARIABLE = YES; 427 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 428 | MTL_ENABLE_DEBUG_INFO = YES; 429 | ONLY_ACTIVE_ARCH = YES; 430 | SDKROOT = iphoneos; 431 | }; 432 | name = Debug; 433 | }; 434 | E965BDF91D5F36E400D05E14 /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ALWAYS_SEARCH_USER_PATHS = NO; 438 | CLANG_ANALYZER_NONNULL = YES; 439 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 440 | CLANG_CXX_LIBRARY = "libc++"; 441 | CLANG_ENABLE_MODULES = YES; 442 | CLANG_ENABLE_OBJC_ARC = YES; 443 | CLANG_WARN_BOOL_CONVERSION = YES; 444 | CLANG_WARN_CONSTANT_CONVERSION = YES; 445 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 446 | CLANG_WARN_EMPTY_BODY = YES; 447 | CLANG_WARN_ENUM_CONVERSION = YES; 448 | CLANG_WARN_INT_CONVERSION = YES; 449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 450 | CLANG_WARN_UNREACHABLE_CODE = YES; 451 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 452 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 453 | COPY_PHASE_STRIP = NO; 454 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 455 | ENABLE_NS_ASSERTIONS = NO; 456 | ENABLE_STRICT_OBJC_MSGSEND = YES; 457 | GCC_C_LANGUAGE_STANDARD = gnu99; 458 | GCC_NO_COMMON_BLOCKS = YES; 459 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 460 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 461 | GCC_WARN_UNDECLARED_SELECTOR = YES; 462 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 463 | GCC_WARN_UNUSED_FUNCTION = YES; 464 | GCC_WARN_UNUSED_VARIABLE = YES; 465 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 466 | MTL_ENABLE_DEBUG_INFO = NO; 467 | SDKROOT = iphoneos; 468 | VALIDATE_PRODUCT = YES; 469 | }; 470 | name = Release; 471 | }; 472 | E965BDFB1D5F36E400D05E14 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 476 | DEVELOPMENT_TEAM = 22BDH96YAS; 477 | INFOPLIST_FILE = Example/Info.plist; 478 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 479 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 480 | PRODUCT_BUNDLE_IDENTIFIER = com.cannedapp.pureformexample; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | }; 483 | name = Debug; 484 | }; 485 | E965BDFC1D5F36E400D05E14 /* Release */ = { 486 | isa = XCBuildConfiguration; 487 | buildSettings = { 488 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 489 | DEVELOPMENT_TEAM = 22BDH96YAS; 490 | INFOPLIST_FILE = Example/Info.plist; 491 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 492 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 493 | PRODUCT_BUNDLE_IDENTIFIER = com.cannedapp.pureformexample; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | }; 496 | name = Release; 497 | }; 498 | /* End XCBuildConfiguration section */ 499 | 500 | /* Begin XCConfigurationList section */ 501 | E965BDDE1D5F36E400D05E14 /* Build configuration list for PBXProject "Example" */ = { 502 | isa = XCConfigurationList; 503 | buildConfigurations = ( 504 | E965BDF81D5F36E400D05E14 /* Debug */, 505 | E965BDF91D5F36E400D05E14 /* Release */, 506 | ); 507 | defaultConfigurationIsVisible = 0; 508 | defaultConfigurationName = Release; 509 | }; 510 | E965BDFA1D5F36E400D05E14 /* Build configuration list for PBXNativeTarget "Example" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | E965BDFB1D5F36E400D05E14 /* Debug */, 514 | E965BDFC1D5F36E400D05E14 /* Release */, 515 | ); 516 | defaultConfigurationIsVisible = 0; 517 | defaultConfigurationName = Release; 518 | }; 519 | /* End XCConfigurationList section */ 520 | }; 521 | rootObject = E965BDDB1D5F36E400D05E14 /* Project object */; 522 | } 523 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 13.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 13.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/ic_turtle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_turtle.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ic_turtle@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ic_turtle@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/ic_turtle.imageset/ic_turtle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unboxme/PureForm/84c77c143b4249c19e11d0728bcb23cd0adffb21/Example/Example/Assets.xcassets/ic_turtle.imageset/ic_turtle.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/ic_turtle.imageset/ic_turtle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unboxme/PureForm/84c77c143b4249c19e11d0728bcb23cd0adffb21/Example/Example/Assets.xcassets/ic_turtle.imageset/ic_turtle@2x.png -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/ic_turtle.imageset/ic_turtle@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unboxme/PureForm/84c77c143b4249c19e11d0728bcb23cd0adffb21/Example/Example/Assets.xcassets/ic_turtle.imageset/ic_turtle@3x.png -------------------------------------------------------------------------------- /Example/Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Example/Example/Cells/ButtonCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ButtonCell.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 17.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ButtonCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Example/Cells/ButtonCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ButtonCell.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 17.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "ButtonCell.h" 10 | 11 | @implementation ButtonCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Example/Example/Cells/MultiTextCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiTextCell.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 18.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MultiTextCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | @property (weak, nonatomic) IBOutlet UITextField *dayTextField; 15 | @property (weak, nonatomic) IBOutlet UITextField *monthTextField; 16 | @property (weak, nonatomic) IBOutlet UITextField *yearTextField; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Example/Example/Cells/MultiTextCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultiTextCell.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 18.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "MultiTextCell.h" 10 | 11 | @implementation MultiTextCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Example/Example/Cells/SegmentedCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SegmentedCell.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 19.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SegmentedCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedControl; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Example/Cells/SegmentedCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SegmentedCell.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 19.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "SegmentedCell.h" 10 | 11 | @implementation SegmentedCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Example/Example/Cells/SliderCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SliderCell.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 20.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SliderCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | @property (weak, nonatomic) IBOutlet UISlider *sliderControl; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Example/Example/Cells/SliderCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SliderCell.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 20.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "SliderCell.h" 10 | 11 | @implementation SliderCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Example/Cells/StepperCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // StepperCell.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 20.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface StepperCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | @property (weak, nonatomic) IBOutlet UILabel *displayLabel; 15 | @property (weak, nonatomic) IBOutlet UIStepper *stepperControl; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Example/Cells/StepperCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // StepperCell.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 20.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "StepperCell.h" 10 | 11 | @implementation StepperCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Example/Cells/SwitchCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchCell.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 20.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SwitchCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | @property (weak, nonatomic) IBOutlet UISwitch *switchControl; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Example/Example/Cells/SwitchCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchCell.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 20.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "SwitchCell.h" 10 | 11 | @implementation SwitchCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Example/Cells/TextCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // TextCell.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 14.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TextCell : UITableViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | @property (weak, nonatomic) IBOutlet UITextField *textField; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Example/Example/Cells/TextCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // TextCell.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 14.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "TextCell.h" 10 | 11 | @implementation TextCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Example/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 | 0.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/Example/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 13.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/Example/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 13.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "PureForm.h" 12 | 13 | @interface ViewController () 14 | 15 | @property(strong, nonatomic) PFFormController *formController; 16 | @property(weak, nonatomic) IBOutlet UITableView *tableView; 17 | 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | PFSettings *settings = [[PFSettings alloc] init]; 26 | settings.keyboardTypeValidation = YES; 27 | settings.keyboardAvoiding = YES; 28 | settings.formDelegate = self; 29 | settings.tableViewDelegate = self; 30 | 31 | self.formController = [[PFFormController alloc] initWithTableView:self.tableView settings:settings]; 32 | [self.formController makeFormWithJSONFile:@"form"]; 33 | } 34 | 35 | #pragma mark - PFFormDelegate 36 | 37 | - (void)valueDidUpdate { 38 | NSLog(@"valueDidUpdate"); 39 | } 40 | 41 | - (void)valueDidUpdateWithError:(PFError *)error { 42 | NSLog(@"valueDidUpdateWithError: - %@", error.reason); 43 | } 44 | 45 | - (void)validationDidEndSuccessfully { 46 | NSLog(@"validationDidEndSuccessfully"); 47 | 48 | NSLog(@"%@", [self.formController allKeyValuePairs]); 49 | [self showAlertWithMessage:[NSString stringWithFormat:@"%@", [self.formController allKeyValuePairs]] error:NO]; 50 | } 51 | 52 | - (void)validationDidFailWithErrors:(NSArray *)errors { 53 | NSLog(@"validationDidFailWithErrors: - \n%@", [PFError descriptionFromErrors:errors]); 54 | 55 | [self showAlertWithMessage:@"Some errors, check log." error:YES]; 56 | } 57 | 58 | #pragma mark - UITableViewDelegate 59 | 60 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 61 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 62 | 63 | if (indexPath.section == 3) { 64 | [self.formController validate]; 65 | } 66 | } 67 | 68 | #pragma mark - Alerts 69 | 70 | - (void)showAlertWithMessage:(NSString *)message error:(BOOL)isError { 71 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:isError ? @"Error" : @"Yep" 72 | message:message 73 | preferredStyle:UIAlertControllerStyleAlert]; 74 | UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" 75 | style:UIAlertActionStyleCancel 76 | handler:nil]; 77 | [alertController addAction:okAction]; 78 | 79 | [self presentViewController:alertController animated:YES completion:nil]; 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Example/Example/form.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "": { 4 | "textField?": { 5 | "placeholder": "Thomas", 6 | "": "name", 7 | "": { 8 | "required": true 9 | } 10 | }, 11 | "titleLabel": { 12 | "text": "Name" 13 | } 14 | } 15 | }, 16 | { 17 | "<= + =>": { 18 | "textField?": { 19 | "placeholder": "Bakker", 20 | "": "surname", 21 | "": { 22 | "required": true 23 | } 24 | }, 25 | "titleLabel": { 26 | "text": "Surname" 27 | } 28 | } 29 | }, 30 | { 31 | "": { 32 | "dayTextField?": { 33 | "placeholder": "01", 34 | "keyboardType": "UIKeyboardTypeNumberPad", 35 | "": "dob_day", 36 | "": { 37 | "min_length": 1, 38 | "max_length": 2 39 | } 40 | }, 41 | "monthTextField?": { 42 | "placeholder": "11", 43 | "keyboardType": "UIKeyboardTypeNumberPad", 44 | "": "dob_month", 45 | "": { 46 | "min_length": 1, 47 | "max_length": 2 48 | } 49 | }, 50 | "yearTextField?": { 51 | "placeholder": "1990", 52 | "keyboardType": "UIKeyboardTypeNumberPad", 53 | "": "dob_year", 54 | "": { 55 | "equal_length": 4 56 | } 57 | }, 58 | "titleLabel": { 59 | "text": "Date of birth" 60 | } 61 | } 62 | }, 63 | { 64 | "": { 65 | "segmentedControl?": { 66 | "sectionTitle0": "Male", 67 | "sectionTitle1": "Female", 68 | "tintColor": "#6F3F83", 69 | "": 0, 70 | "": "gender", 71 | "": { 72 | "required": true 73 | } 74 | } 75 | } 76 | }, 77 | { 78 | "
": 1 79 | }, 80 | { 81 | "": { 82 | "textField?": { 83 | "placeholder": "Klundert", 84 | "": "city", 85 | "": { 86 | "required": true 87 | } 88 | }, 89 | "titleLabel": { 90 | "text": "City" 91 | } 92 | } 93 | }, 94 | { 95 | "<= + =>": { 96 | "textField?": { 97 | "placeholder": "Prins Willemstraat 1", 98 | "": "address", 99 | "": { 100 | "required": true 101 | } 102 | }, 103 | "titleLabel": { 104 | "text": "Address" 105 | } 106 | } 107 | }, 108 | { 109 | "<= + =>": { 110 | "textField?": { 111 | "placeholder": "+31 6 12345678", 112 | "keyboardType": "UIKeyboardTypePhonePad", 113 | "": "phone", 114 | "": { 115 | "required": true 116 | } 117 | }, 118 | "titleLabel": { 119 | "text": "Phone" 120 | } 121 | } 122 | }, 123 | { 124 | "<= + =>": { 125 | "textField?": { 126 | "placeholder": "thomas.bakker@domain.com", 127 | "keyboardType": "UIKeyboardTypeEmailAddress", 128 | "": "email", 129 | "": { 130 | "type": "email" 131 | } 132 | }, 133 | "titleLabel": { 134 | "text": "Email" 135 | } 136 | } 137 | }, 138 | { 139 | "
": 2 140 | }, 141 | { 142 | "": { 143 | "stepperControl?": { 144 | "stepValue": 1, 145 | "tintColor": "#6F3F83", 146 | "": "children_count", 147 | "": "displayLabel", 148 | "": "0" 149 | }, 150 | "titleLabel": { 151 | "text": "Children" 152 | } 153 | } 154 | }, 155 | { 156 | "": { 157 | "switchControl?": { 158 | "onTintColor": "#6F3F83", 159 | "": true, 160 | "": "driver_license" 161 | }, 162 | "titleLabel": { 163 | "text": "Driver licence" 164 | } 165 | } 166 | }, 167 | { 168 | "": { 169 | "sliderControl?": { 170 | "stepValue": 1, 171 | "minimumValue": 0, 172 | "maximumValue": 100, 173 | "minimumTrackTintColor": "#6F3F83", 174 | "": 50, 175 | "": "test" 176 | }, 177 | "titleLabel": { 178 | "text": "Center it" 179 | } 180 | } 181 | }, 182 | { 183 | "
": 3 184 | }, 185 | { 186 | "": { 187 | "titleLabel": { 188 | "text": "Accept", 189 | "textColor": "#6F3F83" 190 | } 191 | } 192 | }, 193 | { 194 | "
": 4 195 | }, 196 | { 197 | "": { 198 | "imageView": { 199 | "image": "ic_turtle#6F3F83" 200 | }, 201 | "textLabel": { 202 | "text": "Colored turtle :)" 203 | } 204 | } 205 | } 206 | ] -------------------------------------------------------------------------------- /Example/Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 13.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Pavel Puzyrev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PureForm.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "PureForm" 3 | s.version = "0.0.2" 4 | s.summary = "Easy form creator based on JSON" 5 | s.homepage = "https://github.com/unboxme/PureForm" 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.author = { "Pavel Puzyrev" => "cannedapp@yahoo.com" } 8 | s.platform = :ios, '8.0' 9 | s.source = { :git => "https://github.com/unboxme/PureForm.git", :tag => s.version.to_s } 10 | 11 | s.source_files = 'PureForm/**/*.{h,m}' 12 | s.public_header_files = 'PureForm/**/*.h' 13 | 14 | s.framework = 'Foundation' 15 | s.requires_arc = true 16 | end 17 | -------------------------------------------------------------------------------- /PureForm/Categories/NSString+PureForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+PureForm.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | @interface NSString (PureForm) 27 | 28 | /** 29 | @return `YES` if the string is special PureForm key, otherwise `NO`. 30 | */ 31 | - (BOOL)pf_isSpecialKey; 32 | 33 | /** 34 | @return `YES` if the string contains cell class and identifier, otherwise `NO`. 35 | */ 36 | - (BOOL)pf_isCombinedKey; 37 | 38 | /** 39 | @return `YES` if the string has suffix, otherwise `NO`. 40 | */ 41 | - (BOOL)pf_hasSuffix; 42 | 43 | /** 44 | @return `YES` if the string has localized suffix, otherwise `NO`. 45 | */ 46 | - (BOOL)pf_hasLocalizedSuffix; 47 | 48 | /** 49 | @return `YES` if the string has form view suffix, otherwise `NO`. 50 | */ 51 | - (BOOL)pf_hasFormViewSuffix; 52 | 53 | /** 54 | @return The cell class name. 55 | */ 56 | - (NSString *)pf_cellClassName; 57 | 58 | /** 59 | @return The cell identifier. 60 | */ 61 | - (NSString *)pf_cellIdentifier; 62 | 63 | /** 64 | @return The string without suffix. 65 | */ 66 | - (NSString *)pf_suffixFreeString; 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /PureForm/Categories/NSString+PureForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+PureForm.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "NSString+PureForm.h" 25 | #import "PFConstants.h" 26 | 27 | @implementation NSString (PureForm) 28 | 29 | NSArray *availablePFSuffixes() { 30 | return @[PFSuffixFormView, PFSuffixForce, PFSuffixLocalized]; 31 | }; 32 | 33 | - (NSString *)pf_suffix { 34 | NSInteger suffixIndex = self.length - 1; 35 | 36 | if (suffixIndex < 1) { 37 | return nil; 38 | } 39 | 40 | return [self substringFromIndex:(NSUInteger) suffixIndex]; 41 | } 42 | 43 | - (NSString *)pf_clearString { 44 | NSString *forbiddenChars = [NSString stringWithFormat:@"%@%@", PFSpecialCharBegin, PFSpecialCharEnd]; 45 | NSCharacterSet *specialCharacterSet = [NSCharacterSet characterSetWithCharactersInString:forbiddenChars]; 46 | 47 | NSString *string = self; 48 | string = [string stringByReplacingOccurrencesOfString:@" " withString:@""]; 49 | string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 50 | string = [string stringByTrimmingCharactersInSet:specialCharacterSet]; 51 | 52 | return string; 53 | } 54 | 55 | #pragma mark - 56 | 57 | - (BOOL)pf_isSpecialKey { 58 | if (self.length < 3) { 59 | return NO; 60 | } 61 | 62 | NSString *firstChar = [self substringToIndex:1]; 63 | NSString *lastChar = [self substringFromIndex:self.length - 1]; 64 | 65 | return [firstChar isEqualToString:PFSpecialCharBegin] && [lastChar isEqualToString:PFSpecialCharEnd]; 66 | } 67 | 68 | - (BOOL)pf_isCombinedKey { 69 | return [[self pf_clearString] componentsSeparatedByString:PFSpecialCharPlus].count > 1; 70 | } 71 | 72 | - (BOOL)pf_hasSuffix { 73 | return [availablePFSuffixes() containsObject:[self pf_suffix]]; 74 | } 75 | 76 | - (BOOL)pf_hasLocalizedSuffix { 77 | return [[self pf_suffix] isEqualToString:PFSuffixLocalized]; 78 | } 79 | 80 | - (BOOL)pf_hasFormViewSuffix { 81 | return [[self pf_suffix] isEqualToString:PFSuffixFormView]; 82 | } 83 | 84 | - (NSString *)pf_cellClassName { 85 | return [[self pf_clearString] componentsSeparatedByString:PFSpecialCharPlus].firstObject; 86 | } 87 | 88 | - (NSString *)pf_cellIdentifier { 89 | return [[self pf_clearString] componentsSeparatedByString:PFSpecialCharPlus].lastObject; 90 | } 91 | 92 | - (NSString *)pf_suffixFreeString { 93 | if (![self pf_hasSuffix]) { 94 | return self; 95 | } 96 | 97 | return [[self pf_clearString] substringToIndex:self.length - 1]; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /PureForm/Categories/PFInputView+Validation.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFInputView+Validation.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFInputView.h" 25 | #import "PFValidator.h" 26 | #import "PFModel.h" 27 | 28 | @interface PFInputView (Validation) 29 | 30 | /** 31 | @param value The value for validation. 32 | @param number The array of PFModel objects from the section. 33 | @param index The index of the first param. 34 | @param force Use `YES` for a quick validation. Only validators with `!` will be used. 35 | @return The failed validation PFValidator object. 36 | */ 37 | - (PFValidator *)failedValidatorWithValue:(id)value sectionModels:(NSArray *)models modelIndex:(NSUInteger)index force:(BOOL)force; 38 | 39 | /** 40 | @param comparable The full `float` or `double`, wrapped into NSNumber value which will be corrected. 41 | @param number The `float` or `double` wrapped into NSNumber value without odd numbers. 42 | @return The double value without odd numbers. 43 | */ 44 | + (double)doubleWithComparableNumber:(NSNumber *)comparable number:(NSNumber *)number; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /PureForm/Categories/PFInputView+Validation.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFInputView+Validation.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFInputView+Validation.h" 25 | #import "PFConstants.h" 26 | 27 | @implementation PFInputView (Validation) 28 | 29 | - (PFValidator *)failedValidatorWithValue:(id)value sectionModels:(NSArray *)models modelIndex:(NSUInteger)index force:(BOOL)force { 30 | for (PFValidator *validator in self.validators) { 31 | if (force && !validator.isForceValidation) { 32 | continue; 33 | } 34 | 35 | switch (validator.type) { 36 | case PFValidatorTypeEqual: { 37 | if (![value isEqual:validator.value]) { 38 | return validator; 39 | } 40 | 41 | break; 42 | } 43 | case PFValidatorTypeEqualNext: { 44 | if (index >= models.count - 1) { 45 | break; 46 | } 47 | 48 | NSArray *nextInputViews = models[index + 1].inputViews; 49 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isFormView == YES"]; 50 | NSArray *results = [nextInputViews filteredArrayUsingPredicate:predicate]; 51 | 52 | if (results.count == 0) { 53 | break; 54 | } 55 | 56 | if (![results.firstObject.value isEqual:value]) { 57 | return validator; 58 | } 59 | 60 | break; 61 | } 62 | case PFValidatorTypeEqualPrevious: { 63 | if (index == 0) { 64 | break; 65 | } 66 | 67 | NSArray *nextInputViews = models[index - 1].inputViews; 68 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isFormView == YES"]; 69 | NSArray *results = [nextInputViews filteredArrayUsingPredicate:predicate]; 70 | 71 | if (results.count == 0) { 72 | break; 73 | } 74 | 75 | if (![results.firstObject.value isEqual:value]) { 76 | return validator; 77 | } 78 | 79 | break; 80 | } 81 | case PFValidatorTypeEqualLength: { 82 | if (!value && [validator.value integerValue] > 0) { 83 | return validator; 84 | } 85 | 86 | if ([value isKindOfClass:[NSString class]]) { 87 | NSString *string = value; 88 | if (string.length != [validator.value integerValue]) { 89 | return validator; 90 | } 91 | } 92 | 93 | break; 94 | } 95 | case PFValidatorTypeMinLength: { 96 | if (!value && [validator.value integerValue] > 0) { 97 | return validator; 98 | } 99 | 100 | if ([value isKindOfClass:[NSString class]]) { 101 | NSString *string = value; 102 | if (string.length < [validator.value integerValue]) { 103 | return validator; 104 | } 105 | } 106 | 107 | break; 108 | } 109 | case PFValidatorTypeMaxLength: { 110 | if ([value isKindOfClass:[NSString class]]) { 111 | NSString *string = value; 112 | if (string.length > [validator.value integerValue]) { 113 | return validator; 114 | } 115 | } 116 | 117 | break; 118 | } 119 | case PFValidatorTypeMinValue: { 120 | if ([value isKindOfClass:[NSNumber class]]) { 121 | if ([PFInputView doubleWithComparableNumber:value number:validator.value] < [validator.value doubleValue]) { 122 | return validator; 123 | } 124 | } 125 | 126 | break; 127 | } 128 | case PFValidatorTypeMaxValue: { 129 | if ([value isKindOfClass:[NSNumber class]]) { 130 | if ([PFInputView doubleWithComparableNumber:value number:validator.value] > [validator.value doubleValue]) { 131 | return validator; 132 | } 133 | } 134 | 135 | break; 136 | } 137 | case PFValidatorTypeRequired: { 138 | if (!value) { 139 | return validator; 140 | } 141 | 142 | if ([value isKindOfClass:[NSString class]]) { 143 | NSString *string = value; 144 | if ([string isEqualToString:@""]) { 145 | return validator; 146 | } 147 | } 148 | 149 | break; 150 | } 151 | case PFValidatorTypeFormType: 152 | case PFValidatorTypeCustom: { 153 | if (![value isKindOfClass:[NSString class]] || ![validator.value isKindOfClass:[NSString class]]) { 154 | return validator; 155 | } 156 | 157 | NSString *regexString; 158 | if ([validator.value isEqualToString:PFValidationTypeEmailKey]) { 159 | regexString = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"; 160 | } else if ([validator.value isEqualToString:PFValidationTypeNameKey]) { 161 | regexString = @"^[a-z]([-']?[a-z]+)*( [a-z]([-']?[a-z]+)*)+$"; 162 | } else { 163 | regexString = validator.value; 164 | } 165 | 166 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexString]; 167 | if (![predicate evaluateWithObject:value]) { 168 | return validator; 169 | } 170 | 171 | break; 172 | } 173 | case PFValidatorTypeUnknown: { 174 | break; 175 | } 176 | } 177 | } 178 | 179 | return nil; 180 | } 181 | 182 | + (double)doubleWithComparableNumber:(NSNumber *)comparable number:(NSNumber *)number { 183 | NSString *fraction = [[NSString stringWithFormat:@"%@", number] 184 | componentsSeparatedByCharactersInSet:[NSCharacterSet punctuationCharacterSet]].lastObject; 185 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 186 | [formatter setRoundingMode:NSNumberFormatterRoundHalfUp]; 187 | [formatter setMaximumFractionDigits:fraction.length]; 188 | 189 | return [[formatter stringFromNumber:comparable] doubleValue]; 190 | } 191 | 192 | @end 193 | -------------------------------------------------------------------------------- /PureForm/Categories/PFValidator+FailureReason.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFValidator+FailureReason.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFValidator.h" 25 | 26 | @interface PFValidator (FailureReason) 27 | 28 | /** 29 | @param failureReasons The user dictionary of custom validation failure reasons. 30 | @return The failure reason string for current validator. 31 | */ 32 | - (NSString *)failureReasonWithUserReasons:(NSDictionary *)failureReasons; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /PureForm/Categories/PFValidator+FailureReason.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFValidator+FailureReason.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFValidator+FailureReason.h" 25 | #import "PFSettings.h" 26 | 27 | @implementation PFValidator (FailureReason) 28 | 29 | - (NSString *)failureReasonWithUserReasons:(NSDictionary *)failureReasons { 30 | static NSDictionary *defaultReasons = nil; 31 | static NSArray *defaultReasonsArray = nil; 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | defaultReasons = @{ 35 | PFValidationEqualFailureKey : @"Value doesn't match expected one (%@)", 36 | PFValidationEqualNextFailureKey : @"Value doesn't match next one", 37 | PFValidationEqualPreviousFailureKey : @"Value doesn't match previous one", 38 | PFValidationEqualLengthFailureKey : @"Value length doesn't match expected one (%@)", 39 | PFValidationMinLengthFailureKey : @"String length is too short, than expected (%@)", 40 | PFValidationMaxLengthFailureKey : @"String length is too long, than expected (%@)", 41 | PFValidationMinValueFailureKey : @"Value is too small, than expected (%@)", 42 | PFValidationMaxValueFailureKey : @"Value is too big, than expected (%@)", 43 | PFValidationRequiredFailureKey : @"Value is required", 44 | PFValidationTypeFailureKey : @"Value doesn't match %@ format", 45 | PFValidationCustomFailureKey : @"Check value you entered", 46 | PFValidationUnknownFailureKey : @"" 47 | }; 48 | 49 | defaultReasonsArray = @[ 50 | PFValidationEqualFailureKey, 51 | PFValidationEqualNextFailureKey, 52 | PFValidationEqualPreviousFailureKey, 53 | PFValidationEqualLengthFailureKey, 54 | PFValidationMinLengthFailureKey, 55 | PFValidationMaxLengthFailureKey, 56 | PFValidationMinValueFailureKey, 57 | PFValidationMaxValueFailureKey, 58 | PFValidationRequiredFailureKey, 59 | PFValidationTypeFailureKey, 60 | PFValidationCustomFailureKey, 61 | PFValidationUnknownFailureKey 62 | ]; 63 | }); 64 | 65 | NSString *value = [self.value isKindOfClass:[NSNumber class]] ? [self.value stringValue] : self.value; 66 | NSString *key = defaultReasonsArray[self.type]; 67 | NSString *reason = [failureReasons.allKeys containsObject:key] ? failureReasons[key] : defaultReasons[key]; 68 | reason = [reason stringByReplacingOccurrencesOfString:@"%@" withString:value]; 69 | 70 | return reason; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /PureForm/Categories/UIColor+PureForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+PureForm.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | @interface UIColor (PureForm) 27 | 28 | /** 29 | @param The hex code string; 30 | @return The color object from hex. 31 | */ 32 | + (UIColor *)pf_colorWithHexString:(NSString *)hexString; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /PureForm/Categories/UIColor+PureForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+PureForm.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 23.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "UIColor+PureForm.h" 10 | #import "PFConstants.h" 11 | 12 | @implementation UIColor (PureForm) 13 | 14 | + (UIColor *)pf_colorWithHexString:(NSString *)hexString { 15 | unsigned rgbValue = 0; 16 | 17 | NSScanner *scanner = [NSScanner scannerWithString:hexString]; 18 | [scanner setScanLocation:[hexString containsString:PFSpecialCharHex] ? 1 : 0]; 19 | [scanner scanHexInt:&rgbValue]; 20 | 21 | return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.f 22 | green:((rgbValue & 0xFF00) >> 8) / 255.f 23 | blue:(rgbValue & 0xFF) / 255.f 24 | alpha:1.0]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /PureForm/Categories/UISlider+PureForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // UISlider+PureForm.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | @interface UISlider (PureForm) 27 | 28 | /** 29 | The UIStepper-like step value. 30 | */ 31 | @property(assign, nonatomic) CGFloat pf_stepValue; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /PureForm/Categories/UISlider+PureForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // UISlider+PureForm.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "UISlider+PureForm.h" 25 | #import 26 | 27 | static void *kStepValueRuntimePropertyKey = &kStepValueRuntimePropertyKey; 28 | 29 | @implementation UISlider (PureForm) 30 | 31 | - (CGFloat)pf_stepValue { 32 | return [objc_getAssociatedObject(self, kStepValueRuntimePropertyKey) floatValue]; 33 | } 34 | 35 | - (void)setPf_stepValue:(CGFloat)pf_stepValue { 36 | objc_setAssociatedObject(self, kStepValueRuntimePropertyKey, @(pf_stepValue), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /PureForm/Categories/UITableViewCell+PureForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+PureForm.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | @interface UITableViewCell (PureForm) 27 | 28 | /** 29 | @param value The value which will be set. 30 | @param inputView The cell property name. 31 | */ 32 | - (void)setValue:(id)value forPropertyName:(id)inputView; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /PureForm/Categories/UITableViewCell+PureForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+PureForm.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "UITableViewCell+PureForm.h" 25 | #import 26 | #import "PFEnums.h" 27 | #import "PFConstants.h" 28 | #import "PFProtocols.h" 29 | #import "UIView+PureForm.h" 30 | #import "PFInputView+Validation.h" 31 | #import "UISlider+PureForm.h" 32 | #import "UIColor+PureForm.h" 33 | 34 | @implementation UITableViewCell (PureForm) 35 | 36 | - (void)setValue:(id)value forPropertyName:(id)inputView { 37 | id inputViewObject = [self valueForKey:inputView]; 38 | 39 | if ([inputViewObject respondsToSelector:@selector(text)]) { 40 | id dInputViewObject = inputViewObject; 41 | dInputViewObject.text = value; 42 | } 43 | 44 | if ([inputViewObject isKindOfClass:[UISegmentedControl class]]) { 45 | UISegmentedControl *segmentedControl = inputViewObject; 46 | segmentedControl.selectedSegmentIndex = [value integerValue]; 47 | } 48 | 49 | if ([inputViewObject isKindOfClass:[UISwitch class]]) { 50 | UISwitch *switchControl = inputViewObject; 51 | switchControl.on = [value boolValue]; 52 | } 53 | 54 | if ([inputViewObject isKindOfClass:[UIStepper class]]) { 55 | UIStepper *stepperControl = inputViewObject; 56 | 57 | double dValue = [PFInputView doubleWithComparableNumber:value number:@(stepperControl.stepValue)]; 58 | stepperControl.value = dValue; 59 | stepperControl.pf_display.text = [NSString stringWithFormat:@"%@", @(dValue)]; 60 | } 61 | 62 | if ([inputViewObject isKindOfClass:[UISlider class]]) { 63 | UISlider *sliderControl = inputViewObject; 64 | 65 | sliderControl.value = [value floatValue]; 66 | sliderControl.pf_display.text = [value stringValue]; 67 | } 68 | } 69 | 70 | - (void)setValue:(id)value forKeyPath:(NSString *)keyPath { 71 | NSString *propertyName = [keyPath componentsSeparatedByString:@"."].firstObject; 72 | NSString *paramName = [keyPath componentsSeparatedByString:@"."].lastObject; 73 | id view = [self valueForKey:propertyName]; 74 | 75 | Class propertyClass = [[self valueForKeyPath:propertyName] class]; 76 | Class paramClass = [self classOfPropertyNamed:paramName withOwnerClass:propertyClass]; 77 | 78 | if (paramClass == [UIImage class]) { 79 | UIImage *image = [UIImage imageNamed:value]; 80 | if ([value containsString:PFSpecialCharHex]) { 81 | NSArray *valueComponents = [value componentsSeparatedByString:PFSpecialCharHex]; 82 | image = [UIImage imageNamed:valueComponents.firstObject]; 83 | image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 84 | [view setTintColor:[UIColor pf_colorWithHexString:valueComponents.lastObject]]; 85 | } 86 | 87 | [super setValue:image forKeyPath:keyPath]; 88 | 89 | return; 90 | } 91 | 92 | if (paramClass == [UIColor class]) { 93 | [super setValue:[UIColor pf_colorWithHexString:value] forKeyPath:keyPath]; 94 | 95 | return; 96 | } 97 | 98 | #pragma mark - UITextInputTraits 99 | 100 | //TODO: I'm not sure that it's a good solution, but it works, hm 101 | if ([view conformsToProtocol:@protocol(UITextInputTraits)]) { 102 | NSDictionary *specialInputTraitsCases = @{ 103 | [NSString stringWithFormat:@"%@.autocapitalizationType", propertyName] : ^(UITextField *t, NSInteger v) { 104 | t.autocapitalizationType = (UITextAutocapitalizationType) v; 105 | }, 106 | [NSString stringWithFormat:@"%@.autocorrectionType", propertyName] : ^(UITextField *t, NSInteger v) { 107 | t.autocorrectionType = (UITextAutocorrectionType) v; 108 | }, 109 | [NSString stringWithFormat:@"%@.spellCheckingType", propertyName] : ^(UITextField *t, NSInteger v) { 110 | t.spellCheckingType = (UITextSpellCheckingType) v; 111 | }, 112 | [NSString stringWithFormat:@"%@.keyboardType", propertyName] : ^(UITextField *t, NSInteger v) { 113 | t.keyboardType = (UIKeyboardType) v; 114 | }, 115 | [NSString stringWithFormat:@"%@.keyboardAppearance", propertyName] : ^(UITextField *t, NSInteger v) { 116 | t.keyboardAppearance = (UIKeyboardAppearance) v; 117 | }, 118 | [NSString stringWithFormat:@"%@.returnKeyType", propertyName] : ^(UITextField *t, NSInteger v) { 119 | t.returnKeyType = (UIReturnKeyType) v; 120 | }}; 121 | 122 | NSDictionary *specialBoolCases = @{ 123 | [NSString stringWithFormat:@"%@.enablesReturnKeyAutomatically", propertyName] : ^(UITextField *t, BOOL v) { 124 | t.enablesReturnKeyAutomatically = v; 125 | }, 126 | [NSString stringWithFormat:@"%@.secureTextEntry", propertyName] : ^(UITextField *t, BOOL v) { 127 | t.secureTextEntry = v; 128 | }}; 129 | 130 | if ([specialInputTraitsCases.allKeys containsObject:keyPath]) { 131 | void (^block)(UITextField *f, NSInteger v) = specialInputTraitsCases[keyPath]; 132 | block(view, [PFEnums enumFromValue:value]); 133 | 134 | return; 135 | } 136 | 137 | if ([specialBoolCases.allKeys containsObject:keyPath]) { 138 | void (^block)(UITextField *f, BOOL v) = specialBoolCases[keyPath]; 139 | block(view, [value boolValue]); 140 | 141 | return; 142 | } 143 | } 144 | 145 | #pragma mark - UISegmentedControl 146 | 147 | if ([view isKindOfClass:[UISegmentedControl class]]) { 148 | NSCharacterSet *notNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; 149 | NSUInteger index = (NSUInteger) [[paramName stringByTrimmingCharactersInSet:notNumbers] integerValue]; 150 | 151 | UISegmentedControl *segmentedControl = view; 152 | 153 | if ([paramName containsString:PFSegmentedControlSectionTitleKey]) { 154 | [segmentedControl removeSegmentAtIndex:index animated:NO]; 155 | [segmentedControl insertSegmentWithTitle:value atIndex:index animated:NO]; 156 | 157 | return; 158 | } 159 | 160 | if ([paramName containsString:PFSegmentedControlSectionImageKey]) { 161 | [segmentedControl removeSegmentAtIndex:index animated:NO]; 162 | [segmentedControl insertSegmentWithImage:[UIImage imageNamed:value] atIndex:index animated:NO]; 163 | 164 | return; 165 | } 166 | } 167 | 168 | if ([view isKindOfClass:[UISwitch class]]) { 169 | UISwitch *switchControl = view; 170 | switchControl.on = [value boolValue]; 171 | 172 | return; 173 | } 174 | 175 | if ([view isKindOfClass:[UISlider class]] && [paramName isEqualToString:PFSliderControlStepValueKey]) { 176 | UISlider *sliderControl = view; 177 | sliderControl.pf_stepValue = [value floatValue]; 178 | 179 | return; 180 | } 181 | 182 | [super setValue:value forKeyPath:keyPath]; 183 | } 184 | 185 | - (Class)classOfPropertyNamed:(NSString *)propertyName withOwnerClass:(Class)ownerClass { 186 | if (ownerClass == NULL) { 187 | return NULL; 188 | } 189 | 190 | Class propertyClass = nil; 191 | objc_property_t property = class_getProperty(ownerClass, [propertyName UTF8String]); 192 | 193 | if (property == NULL) { 194 | return NULL; 195 | } 196 | 197 | NSString *propertyAttributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding]; 198 | NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@","]; 199 | 200 | if (splitPropertyAttributes.count > 0) { 201 | NSString *encodeType = splitPropertyAttributes[0]; 202 | NSArray *splitEncodeType = [encodeType componentsSeparatedByString:@"\""]; 203 | NSString *className = splitEncodeType.count > 1 ? splitEncodeType[1] : nil; 204 | propertyClass = NSClassFromString(className); 205 | } 206 | 207 | return propertyClass; 208 | } 209 | 210 | @end 211 | -------------------------------------------------------------------------------- /PureForm/Categories/UIView+PureForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+PureForm.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import "PFProtocols.h" 26 | 27 | @interface UIView (PureForm) 28 | 29 | /** 30 | The section index of the table view section where current view is. 31 | */ 32 | @property(assign, nonatomic) NSUInteger pf_section; 33 | 34 | /** 35 | The row index of the table view row where current view is. 36 | */ 37 | @property(assign, nonatomic) NSUInteger pf_index; 38 | 39 | /** 40 | The UIView-like tag. 41 | */ 42 | @property(assign, nonatomic) NSUInteger pf_tag; 43 | 44 | /** 45 | The object with a `text` property, such as UILabel, UITextView, etc. 46 | */ 47 | @property(strong, nonatomic) id pf_display; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /PureForm/Categories/UIView+PureForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+PureForm.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "UIView+PureForm.h" 25 | #import 26 | 27 | static void *kIndexRuntimePropertyKey = &kIndexRuntimePropertyKey; 28 | static void *kSectionRuntimePropertyKey = &kSectionRuntimePropertyKey; 29 | static void *kTagRuntimePropertyKey = &kTagRuntimePropertyKey; 30 | static void *kDisplayRuntimePropertyKey = &kDisplayRuntimePropertyKey; 31 | 32 | @implementation UIView (PureForm) 33 | 34 | - (NSUInteger)pf_section { 35 | return [objc_getAssociatedObject(self, kSectionRuntimePropertyKey) unsignedIntegerValue]; 36 | } 37 | 38 | - (void)setPf_section:(NSUInteger)pf_section { 39 | objc_setAssociatedObject(self, kSectionRuntimePropertyKey, @(pf_section), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 40 | } 41 | 42 | - (NSUInteger)pf_index { 43 | return [objc_getAssociatedObject(self, kIndexRuntimePropertyKey) unsignedIntegerValue]; 44 | } 45 | 46 | - (void)setPf_index:(NSUInteger)pf_index { 47 | objc_setAssociatedObject(self, kIndexRuntimePropertyKey, @(pf_index), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 48 | } 49 | 50 | - (NSUInteger)pf_tag { 51 | return [objc_getAssociatedObject(self, kTagRuntimePropertyKey) unsignedIntegerValue]; 52 | } 53 | 54 | - (void)setPf_tag:(NSUInteger)pf_tag { 55 | objc_setAssociatedObject(self, kTagRuntimePropertyKey, @(pf_tag), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 56 | } 57 | 58 | - (id )pf_display { 59 | return objc_getAssociatedObject(self, kDisplayRuntimePropertyKey); 60 | } 61 | 62 | - (void)setPf_display:(id )pf_display { 63 | objc_setAssociatedObject(self, kDisplayRuntimePropertyKey, pf_display, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 64 | } 65 | 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /PureForm/Common/PFConstants.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFConstants.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | #pragma mark - NSNotification 27 | 28 | static NSString *const PFDisplayLabelShouldChangeNotification = @"PFDisplayLabelShouldChangeNotification"; 29 | 30 | #pragma mark - JSON Keys 31 | 32 | static NSString *const PFSectionKey = @"
"; 33 | static NSString *const PFValueKey = @""; 34 | static NSString *const PFKeyKey = @""; 35 | static NSString *const PFValidationKey = @""; 36 | static NSString *const PFDisplayKey = @""; 37 | 38 | static NSString *const PFValidationEqualKey = @"equal"; 39 | static NSString *const PFValidationEqualNextKey = @"equal_next"; 40 | static NSString *const PFValidationEqualPreviousKey = @"equal_previous"; 41 | static NSString *const PFValidationEqualLengthKey = @"equal_length"; 42 | static NSString *const PFValidationMinLengthKey = @"min_length"; 43 | static NSString *const PFValidationMaxLengthKey = @"max_length"; 44 | static NSString *const PFValidationMinValueKey = @"min_value"; 45 | static NSString *const PFValidationMaxValueKey = @"max_value"; 46 | static NSString *const PFValidationRequiredKey = @"required"; 47 | static NSString *const PFValidationTypeKey = @"type"; 48 | static NSString *const PFValidationCustomKey = @"custom"; 49 | 50 | static NSString *const PFValidationTypeEmailKey = @"email"; 51 | static NSString *const PFValidationTypeNameKey = @"full_name"; 52 | 53 | #pragma mark - Special Chars 54 | 55 | static NSString *const PFSpecialCharBegin = @"<"; 56 | static NSString *const PFSpecialCharEnd = @">"; 57 | static NSString *const PFSpecialCharPlus = @"+"; 58 | static NSString *const PFSpecialCharSame = @"="; 59 | static NSString *const PFSpecialCharHex = @"#"; 60 | 61 | static NSString *const PFSuffixForce = @"!"; 62 | static NSString *const PFSuffixFormView = @"?"; 63 | static NSString *const PFSuffixLocalized = @"*"; 64 | 65 | static NSString *const PFUnknownKey = @"unknown_key"; 66 | 67 | #pragma mark - Available Chars 68 | 69 | static NSString *const PFPhonePadCharacters = @"+*#0123456789"; 70 | static NSString *const PFNumberPadCharacters = @"0123456789"; 71 | static NSString *const PFDecimalPadCharacters = @".,0123456789"; 72 | 73 | #pragma mark - Custom Controls 74 | 75 | static NSString *const PFSegmentedControlSectionTitleKey = @"sectionTitle"; 76 | static NSString *const PFSegmentedControlSectionImageKey = @"sectionImage"; 77 | static NSString *const PFSliderControlStepValueKey = @"stepValue"; 78 | -------------------------------------------------------------------------------- /PureForm/Common/PFEnums.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFEnums.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | typedef NS_ENUM(NSUInteger, PFValidatorType) { 27 | PFValidatorTypeEqual, 28 | PFValidatorTypeEqualNext, 29 | PFValidatorTypeEqualPrevious, 30 | PFValidatorTypeEqualLength, 31 | PFValidatorTypeMinLength, 32 | PFValidatorTypeMaxLength, 33 | PFValidatorTypeMinValue, 34 | PFValidatorTypeMaxValue, 35 | PFValidatorTypeRequired, 36 | PFValidatorTypeFormType, 37 | PFValidatorTypeCustom, 38 | PFValidatorTypeUnknown 39 | }; 40 | 41 | @interface PFEnums : NSObject 42 | 43 | /** 44 | @param string The string enum value. 45 | @return The enum value. 46 | */ 47 | + (NSInteger)enumFromValue:(id)value; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /PureForm/Common/PFEnums.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFEnums.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFEnums.h" 25 | 26 | #import "NSString+PureForm.h" 27 | #import "PFConstants.h" 28 | 29 | @implementation PFEnums 30 | 31 | + (NSInteger)enumFromValue:(id)value { 32 | if ([value isKindOfClass:[NSNumber class]]) { 33 | return [value integerValue]; 34 | } 35 | 36 | value = [value pf_suffixFreeString]; 37 | 38 | NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"ANY SELF == %@", value]; 39 | NSArray *results = [[self enumValues] filteredArrayUsingPredicate:searchPredicate]; 40 | 41 | if (results.count > 0) { 42 | return (NSInteger) [results.firstObject indexOfObject:value]; 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | + (NSArray *)enumValues { 49 | static NSArray *enumValues = nil; 50 | static dispatch_once_t onceToken; 51 | dispatch_once(&onceToken, ^{ 52 | 53 | #pragma mark - PureForm 54 | 55 | NSArray *PFValidationArray = @[ 56 | PFValidationEqualKey, 57 | PFValidationEqualNextKey, 58 | PFValidationEqualPreviousKey, 59 | PFValidationEqualLengthKey, 60 | PFValidationMinLengthKey, 61 | PFValidationMaxLengthKey, 62 | PFValidationMinValueKey, 63 | PFValidationMaxValueKey, 64 | PFValidationRequiredKey, 65 | PFValidationTypeKey, 66 | PFValidationCustomKey, 67 | ]; 68 | 69 | #pragma mark - UITextInputTraits 70 | 71 | NSArray *UITextAutocapitalizationTypeArray = @[ 72 | @"UITextAutocapitalizationTypeNone", 73 | @"UITextAutocapitalizationTypeWords", 74 | @"UITextAutocapitalizationTypeSentences", 75 | @"UITextAutocapitalizationTypeAllCharacters" 76 | ]; 77 | 78 | NSArray *UITextAutocorrectionTypeArray = @[ 79 | @"UITextAutocorrectionTypeDefault", 80 | @"UITextAutocorrectionTypeNo", 81 | @"UITextAutocorrectionTypeYes" 82 | ]; 83 | 84 | NSArray *UITextSpellCheckingTypeArray = @[ 85 | @"UITextSpellCheckingTypeDefault", 86 | @"UITextSpellCheckingTypeNo", 87 | @"UITextSpellCheckingTypeYes" 88 | ]; 89 | 90 | NSArray *UIKeyboardTypeArray = @[ 91 | @"UIKeyboardTypeDefault", 92 | @"UIKeyboardTypeASCIICapable", 93 | @"UIKeyboardTypeNumbersAndPunctuation", 94 | @"UIKeyboardTypeURL", 95 | @"UIKeyboardTypeNumberPad", 96 | @"UIKeyboardTypePhonePad", 97 | @"UIKeyboardTypeNamePhonePad", 98 | @"UIKeyboardTypeEmailAddress", 99 | @"UIKeyboardTypeDecimalPad", 100 | @"UIKeyboardTypeTwitter", 101 | @"UIKeyboardTypeWebSearch", 102 | @"UIKeyboardTypeAlphabet" 103 | ]; 104 | 105 | NSArray *UIKeyboardAppearanceArray = @[ 106 | @"UIKeyboardAppearanceDefault", 107 | @"UIKeyboardAppearanceDark", 108 | @"UIKeyboardAppearanceLight", 109 | @"UIKeyboardAppearanceAlert" 110 | ]; 111 | 112 | NSArray *UIReturnKeyTypeArray = @[ 113 | @"UIReturnKeyDefault", 114 | @"UIReturnKeyGo", 115 | @"UIReturnKeyGoogle", 116 | @"UIReturnKeyJoin", 117 | @"UIReturnKeyNext", 118 | @"UIReturnKeyRoute", 119 | @"UIReturnKeySearch", 120 | @"UIReturnKeySend", 121 | @"UIReturnKeyYahoo", 122 | @"UIReturnKeyDone", 123 | @"UIReturnKeyEmergencyCall", 124 | @"UIReturnKeyContinue" 125 | ]; 126 | 127 | #pragma mark - UITextField 128 | 129 | NSArray *UITextFieldViewModeArray = @[ 130 | @"UITextFieldViewModeNever", 131 | @"UITextFieldViewModeWhileEditing", 132 | @"UITextFieldViewModeUnlessEditing", 133 | @"UITextFieldViewModeAlways" 134 | ]; 135 | 136 | #pragma mark - Output 137 | 138 | enumValues = @[ 139 | PFValidationArray, 140 | UITextAutocapitalizationTypeArray, 141 | UITextAutocorrectionTypeArray, 142 | UITextSpellCheckingTypeArray, 143 | UIKeyboardTypeArray, 144 | UIKeyboardAppearanceArray, 145 | UIReturnKeyTypeArray, 146 | UITextFieldViewModeArray 147 | ]; 148 | 149 | }); 150 | 151 | return enumValues; 152 | } 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /PureForm/Common/PFMacros.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFMacros.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef PFMacros_h 25 | #define PFMacros_h 26 | 27 | #ifdef DEBUG 28 | # define PFLog(format, ...) NSLog((@"PureForm: " format @", occurred in: %s [Line %d]"), ##__VA_ARGS__, __PRETTY_FUNCTION__, __LINE__) 29 | #else 30 | # define PFLog(...) 31 | #endif 32 | 33 | #endif /* PFMacros_h */ 34 | -------------------------------------------------------------------------------- /PureForm/Common/PFProtocols.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFProtocols.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | @protocol PFDelegate 27 | 28 | @required 29 | @property(weak, nonatomic) id delegate; 30 | 31 | @end 32 | 33 | @protocol PFText 34 | 35 | @required 36 | @property(copy, nonatomic) NSString *text; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /PureForm/Delegates/PFFormDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFFormDelegate.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import "PFModel.h" 26 | #import "PFError.h" 27 | 28 | @protocol PFFormDelegate 29 | 30 | @optional 31 | /** 32 | Called when a value was successfully updated. 33 | */ 34 | - (void)valueDidUpdate; 35 | 36 | /** 37 | Called when a value was successfully updated. 38 | @param inputView The updated PFInputView object. 39 | */ 40 | - (void)valueDidUpdateWithInputView:(PFInputView *)inputView; 41 | 42 | /** 43 | Called when a value was updated with a validation error. 44 | @param error The validation error. 45 | */ 46 | - (void)valueDidUpdateWithError:(PFError *)error; 47 | 48 | /** 49 | Called when values successfully passed a validation. 50 | */ 51 | - (void)validationDidEndSuccessfully; 52 | 53 | /** 54 | Called when values failed a validation. 55 | @param errors The array of validation errors. 56 | */ 57 | - (void)validationDidFailWithErrors:(NSArray *)errors; 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /PureForm/Delegates/PFInputViewDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFInputViewDelegate.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import 26 | 27 | @class PFModel; 28 | @class PFSettings; 29 | 30 | @interface PFInputViewDelegate : NSObject 31 | 32 | /** 33 | @return The instance of PFInputViewDelegate. 34 | */ 35 | - (instancetype)initWithModels:(NSMutableArray *> *)models tableView:(UITableView *)tableView settings:(PFSettings *)settings; 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /PureForm/Delegates/PFInputViewDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFInputViewDelegate.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFInputViewDelegate.h" 25 | 26 | #import 27 | #import "PFModel.h" 28 | #import "UIView+PureForm.h" 29 | #import "PFSettings.h" 30 | #import "PFInputView+Validation.h" 31 | #import "PFValidator+FailureReason.h" 32 | #import "PFConstants.h" 33 | #import "UISlider+PureForm.h" 34 | 35 | #define PFFormDelegate self.settings.formDelegate 36 | #define PFTextFieldDelegate self.settings.textFieldDelegate 37 | #define PFSegmentedControlDelegate self.settings.segmentedControlDelegate 38 | #define PFSwitchControlDelegate self.settings.switchControlDelegate 39 | #define PFSliderControlDelegate self.settings.sliderControlDelegate 40 | #define PFStepperControlDelegate self.settings.stepperControlDelegate 41 | 42 | static void *PFCurrentInputViewPropertyKey = &PFCurrentInputViewPropertyKey; 43 | static void *PFOriginalInputViewPropertyKey = &PFOriginalInputViewPropertyKey; 44 | 45 | @interface PFInputViewDelegate () 46 | 47 | @property(weak, nonatomic) PFSettings *settings; 48 | @property(weak, nonatomic) NSMutableArray *> *models; 49 | 50 | @property(strong, nonatomic) PFInputView *currentInputView; 51 | @property(strong, nonatomic) id originalValue; 52 | @property(strong, nonatomic) UITableView *tableView; 53 | @property(strong, nonatomic) NSValue *showedKeyboardFrame; 54 | 55 | @end 56 | 57 | @implementation PFInputViewDelegate 58 | 59 | - (PFInputView *)currentInputView { 60 | return objc_getAssociatedObject(self, PFCurrentInputViewPropertyKey); 61 | } 62 | 63 | - (void)setCurrentInputView:(PFInputView *)currentInputView { 64 | objc_setAssociatedObject(self, PFCurrentInputViewPropertyKey, currentInputView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 65 | } 66 | 67 | - (id)originalValue { 68 | return objc_getAssociatedObject(self, PFOriginalInputViewPropertyKey); 69 | } 70 | 71 | - (void)setOriginalValue:(id)originalValue { 72 | objc_setAssociatedObject(self, PFOriginalInputViewPropertyKey, originalValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 73 | } 74 | 75 | - (instancetype)initWithModels:(NSMutableArray *> *)models tableView:(UITableView *)tableView settings:(PFSettings *)settings { 76 | self = [super init]; 77 | 78 | if (self) { 79 | _tableView = tableView; 80 | _settings = settings; 81 | _models = models; 82 | 83 | if (settings.isKeyboardAvoiding) { 84 | [[NSNotificationCenter defaultCenter] addObserver:self 85 | selector:@selector(keyboardWillShow:) 86 | name:UIKeyboardWillShowNotification 87 | object:nil]; 88 | [[NSNotificationCenter defaultCenter] addObserver:self 89 | selector:@selector(keyboardWillHide:) 90 | name:UIKeyboardWillHideNotification 91 | object:nil]; 92 | } 93 | } 94 | 95 | return self; 96 | } 97 | 98 | - (void)dealloc { 99 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 100 | } 101 | 102 | - (void)searchAndAssignCurrentInputViewByView:(UIView *)view { 103 | self.currentInputView = nil; 104 | 105 | for (NSArray *array in self.models) { 106 | NSPredicate *modelPredicate = [NSPredicate predicateWithFormat:@"ANY inputViews.view == %@", view]; 107 | NSArray *results = [array filteredArrayUsingPredicate:modelPredicate]; 108 | 109 | if (results.count > 0) { 110 | PFModel *model = results.firstObject; 111 | NSPredicate *inputViewPredicate = [NSPredicate predicateWithFormat:@"view == %@", view]; 112 | 113 | self.currentInputView = [model.inputViews filteredArrayUsingPredicate:inputViewPredicate].firstObject; 114 | self.originalValue = self.currentInputView.value; 115 | } 116 | } 117 | } 118 | 119 | #pragma mark - PFSegmentedControlDelegate, PFSwitchControlDelegate, PFSliderControlDelegate, PFStepperControlDelegate 120 | 121 | - (void)sliderControlDidEndEditing:(id)controlView { 122 | UISlider *sliderControl = controlView; 123 | [self makeValidationWithSectionModels:nil modelIndex:0]; 124 | 125 | if (PFSliderControlDelegate && 126 | [PFSliderControlDelegate respondsToSelector:@selector(sliderControl:didChangeValue:)]) { 127 | [PFSliderControlDelegate sliderControl:sliderControl didChangeValue:sliderControl.value]; 128 | } 129 | } 130 | 131 | - (void)controlViewDidChangeValue:(id)controlView { 132 | [self searchAndAssignCurrentInputViewByView:controlView]; 133 | 134 | if ([controlView isKindOfClass:[UISegmentedControl class]]) { 135 | UISegmentedControl *segmentedControl = controlView; 136 | self.currentInputView.value = @(segmentedControl.selectedSegmentIndex); 137 | [self makeValidationWithSectionModels:nil modelIndex:0]; 138 | 139 | if (PFSegmentedControlDelegate && 140 | [PFSegmentedControlDelegate respondsToSelector:@selector(segmentedControl:didSelectItemAtIndex:)]) { 141 | [PFSegmentedControlDelegate segmentedControl:segmentedControl 142 | didSelectItemAtIndex:segmentedControl.selectedSegmentIndex]; 143 | } 144 | } 145 | 146 | if ([controlView isKindOfClass:[UISwitch class]]) { 147 | UISwitch *switchControl = controlView; 148 | self.currentInputView.value = @(switchControl.isOn); 149 | [self makeValidationWithSectionModels:nil modelIndex:0]; 150 | 151 | if (PFSwitchControlDelegate && 152 | [PFSwitchControlDelegate respondsToSelector:@selector(switchControl:didChangeState:)]) { 153 | [PFSwitchControlDelegate switchControl:switchControl didChangeState:switchControl.isOn]; 154 | } 155 | } 156 | 157 | if ([controlView isKindOfClass:[UIStepper class]]) { 158 | UIStepper *stepperControl = controlView; 159 | self.currentInputView.value = @(stepperControl.value); 160 | [self makeValidationWithSectionModels:nil modelIndex:0]; 161 | 162 | [[NSNotificationCenter defaultCenter] postNotificationName:PFDisplayLabelShouldChangeNotification 163 | object:self.currentInputView]; 164 | 165 | if (PFStepperControlDelegate && 166 | [PFStepperControlDelegate respondsToSelector:@selector(stepperControl:didChangeValue:)]) { 167 | [PFStepperControlDelegate stepperControl:stepperControl didChangeValue:(CGFloat) stepperControl.value]; 168 | } 169 | } 170 | 171 | if ([controlView isKindOfClass:[UISlider class]]) { 172 | UISlider *sliderControl = controlView; 173 | CGFloat step = sliderControl.pf_stepValue; 174 | self.currentInputView.value = @(round(sliderControl.value / step) * step); 175 | 176 | [[NSNotificationCenter defaultCenter] postNotificationName:PFDisplayLabelShouldChangeNotification 177 | object:self.currentInputView]; 178 | } 179 | } 180 | 181 | #pragma mark - PFFormDelegate Methods Calls 182 | 183 | - (void)callValueDidUpdate { 184 | BOOL isDifferent = ![self.currentInputView.value isEqual:self.originalValue]; 185 | if (!self.currentInputView.value && !self.originalValue) { 186 | isDifferent = NO; 187 | } 188 | 189 | if ([self.currentInputView.view isKindOfClass:[UISlider class]]) { 190 | isDifferent = YES; 191 | } 192 | 193 | if (PFFormDelegate && [PFFormDelegate respondsToSelector:@selector(valueDidUpdate)] && isDifferent) { 194 | [PFFormDelegate valueDidUpdate]; 195 | } 196 | 197 | if (PFFormDelegate && [PFFormDelegate respondsToSelector:@selector(valueDidUpdateWithInputView:)] && isDifferent) { 198 | [PFFormDelegate valueDidUpdateWithInputView:self.currentInputView]; 199 | } 200 | } 201 | 202 | - (void)callValueUpdateDidFailWithFailedValidator:(PFValidator *)validator { 203 | if (!PFFormDelegate) { 204 | return; 205 | } 206 | 207 | NSString *reason = [validator failureReasonWithUserReasons:self.settings.failureReasons]; 208 | 209 | if ([PFFormDelegate respondsToSelector:@selector(valueDidUpdateWithError:)]) { 210 | PFError *error = [[PFError alloc] initWithView:self.currentInputView.view validator:validator reason:reason]; 211 | [PFFormDelegate valueDidUpdateWithError:error]; 212 | } 213 | } 214 | 215 | #pragma mark - Validation 216 | 217 | - (void)makeValidationWithSectionModels:(NSArray *)models modelIndex:(NSUInteger)index { 218 | PFValidator *failedValidator = [self.currentInputView failedValidatorWithValue:self.currentInputView.value 219 | sectionModels:models 220 | modelIndex:index 221 | force:YES]; 222 | if (failedValidator) { 223 | [self callValueUpdateDidFailWithFailedValidator:failedValidator]; 224 | return; 225 | } else { 226 | [self callValueDidUpdate]; 227 | } 228 | } 229 | 230 | #pragma mark - UITextFieldDelegate 231 | 232 | - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { 233 | if (PFTextFieldDelegate && [PFTextFieldDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) { 234 | return [PFTextFieldDelegate textFieldShouldBeginEditing:textField]; 235 | } 236 | 237 | return YES; 238 | } 239 | 240 | - (void)textFieldDidBeginEditing:(UITextField *)textField { 241 | [self searchAndAssignCurrentInputViewByView:textField]; 242 | 243 | if (PFTextFieldDelegate && [PFTextFieldDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) { 244 | [PFTextFieldDelegate textFieldDidBeginEditing:textField]; 245 | } 246 | } 247 | 248 | - (BOOL)textFieldShouldEndEditing:(UITextField *)textField { 249 | if (PFTextFieldDelegate && [PFTextFieldDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) { 250 | return [PFTextFieldDelegate textFieldShouldEndEditing:textField]; 251 | } 252 | 253 | return YES; 254 | } 255 | 256 | - (void)textFieldDidEndEditing:(UITextField *)textField { 257 | [textField resignFirstResponder]; 258 | 259 | if (PFTextFieldDelegate && [PFTextFieldDelegate respondsToSelector:@selector(textFieldDidEndEditing:)]) { 260 | [PFTextFieldDelegate textFieldDidEndEditing:textField]; 261 | } 262 | 263 | self.currentInputView.value = [textField.text isEqualToString:@""] ? nil : textField.text; 264 | 265 | NSArray *models; 266 | NSUInteger index = 0; 267 | for (NSArray *array in self.models) { 268 | NSPredicate *modelPredicate = [NSPredicate predicateWithFormat:@"ANY inputViews == %@", self.currentInputView]; 269 | NSArray *results = [array filteredArrayUsingPredicate:modelPredicate]; 270 | if (results.count > 0) { 271 | models = array; 272 | index = [array indexOfObject:results.firstObject]; 273 | } 274 | } 275 | 276 | [self makeValidationWithSectionModels:models modelIndex:index]; 277 | } 278 | 279 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { 280 | NSString *fullString = [textField.text stringByReplacingCharactersInRange:range withString:string]; 281 | NSString *characterSetString; 282 | 283 | switch (textField.keyboardType) { 284 | case UIKeyboardTypePhonePad: { 285 | characterSetString = PFPhonePadCharacters; 286 | break; 287 | } 288 | case UIKeyboardTypeNumberPad: { 289 | characterSetString = PFNumberPadCharacters; 290 | break; 291 | } 292 | case UIKeyboardTypeDecimalPad: { 293 | characterSetString = PFDecimalPadCharacters; 294 | break; 295 | } 296 | default: 297 | break; 298 | } 299 | 300 | if (fullString.length > 0 && characterSetString && self.settings.isKeyboardTypeValidation) { 301 | NSCharacterSet *numberPadCharacters = [NSCharacterSet characterSetWithCharactersInString:characterSetString]; 302 | if ([fullString rangeOfCharacterFromSet:[numberPadCharacters invertedSet]].location != NSNotFound) { 303 | string = nil; 304 | } 305 | } 306 | 307 | if (PFTextFieldDelegate && [PFTextFieldDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) { 308 | return [PFTextFieldDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string]; 309 | } 310 | 311 | return string != nil; 312 | } 313 | 314 | - (BOOL)textFieldShouldClear:(UITextField *)textField { 315 | if (PFTextFieldDelegate && [PFTextFieldDelegate respondsToSelector:@selector(textFieldShouldClear:)]) { 316 | return [PFTextFieldDelegate textFieldShouldClear:textField]; 317 | } 318 | 319 | return YES; 320 | } 321 | 322 | - (BOOL)textFieldShouldReturn:(UITextField *)textField { 323 | [textField resignFirstResponder]; 324 | 325 | if (PFTextFieldDelegate && [PFTextFieldDelegate respondsToSelector:@selector(textFieldShouldReturn:)]) { 326 | return [PFTextFieldDelegate textFieldShouldReturn:textField]; 327 | } 328 | 329 | return YES; 330 | } 331 | 332 | #pragma mark - Keyboard Avoiding 333 | 334 | - (void)keyboardWillHide:(NSNotification *)notification { 335 | 336 | CGRect viewFrame = self.tableView.frame; 337 | viewFrame.size.height += self.showedKeyboardFrame.CGRectValue.size.height; 338 | 339 | [UIView beginAnimations:nil context:NULL]; 340 | [UIView setAnimationBeginsFromCurrentState:YES]; 341 | [self.tableView setFrame:viewFrame]; 342 | [UIView commitAnimations]; 343 | 344 | self.showedKeyboardFrame = nil; 345 | } 346 | 347 | - (void)keyboardWillShow:(NSNotification *)notification { 348 | if (self.showedKeyboardFrame) { 349 | return; 350 | } 351 | 352 | CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; 353 | 354 | CGRect viewFrame = self.tableView.frame; 355 | viewFrame.size.height -= keyboardFrame.size.height; 356 | 357 | [UIView beginAnimations:nil context:NULL]; 358 | [UIView setAnimationBeginsFromCurrentState:YES]; 359 | [self.tableView setFrame:viewFrame]; 360 | [UIView commitAnimations]; 361 | 362 | self.showedKeyboardFrame = [NSValue valueWithCGRect:keyboardFrame]; 363 | } 364 | 365 | @end 366 | -------------------------------------------------------------------------------- /PureForm/Delegates/PFViewDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFViewDelegate.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import 26 | 27 | @protocol PFSegmentedControlDelegate 28 | 29 | @optional 30 | /** 31 | Called when a UISegmentedControl value was changed. 32 | @param segmentedControl The UISegmentedControl object. 33 | @param index The index of the selected item. 34 | */ 35 | - (void)segmentedControl:(UISegmentedControl *)segmentedControl didSelectItemAtIndex:(NSInteger)index; 36 | 37 | @end 38 | 39 | @protocol PFSwitchControlDelegate 40 | 41 | @optional 42 | /** 43 | Called when a UISwitch value was changed. 44 | @param switchControl The UISwitch object. 45 | @param state The state of UISwitch object. 46 | */ 47 | - (void)switchControl:(UISwitch *)switchControl didChangeState:(BOOL)state; 48 | 49 | @end 50 | 51 | @protocol PFSliderControlDelegate 52 | 53 | @optional 54 | /** 55 | Called when the user interaction with a UISlider object was ended. 56 | @param sliderControl The UISlider object. 57 | @param value The value of UISlider object. 58 | */ 59 | - (void)sliderControl:(UISlider *)sliderControl didChangeValue:(CGFloat)value; 60 | 61 | @end 62 | 63 | @protocol PFStepperControlDelegate 64 | 65 | @optional 66 | /** 67 | Called when a UIStepper value was changed. 68 | @param stepperControl The UIStepper object. 69 | @param value The value of UIStepper object. 70 | */ 71 | - (void)stepperControl:(UIStepper *)stepperControl didChangeValue:(CGFloat)value; 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /PureForm/Models/PFError.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFError.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import 26 | 27 | @class PFInputView; 28 | @class PFValidator; 29 | 30 | @interface PFError : NSObject 31 | 32 | /** 33 | The error-associated UIView object. 34 | */ 35 | @property(strong, nonatomic) UIView *view; 36 | 37 | /** 38 | The error-associated validator. 39 | */ 40 | @property(assign, nonatomic) PFValidator *validator; 41 | 42 | /** 43 | The error reason. 44 | */ 45 | @property(strong, nonatomic) NSString *reason; 46 | 47 | /** 48 | @return An instance of PFError. 49 | */ 50 | - (instancetype)initWithView:(UIView *)view validator:(PFValidator *)validator reason:(NSString *)reason; 51 | 52 | /** 53 | @return The array of error reasons. 54 | @param The array of PFError objects. 55 | */ 56 | + (NSArray *)descriptionFromErrors:(NSArray *)errors; 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /PureForm/Models/PFError.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFError.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFError.h" 25 | #import "PFModel.h" 26 | #import "UIView+PureForm.h" 27 | 28 | @implementation PFError 29 | 30 | - (instancetype)initWithView:(UIView *)view validator:(PFValidator *)validator reason:(NSString *)reason { 31 | self = [super init]; 32 | 33 | if (self) { 34 | _view = view; 35 | _validator = validator; 36 | _reason = reason; 37 | } 38 | 39 | return self; 40 | } 41 | 42 | - (NSString *)description { 43 | return [NSString stringWithFormat:@"View: (row: %tu, section: %tu, tag: %tu), reason: %@", 44 | self.view.pf_index, self.view.pf_section, self.view.pf_tag, self.reason]; 45 | } 46 | 47 | + (NSArray *)descriptionFromErrors:(NSArray *)errors { 48 | NSMutableArray *descriptions = [[NSMutableArray alloc] initWithCapacity:errors.count]; 49 | 50 | for (PFError *error in errors) { 51 | [descriptions addObject:error.description]; 52 | } 53 | 54 | return [NSArray arrayWithArray:descriptions]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /PureForm/Models/PFInputView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFInputView.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 24.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class PFValidator; 13 | 14 | @interface PFInputView : NSObject 15 | 16 | /** 17 | The real input view. 18 | */ 19 | @property(strong, nonatomic) UIView *view; 20 | 21 | /** 22 | The current value. 23 | */ 24 | @property(strong, nonatomic) id value; 25 | 26 | /** 27 | The key for creating an output dictionary. 28 | */ 29 | @property(strong, nonatomic) NSString *key; 30 | 31 | /** 32 | The validators of the input view (`view`). 33 | */ 34 | @property(strong, nonatomic) NSArray *validators; 35 | 36 | /** 37 | Indicates whether `view` is a form input view (use `?` to mark as an input view). 38 | */ 39 | @property(assign, nonatomic, getter=isFormView) BOOL formView; 40 | 41 | ///--------------- 42 | /// @name Specials 43 | ///--------------- 44 | 45 | @property(strong, nonatomic) NSString *propertyName; 46 | 47 | @property(strong, nonatomic) NSString *displayPropertyName; 48 | 49 | @property(strong, nonatomic) NSDictionary *params; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /PureForm/Models/PFInputView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFInputView.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 24.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "PFInputView.h" 10 | 11 | @implementation PFInputView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PureForm/Models/PFModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFModel.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import 26 | 27 | @class PFInputView; 28 | 29 | @interface PFModel : NSObject 30 | 31 | /** 32 | The array of input views. It contains form views and simple views as well (ex. UILabel with cell title). 33 | */ 34 | @property(strong, nonatomic) NSArray *inputViews; 35 | 36 | /** 37 | Just a custom data. You can associate with this model what you want using this property. 38 | */ 39 | @property(strong, nonatomic) id userData; 40 | 41 | ///--------------- 42 | /// @name Specials 43 | ///--------------- 44 | 45 | @property(strong, nonatomic) NSString *cellClassName; 46 | 47 | @property(strong, nonatomic) NSString *cellIdentifier; 48 | 49 | @property(assign, nonatomic) NSUInteger sectionIndex; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /PureForm/Models/PFModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFModel.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFModel.h" 25 | 26 | @implementation PFModel 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /PureForm/Models/PFSettings.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFSettings.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import 26 | #import "PFViewDelegate.h" 27 | #import "PFFormDelegate.h" 28 | 29 | NS_ASSUME_NONNULL_BEGIN 30 | 31 | @interface PFSettings : NSObject 32 | 33 | /** 34 | Provides a real-time validation for approved characters by UIKeyboardType. 35 | `NO` by default. 36 | */ 37 | @property(assign, nonatomic, getter=isKeyboardTypeValidation) BOOL keyboardTypeValidation; 38 | 39 | /** 40 | Provides an automatic keyboard avoiding. 41 | `NO` by default. 42 | */ 43 | @property(assign, nonatomic, getter=isKeyboardAvoiding) BOOL keyboardAvoiding; 44 | 45 | /** 46 | The height for each cell in the table view. 47 | `nil` by default. 48 | */ 49 | @property(strong, nonatomic, nullable) NSDictionary *failureReasons; 50 | 51 | /** 52 | The height for each cell in the table view. 53 | `PFCellHeightDefault` by default. 54 | */ 55 | @property(assign, nonatomic) CGFloat cellHeight; 56 | 57 | ///---------------- 58 | /// @name Delegates 59 | ///---------------- 60 | 61 | @property(weak, nonatomic, nullable) id formDelegate; 62 | 63 | @property(weak, nonatomic, nullable) id tableViewDelegate; 64 | 65 | @property(weak, nonatomic, nullable) id textFieldDelegate; 66 | 67 | @property(weak, nonatomic, nullable) id segmentedControlDelegate; 68 | 69 | @property(weak, nonatomic, nullable) id switchControlDelegate; 70 | 71 | @property(weak, nonatomic, nullable) id sliderControlDelegate; 72 | 73 | @property(weak, nonatomic, nullable) id stepperControlDelegate; 74 | 75 | @end 76 | 77 | ///--------------------------- 78 | /// @name Cell height contants 79 | ///--------------------------- 80 | 81 | /** 82 | Similar to UITableViewAutomaticDimension. 83 | */ 84 | FOUNDATION_EXPORT CGFloat const PFCellHeightAutomatic; 85 | 86 | /** 87 | Equals to storyboard params. 88 | */ 89 | FOUNDATION_EXPORT CGFloat const PFCellHeightDefault; 90 | 91 | ///------------------------------------- 92 | /// @name Validation failure reason keys 93 | ///------------------------------------- 94 | 95 | /** 96 | When the value doesn't match the expected one. 97 | */ 98 | FOUNDATION_EXPORT NSString *const PFValidationEqualFailureKey; 99 | 100 | /** 101 | When the value doesn't match the next one. 102 | */ 103 | FOUNDATION_EXPORT NSString *const PFValidationEqualNextFailureKey; 104 | 105 | /** 106 | When the value doesn't match the previous one. 107 | */ 108 | FOUNDATION_EXPORT NSString *const PFValidationEqualPreviousFailureKey; 109 | 110 | /** 111 | When the value length doesn't match expected one. 112 | */ 113 | FOUNDATION_EXPORT NSString *const PFValidationEqualLengthFailureKey; 114 | 115 | /** 116 | When the value length is too short, than expected. 117 | */ 118 | FOUNDATION_EXPORT NSString *const PFValidationMinLengthFailureKey; 119 | 120 | /** 121 | When the value length is too long, than expected. 122 | */ 123 | FOUNDATION_EXPORT NSString *const PFValidationMaxLengthFailureKey; 124 | 125 | /** 126 | When the value length is too small, than expected. 127 | */ 128 | FOUNDATION_EXPORT NSString *const PFValidationMinValueFailureKey; 129 | 130 | /** 131 | When the value length is too big, than expected. 132 | */ 133 | FOUNDATION_EXPORT NSString *const PFValidationMaxValueFailureKey; 134 | 135 | /** 136 | When the value doesn't exist. 137 | */ 138 | FOUNDATION_EXPORT NSString *const PFValidationRequiredFailureKey; 139 | 140 | /** 141 | When the value doesn't match special format. 142 | */ 143 | FOUNDATION_EXPORT NSString *const PFValidationTypeFailureKey; 144 | 145 | /** 146 | When the value doesn't match regex format. 147 | */ 148 | FOUNDATION_EXPORT NSString *const PFValidationCustomFailureKey; 149 | 150 | /** 151 | When uses unknown validator. 152 | */ 153 | FOUNDATION_EXPORT NSString *const PFValidationUnknownFailureKey; 154 | 155 | NS_ASSUME_NONNULL_END 156 | -------------------------------------------------------------------------------- /PureForm/Models/PFSettings.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFSettings.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFSettings.h" 25 | 26 | NSString *const PFValidationEqualFailureKey = @"PFValidationEqualFailure"; 27 | NSString *const PFValidationEqualNextFailureKey = @"PFValidationEqualNextFailure"; 28 | NSString *const PFValidationEqualPreviousFailureKey = @"PFValidationEqualPreviousFailure"; 29 | NSString *const PFValidationEqualLengthFailureKey = @"PFValidationEqualLengthFailure"; 30 | NSString *const PFValidationMinLengthFailureKey = @"PFValidationMinLengthFailure"; 31 | NSString *const PFValidationMaxLengthFailureKey = @"PFValidationMaxLengthFailure"; 32 | NSString *const PFValidationMinValueFailureKey = @"PFValidationMinValueFailure"; 33 | NSString *const PFValidationMaxValueFailureKey = @"PFValidationMaxValueFailure"; 34 | NSString *const PFValidationRequiredFailureKey = @"PFValidationRequiredFailure"; 35 | NSString *const PFValidationTypeFailureKey = @"PFValidationTypeFailure"; 36 | NSString *const PFValidationCustomFailureKey = @"PFValidationCustomFailure"; 37 | NSString *const PFValidationUnknownFailureKey = @"PFValidationUnknownFailure"; 38 | 39 | CGFloat const PFCellHeightAutomatic = -1.f; 40 | CGFloat const PFCellHeightDefault = -2.f; 41 | 42 | @implementation PFSettings 43 | 44 | - (instancetype)init { 45 | self = [super init]; 46 | 47 | if (self) { 48 | _cellHeight = PFCellHeightDefault; 49 | } 50 | 51 | return self; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /PureForm/Models/PFValidator.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFValidator.h 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 24.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PFEnums.h" 11 | 12 | @interface PFValidator : NSObject 13 | 14 | /** 15 | The validator type. 16 | */ 17 | @property(assign, nonatomic) PFValidatorType type; 18 | 19 | /** 20 | It contains validation rule (ex. regex, maximum value, etc). 21 | */ 22 | @property(strong, nonatomic) id value; 23 | 24 | ///--------------- 25 | /// @name Specials 26 | ///--------------- 27 | 28 | @property(assign, nonatomic, getter=isForceValidation) BOOL forceValidation; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /PureForm/Models/PFValidator.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFValidator.m 3 | // Example 4 | // 5 | // Created by Puzyrev Pavel on 24.08.16. 6 | // Copyright © 2016 Puzyrev Pavel. All rights reserved. 7 | // 8 | 9 | #import "PFValidator.h" 10 | 11 | @implementation PFValidator 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PureForm/PFFormController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFFormController.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import 26 | 27 | @class PFSettings; 28 | @class PFInputView; 29 | @class PFModel; 30 | 31 | NS_ASSUME_NONNULL_BEGIN 32 | 33 | @interface PFFormController : NSObject 34 | 35 | /** 36 | @param tableView The user form table view. 37 | @return An instance of PFFormController. 38 | */ 39 | - (instancetype)initWithTableView:(nonnull __kindof UITableView *)tableView settings:(nullable PFSettings *)settings; 40 | 41 | /** 42 | @param fileName The file name with JSON data. 43 | */ 44 | - (void)makeFormWithJSONFile:(nullable NSString *)fileName; 45 | 46 | ///---------------------------------- 47 | /// @name Validation & getting values 48 | ///---------------------------------- 49 | 50 | /** 51 | @return `YES` if validation was successfully passed, otherwise `NO`. 52 | */ 53 | - (BOOL)validate; 54 | 55 | /** 56 | @return The array of all form values at the moment. 57 | */ 58 | - (NSArray *)allValues; 59 | 60 | /** 61 | Uses special keys such as , . 62 | @return The key-value pairs of all form values at the moment. 63 | */ 64 | - (NSDictionary *)allKeyValuePairs; 65 | 66 | ///----------------------------- 67 | /// @name Getting views & models 68 | ///----------------------------- 69 | 70 | /** 71 | @param view The view which will be using for search. 72 | @return The model which contains `view` in one of the `inputViews` array. 73 | */ 74 | - (nullable PFModel *)modelByView:(__kindof UIView *)view; 75 | 76 | /** 77 | @param indexPath The index path which will be using for search. 78 | @return The model which uses for displaying cell by the index path. 79 | */ 80 | - (nullable PFModel *)modelByIndexPath:(NSIndexPath *)indexPath; 81 | 82 | /** 83 | @param view The view which will be using for search. 84 | @return The PFInputView object associated with the view. 85 | */ 86 | - (nullable PFInputView *)inputViewByView:(__kindof UIView *)view; 87 | 88 | /** 89 | Use it if the cell has only one input view. 90 | 91 | @param indexPath The index path which will be using for search. 92 | @return The PFInputView object associated with the indexPath. 93 | */ 94 | - (nullable PFInputView *)inputViewByIndexPath:(NSIndexPath *)indexPath; 95 | 96 | /** 97 | Use it if the cell has more than one input view. 98 | 99 | @param indexPath The index path which will be using for search. 100 | @param tag The tag for exact search. 101 | @return The PFInputView object associated with the indexPath. 102 | */ 103 | - (nullable PFInputView *)inputViewByIndexPath:(NSIndexPath *)indexPath tag:(NSUInteger)tag; 104 | 105 | @end 106 | 107 | NS_ASSUME_NONNULL_END 108 | -------------------------------------------------------------------------------- /PureForm/PFFormController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFFormController.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFFormController.h" 25 | 26 | #import "PFJSONSerialization.h" 27 | #import "PFSettings.h" 28 | #import "PFTableViewDataSource.h" 29 | #import "PFModel.h" 30 | #import "PFConstants.h" 31 | #import "PFInputView+Validation.h" 32 | #import "PFError.h" 33 | #import "PFValidator+FailureReason.h" 34 | #import "PFConstants.h" 35 | #import "UIView+PureForm.h" 36 | 37 | @interface PFFormController () 38 | 39 | @property(strong, nonatomic) UITableView *tableView; 40 | @property(strong, nonatomic) PFSettings *settings; 41 | @property(strong, nonatomic) NSArray *models; 42 | @property(strong, nonatomic) PFTableViewDataSource *tableViewDataSource; 43 | 44 | @end 45 | 46 | @implementation PFFormController 47 | 48 | - (instancetype)initWithTableView:(UITableView *)tableView settings:(PFSettings *)settings { 49 | self = [super init]; 50 | 51 | if (self) { 52 | _tableView = tableView; 53 | _settings = settings; 54 | } 55 | 56 | return self; 57 | } 58 | 59 | - (void)makeFormWithJSONFile:(NSString *)fileName { 60 | self.models = [PFJSONSerialization modelsFromFile:fileName]; 61 | 62 | self.tableViewDataSource = [[PFTableViewDataSource alloc] initWithModels:self.models 63 | tableView:self.tableView 64 | settings:self.settings]; 65 | self.tableView.dataSource = self.tableViewDataSource; 66 | self.tableView.delegate = self.settings.tableViewDelegate; 67 | 68 | CGFloat rowHeight = self.settings.cellHeight; 69 | if (rowHeight > PFCellHeightDefault) { 70 | self.tableView.rowHeight = rowHeight; 71 | self.tableView.estimatedRowHeight = rowHeight; 72 | } 73 | } 74 | 75 | - (NSArray *)inputFormViews { 76 | NSMutableArray *inputFormViews = [[NSMutableArray alloc] init]; 77 | 78 | NSPredicate *formPredicate = [NSPredicate predicateWithFormat:@"ANY inputViews.isFormView == YES"]; 79 | 80 | for (PFModel *model in [self.models filteredArrayUsingPredicate:formPredicate]) { 81 | NSPredicate *inputViewPredicate = [NSPredicate predicateWithFormat:@"isFormView == YES"]; 82 | NSArray *results = [model.inputViews filteredArrayUsingPredicate:inputViewPredicate]; 83 | [inputFormViews addObjectsFromArray:results]; 84 | } 85 | 86 | return inputFormViews; 87 | } 88 | 89 | - (BOOL)validate { 90 | BOOL isValid = YES; 91 | 92 | NSMutableArray *errors = [[NSMutableArray alloc] init]; 93 | 94 | for (PFInputView *inputView in [self inputFormViews]) { 95 | NSArray *sectionModels = self.tableViewDataSource.dataSource[inputView.view.pf_section]; 96 | PFValidator *validator = [inputView failedValidatorWithValue:inputView.value 97 | sectionModels:sectionModels 98 | modelIndex:inputView.view.pf_index 99 | force:NO]; 100 | 101 | if (validator) { 102 | NSString *reason = [validator failureReasonWithUserReasons:self.settings.failureReasons]; 103 | PFError *error = [[PFError alloc] initWithView:inputView.view validator:validator reason:reason]; 104 | [errors addObject:error]; 105 | } 106 | 107 | if (isValid && validator) { 108 | isValid = NO; 109 | } 110 | } 111 | 112 | if (!isValid && self.settings.formDelegate && 113 | [self.settings.formDelegate respondsToSelector:@selector(validationDidFailWithErrors:)]) { 114 | [self.settings.formDelegate validationDidFailWithErrors:[NSArray arrayWithArray:errors]]; 115 | } 116 | 117 | if (isValid && [self.settings.formDelegate respondsToSelector:@selector(validationDidEndSuccessfully)]) { 118 | [self.settings.formDelegate validationDidEndSuccessfully]; 119 | } 120 | 121 | return isValid; 122 | } 123 | 124 | - (NSArray *)allValues { 125 | NSMutableArray *values = [[NSMutableArray alloc] init]; 126 | 127 | for (PFInputView *inputView in [self inputFormViews]) { 128 | if (inputView.value) { 129 | [values addObject:inputView.value]; 130 | } 131 | } 132 | 133 | return values; 134 | } 135 | 136 | - (NSDictionary *)allKeyValuePairs { 137 | NSMutableDictionary *pairs = [[NSMutableDictionary alloc] init]; 138 | 139 | NSUInteger emptyIndex = 0; 140 | for (PFInputView *inputView in [self inputFormViews]) { 141 | if (!inputView.value && !inputView.key) { 142 | continue; 143 | } 144 | 145 | NSString *key = inputView.key; 146 | NSString *value = inputView.value ?: @""; 147 | 148 | if (!inputView.key || [inputView.key isEqualToString:@""]) { 149 | key = [NSString stringWithFormat:@"%@_%tu", PFUnknownKey, emptyIndex]; 150 | emptyIndex += 1; 151 | } 152 | 153 | pairs[key] = value; 154 | } 155 | 156 | return pairs; 157 | } 158 | 159 | - (PFModel *)modelByView:(__kindof UIView *)view { 160 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY inputViews.view == %@", view]; 161 | NSArray *results = [self.models filteredArrayUsingPredicate:predicate]; 162 | 163 | return results.count > 0 ? results.firstObject : nil; 164 | } 165 | 166 | - (PFModel *)modelByIndexPath:(NSIndexPath *)indexPath { 167 | NSPredicate *predicate = [NSPredicate predicateWithFormat: 168 | @"ANY inputViews.view.pf_index == %ld AND sectionIndex == %ld", indexPath.row, indexPath.section]; 169 | NSArray *results = [self.models filteredArrayUsingPredicate:predicate]; 170 | 171 | return results.count > 0 ? results.firstObject : nil; 172 | } 173 | 174 | - (PFInputView *)inputViewByView:(__kindof UIView *)view { 175 | PFModel *model = [self modelByView:view]; 176 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"view == %@", view]; 177 | NSArray *results = [model.inputViews filteredArrayUsingPredicate:predicate]; 178 | 179 | return results.count > 0 ? results.firstObject : nil; 180 | } 181 | 182 | - (PFInputView *)inputViewByIndexPath:(NSIndexPath *)indexPath { 183 | return [self inputViewByIndexPath:indexPath tag:0]; 184 | } 185 | 186 | - (PFInputView *)inputViewByIndexPath:(NSIndexPath *)indexPath tag:(NSUInteger)tag { 187 | PFModel *model = [self modelByIndexPath:indexPath]; 188 | NSPredicate *predicate = [NSPredicate predicateWithFormat: 189 | @"view.pf_index == %ld AND view.pf_section == %ld AND view.pf_tag == %ld AND formView == YES", 190 | indexPath.row, indexPath.section, tag]; 191 | NSArray *results = [model.inputViews filteredArrayUsingPredicate:predicate]; 192 | 193 | return results.count > 0 ? results.firstObject : nil; 194 | } 195 | 196 | @end 197 | -------------------------------------------------------------------------------- /PureForm/PFJSONSerialization.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFJSONSerialization.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | NS_ASSUME_NONNULL_BEGIN 27 | 28 | @interface PFJSONSerialization : NSObject 29 | 30 | /** 31 | @param fileName The name of JSON file. 32 | @return The array of PFModel objects. 33 | */ 34 | + (nullable NSArray *)modelsFromFile:(NSString *)fileName; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /PureForm/PFJSONSerialization.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFJSONSerialization.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFJSONSerialization.h" 25 | #import "NSString+PureForm.h" 26 | #import "PFModel.h" 27 | #import "PFConstants.h" 28 | #import "PFMacros.h" 29 | #import "PFInputView.h" 30 | #import "PFValidator.h" 31 | 32 | @implementation PFJSONSerialization 33 | 34 | + (NSArray *)modelsFromFile:(NSString *)fileName { 35 | NSError *error; 36 | 37 | NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"json"]; 38 | NSString *string = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error]; 39 | 40 | if (error) { 41 | PFLog(@"%@", error.localizedDescription); 42 | return nil; 43 | } 44 | 45 | NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; 46 | NSArray *elements = [PFJSONSerialization PFModelWithData:data error:&error]; 47 | 48 | if (error) { 49 | PFLog(@"%@", error.localizedDescription); 50 | return nil; 51 | } 52 | 53 | return elements; 54 | } 55 | 56 | + (NSArray *)PFModelWithData:(NSData *)data error:(NSError **)error { 57 | NSArray *elements = [NSJSONSerialization JSONObjectWithData:data options:0 error:error]; 58 | NSMutableArray *models = [[NSMutableArray alloc] initWithCapacity:elements.count]; 59 | 60 | NSUInteger sectionIndex = 0; 61 | 62 | for (NSDictionary *element in elements) { 63 | if (element.allKeys.count == 1 && [element.allKeys.firstObject isEqualToString:PFSectionKey]) { 64 | sectionIndex = [element.allValues.firstObject unsignedIntegerValue]; 65 | continue; 66 | } 67 | 68 | PFModel *model = [[PFModel alloc] init]; 69 | model.sectionIndex = sectionIndex; 70 | 71 | NSArray *level1Keys = element.allKeys; 72 | 73 | BOOL userDataIsMultiple = level1Keys.count > 2; 74 | NSMutableArray *userData = userDataIsMultiple ? [[NSMutableArray alloc] initWithCapacity:level1Keys.count - 1] : nil; 75 | 76 | NSDictionary *inputViewsDictionary; 77 | for (NSString *level1key in level1Keys) { 78 | if ([level1key pf_isCombinedKey]) { 79 | 80 | NSString *cellClassName = [level1key pf_cellClassName]; 81 | NSString *cellIdentifier = [level1key pf_cellIdentifier]; 82 | 83 | PFModel *previousModel = models.lastObject; 84 | 85 | model.cellClassName = 86 | [cellClassName isEqualToString:PFSpecialCharSame] ? previousModel.cellClassName : cellClassName; 87 | model.cellIdentifier = 88 | [cellIdentifier isEqualToString:PFSpecialCharSame] ? previousModel.cellIdentifier : cellIdentifier; 89 | 90 | inputViewsDictionary = element[level1key]; 91 | 92 | continue; 93 | } 94 | 95 | [userData addObject:element[level1key]]; 96 | } 97 | 98 | // Hm... 99 | NSArray *level2Keys = inputViewsDictionary.allKeys; 100 | NSMutableArray *inputViews = [[NSMutableArray alloc] initWithCapacity:level2Keys.count]; 101 | for (NSString *level2key in level2Keys) { 102 | PFInputView *inputView = [[PFInputView alloc] init]; 103 | inputView.propertyName = [level2key pf_hasFormViewSuffix] ? [level2key pf_suffixFreeString] : level2key; 104 | inputView.formView = [level2key pf_hasFormViewSuffix]; 105 | 106 | NSDictionary *inputViewOptions = inputViewsDictionary[level2key]; 107 | NSMutableDictionary *inputViewParams = [[NSMutableDictionary alloc] init]; 108 | 109 | for (NSString *level3Key in inputViewOptions.allKeys) { 110 | id value = inputViewOptions[level3Key]; 111 | 112 | if ([level3Key pf_isSpecialKey]) { 113 | if ([level3Key isEqualToString:PFValueKey]) { 114 | inputView.value = value; 115 | } 116 | if ([level3Key isEqualToString:PFKeyKey]) { 117 | inputView.key = value; 118 | } 119 | if ([level3Key isEqualToString:PFValidationKey]) { 120 | NSDictionary *options = value; 121 | 122 | NSArray *keys = options.allKeys; 123 | NSMutableArray *validators = [[NSMutableArray alloc] initWithCapacity:keys.count]; 124 | 125 | for (NSString *key in keys) { 126 | PFValidator *validator = [[PFValidator alloc] init]; 127 | 128 | validator.type = (PFValidatorType) [PFEnums enumFromValue:key]; 129 | validator.forceValidation = [key pf_hasSuffix]; 130 | validator.value = options[key]; 131 | 132 | [validators addObject:validator]; 133 | } 134 | 135 | inputView.validators = [NSArray arrayWithArray:validators]; 136 | } 137 | if ([level3Key isEqualToString:PFDisplayKey]) { 138 | inputView.displayPropertyName = value; 139 | } 140 | 141 | continue; 142 | } 143 | 144 | NSString *saveKey = level3Key; 145 | 146 | if ([saveKey pf_hasLocalizedSuffix]) { 147 | value = NSLocalizedString(value,); 148 | saveKey = [saveKey pf_suffixFreeString]; 149 | } 150 | 151 | inputViewParams[saveKey] = value; 152 | } 153 | 154 | inputView.params = [NSDictionary dictionaryWithDictionary:inputViewParams]; 155 | 156 | [inputViews addObject:inputView]; 157 | } 158 | 159 | model.inputViews = [NSArray arrayWithArray:inputViews]; 160 | 161 | [models addObject:model]; 162 | } 163 | 164 | return [NSArray arrayWithArray:models]; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /PureForm/PFTableViewDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFTableViewDataSource.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | #import 26 | 27 | @class PFModel; 28 | @class PFSettings; 29 | 30 | @interface PFTableViewDataSource : NSObject 31 | 32 | /** 33 | The section-grouped data source for table view. 34 | */ 35 | @property(strong, nonatomic, readonly) NSMutableArray *> *dataSource; 36 | 37 | /** 38 | @param elements The array of PFModel objects. 39 | @return The instance of PFTableViewDataSource. 40 | */ 41 | - (instancetype)initWithModels:(NSArray *)models tableView:(UITableView *)tableView settings:(PFSettings *)settings; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /PureForm/PFTableViewDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFTableViewDataSource.m 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import "PFTableViewDataSource.h" 25 | 26 | #import "PFModel.h" 27 | #import "PFMacros.h" 28 | #import "UITableViewCell+PureForm.h" 29 | #import "PFInputViewDelegate.h" 30 | #import "PFSettings.h" 31 | #import "UIView+PureForm.h" 32 | #import "PFConstants.h" 33 | #import "PFInputView.h" 34 | 35 | @interface PFTableViewDataSource () 36 | 37 | @property(strong, nonatomic, readwrite) NSMutableArray *> *dataSource; 38 | @property(strong, nonatomic) PFInputViewDelegate *inputViewDelegate; 39 | @property(strong, nonatomic) PFSettings *settings; 40 | @property(strong, nonatomic) UITableView *tableView; 41 | 42 | @end 43 | 44 | @implementation PFTableViewDataSource 45 | 46 | - (instancetype)initWithModels:(NSArray *)models tableView:(UITableView *)tableView settings:(PFSettings *)settings { 47 | self = [super init]; 48 | 49 | if (self) { 50 | NSMutableArray *sections = [[NSMutableArray alloc] init]; 51 | for (PFModel *model in models) { 52 | NSNumber *sectionIndex = @(model.sectionIndex); 53 | if (![sections containsObject:sectionIndex]) { 54 | [sections addObject:sectionIndex]; 55 | } 56 | } 57 | 58 | // Safe for random selection of sections 59 | NSMutableArray *dataSource = [[NSMutableArray alloc] initWithCapacity:sections.count]; 60 | for (NSUInteger i = 0; i < sections.count; i++) { 61 | [dataSource addObject:@0]; 62 | } 63 | 64 | for (NSNumber *sectionIndex in sections) { 65 | NSPredicate *sectionPredicate = [NSPredicate predicateWithFormat:@"sectionIndex == %@", sectionIndex]; 66 | NSArray *results = [models filteredArrayUsingPredicate:sectionPredicate]; 67 | dataSource[[sectionIndex unsignedIntegerValue]] = results; 68 | } 69 | 70 | _settings = settings; 71 | _dataSource = dataSource; 72 | _tableView = tableView; 73 | 74 | [[NSNotificationCenter defaultCenter] addObserver:self 75 | selector:@selector(changeDisplayLabel:) 76 | name:PFDisplayLabelShouldChangeNotification 77 | object:nil]; 78 | } 79 | 80 | return self; 81 | } 82 | 83 | - (void)dealloc { 84 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 85 | } 86 | 87 | - (void)changeDisplayLabel:(NSNotification *)notification { 88 | PFInputView *inputView = notification.object; 89 | 90 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:inputView.view.pf_index inSection:inputView.view.pf_section]; 91 | UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; 92 | [cell setValue:inputView.value forPropertyName:inputView.propertyName]; 93 | } 94 | 95 | #pragma clang diagnostic push 96 | #pragma clang diagnostic ignored "-Wundeclared-selector" 97 | 98 | #pragma mark - UITableViewDataSource 99 | 100 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 101 | return self.dataSource[(NSUInteger) section].count; 102 | } 103 | 104 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 105 | return self.dataSource.count; 106 | } 107 | 108 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 109 | PFModel *model = self.dataSource[(NSUInteger) indexPath.section][(NSUInteger) indexPath.row]; 110 | 111 | Class CellClass = NSClassFromString(model.cellClassName); 112 | 113 | __kindof UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:model.cellIdentifier]; 114 | if (!cell) { 115 | cell = (__kindof UITableViewCell *) [[CellClass alloc] init]; 116 | } 117 | 118 | NSUInteger tag = 0; 119 | for (PFInputView *inputView in model.inputViews) { 120 | if (inputView.isFormView) { 121 | __kindof UIView *view = [cell valueForKey:inputView.propertyName]; 122 | 123 | view.pf_index = (NSUInteger) indexPath.row; 124 | view.pf_section = (NSUInteger) indexPath.section; 125 | view.pf_tag = tag; 126 | view.pf_display = inputView.displayPropertyName ? [cell valueForKey:inputView.displayPropertyName] : nil; 127 | 128 | inputView.view = view; 129 | 130 | tag += 1; 131 | 132 | [self assignDelegateToView:view]; 133 | } 134 | 135 | for (NSString *paramKey in inputView.params.allKeys) { 136 | NSString *param = inputView.params[paramKey]; 137 | 138 | @try { 139 | [cell setValue:param forKeyPath:[NSString stringWithFormat:@"%@.%@", inputView.propertyName, paramKey]]; 140 | } 141 | @catch (NSException *exception) { 142 | @throw exception; 143 | UIView *view = inputView.view; 144 | PFLog(@"Unable to set '%@' to '%@' (row: %tu, section: %tu, tag: %tu)", 145 | paramKey, inputView.propertyName, view.pf_index, view.pf_section, view.pf_tag); 146 | } 147 | } 148 | 149 | if (inputView.value) { 150 | [cell setValue:inputView.value forPropertyName:inputView.propertyName]; 151 | } 152 | } 153 | 154 | return cell; 155 | } 156 | 157 | #pragma mark - Delegates 158 | 159 | - (void)assignDelegateToView:(id)view { 160 | if (!self.inputViewDelegate) { 161 | self.inputViewDelegate = [[PFInputViewDelegate alloc] initWithModels:self.dataSource 162 | tableView:self.tableView 163 | settings:self.settings]; 164 | } 165 | 166 | if ([view respondsToSelector:@selector(delegate)]) { 167 | id dView = view; 168 | dView.delegate = self.inputViewDelegate; 169 | } else { 170 | if ([view isKindOfClass:[UISlider class]]) { 171 | SEL action = @selector(sliderControlDidEndEditing:); 172 | [view addTarget:self.inputViewDelegate action:action forControlEvents:UIControlEventTouchUpInside]; 173 | } 174 | 175 | SEL action = @selector(controlViewDidChangeValue:); 176 | [view addTarget:self.inputViewDelegate action:action forControlEvents:UIControlEventValueChanged]; 177 | } 178 | } 179 | 180 | #pragma clang diagnostic pop 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /PureForm/PureForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // PureForm.h 3 | // 4 | // Copyright (c) 2016 Puzyrev Pavel 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #import 25 | 26 | #import "PFFormController.h" 27 | #import "PFFormDelegate.h" 28 | #import "PFSettings.h" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PureForm — the easiest way to build form such as login, questionnaire and etc. screens from JSON file. PureForm provides parsing properties and special params into objects for further manipulation of them. 2 | 3 | ## Setup 4 | 5 | ### Manually 6 | 7 | Download the source files from the [PureForm subdirectory](https://github.com/unboxme/PureForm/tree/master/PureForm). Then just add source files to your Xcode project and import the `PureLayout.h` header. 8 | 9 | ### Cocoapods 10 | 11 | Add the pod PureForm to your Podfile: 12 | 13 | ```ruby 14 | platform :ios, '8.0' 15 | 16 | target 'YourProject' do 17 | pod 'PureForm' 18 | end 19 | ``` 20 | 21 | Then, run the following command: 22 | 23 | ```terminal 24 | $ pod install 25 | ``` 26 | 27 | ## Usage 28 | 29 | PureForm is clean and pretty easy to use in several steps: 30 | 31 | 1. Describe your form in a JSON file using `JSON Rules` paragraph below; 32 | 2. Create settings via `PFSettings` using `Settings` paragraph below: 33 | 34 | ```objc 35 | PFSettings *settings = [[PFSettings alloc] init]; 36 | settings.keyboardTypeValidation = YES; 37 | settings.keyboardAvoiding = YES; 38 | settings.formDelegate = self; 39 | settings.tableViewDelegate = self; 40 | 41 | // etc. 42 | ``` 43 | 3. Create a `PFFormController` instance with a `UITableView` and settings which were created at the previous step: 44 | 45 | ```objc 46 | self.formController = [[PFFormController alloc] initWithTableView:self.tableView settings:settings]; 47 | ``` 48 | 4. Just use a `makeFormWithJSONFile:` method to create and display your form from the JSON file from the first step: 49 | 50 | ```objc 51 | [self.formController makeFormWithJSONFile:@"form"]; 52 | ``` 53 | 54 | 5. That's it! 55 | 56 | ## Settings 57 | 58 | The [settings header](PureForm/Models/PFSettings.h) is fully documented. There are main params here you can set: 59 | 60 | * `keyboardTypeValidation` 61 | * `keyboardAvoiding` 62 | * `failureReasons` 63 | * `cellHeight` 64 | 65 | As well as delegates for `UIKit` controls: 66 | 67 | * `PFFormDelegate` 68 | * `UITableViewDelegate` 69 | * `UITextFieldDelegate` 70 | * `PFSegmentedControlDelegate` 71 | * `PFSwitchControlDelegate` 72 | * `PFSliderControlDelegate` 73 | * `PFStepperControlDelegate` 74 | 75 | ## JSON Rules 76 | 77 | Describe your custom cell in JSON just using property names and values for them. Of course, __reserved words__ and __special chars__ are used too. You can find reserved words and special chars in a [constants header](PureForm/Common/PFConstants.h). 78 | 79 | ### Basics 80 | 81 | * Pairs of class cell name or cell identifier and reserved words are segregated by `<` and `>`. 82 | * The base structure for 2 cells looks like __(pay attention on levels)__: 83 | 84 | ``` 85 | [ /// 0 level 86 | { 87 | "": { // First cell 88 | property_name_1: { /// 1 level 89 | in_property_name_1.1: value, /// 2 level 90 | in_property_name_1.2: value 91 | }, 92 | property_name_2: { 93 | in_property_name_2.1: value 94 | } 95 | } 96 | }, 97 | { 98 | "": { // Second cell 99 | // Setup 100 | }, 101 | { 102 | // etc. 103 | } 104 | ] 105 | ``` 106 | __Note that JSON parsing supports only 2 levels of nesting yet.__ 107 | * If a cell class name or a cell identifier equals to the previous one, you can simplify it using `=` like that: 108 | 109 | ``` 110 | { 111 | "<= + =>": { // Any cell 112 | // Setup 113 | }, 114 | ``` 115 | * Usage of reserved words and special chars: 116 | 117 | | Reserved word | Level | Value 118 | |----------------------|---------|---------- 119 | | `
` | 0 | index of section 120 | | `` | 1 | current value 121 | | `` | 1 | save key for output dictionary 122 | | `` | 1 | array of validators 123 | | `` | 1 | property name to display value after change 124 | __All reserved words and special chars are optional.__ 125 | 126 | | Char | Usage in | Example | Description 127 | |------------------|-----------|-------------|------ 128 | | ? | property name | textField? | Shows that it's a form for validation — not just a view 129 | | ! | validator name | min_value! | Starts validation immediately after value change 130 | | * | property name | text* | Shows that this value is a key for `NSLocalizedString` macros 131 | | # | value | ic_turtle#6F3F83 or #6F3F83| Uses as it is or for hex coloring image by their name 132 | * Available validator keys for ``: 133 | 134 | | Validator | Value 135 | |-------------|---------- 136 | | `equal` | string or number 137 | | `equal_next` | `true` 138 | | `equal_previous` | `true` 139 | | `equal_length` | number 140 | | `min_length` | number 141 | | `max_length` | number 142 | | `min_value` | number 143 | | `max_value` | number 144 | | `required` | `true` 145 | | `type` | `email` or `full_name` 146 | | `custom` | regex string 147 | __Validators are used in 2 level and they should be nested into ``.__ 148 | 149 | * Enums are just numeric values in JSON. By the way there are some enums which are associated with string values: 150 | * `UIKeyboardType` 151 | * `UITextAutocapitalizationType` 152 | * `UITextAutocorrectionType` 153 | * `UITextSpellCheckingType` 154 | * `UIKeyboardAppearance` 155 | * `UIReturnKeyType` 156 | * `UITextFieldViewMode` 157 | 158 | ### Examples 159 | 160 | * Well, how it looks in a real example: 161 | 162 | ```json 163 | [ 164 | { 165 | "": { 166 | "textField?": { 167 | "placeholder": "Huh, centered?", 168 | "textAlignment": 1 169 | } 170 | } 171 | } 172 | ] 173 | ``` 174 | 175 |

176 | 177 |

178 | 179 | * Now let's make a login form with validators: 180 | 181 | ```json 182 | [ 183 | { 184 | "": { 185 | "textField?": { 186 | "placeholder": "Login", 187 | "textAlignment": 1, 188 | "keyboardType": "UIKeyboardTypeEmailAddress", 189 | "": "login", 190 | "": { 191 | "type": "email" 192 | } 193 | } 194 | } 195 | }, 196 | { 197 | "<= + =>": { 198 | "textField?": { 199 | "placeholder": "Password", 200 | "textAlignment": 1, 201 | "secureTextEntry": true, 202 | "": "password", 203 | "": { 204 | "min_length!": 6, 205 | "max_length!": 25 206 | } 207 | } 208 | } 209 | }, 210 | { 211 | "
": 1 212 | }, 213 | { 214 | "": { 215 | "titleLabel": { 216 | "text": "Login", 217 | "textColor": "#6F3F83" 218 | } 219 | } 220 | } 221 | ] 222 | ``` 223 | 224 |

225 | 226 |

227 | 228 | * It remains only to validate entered values and save they into the dictionary. Credentials from left form satisfies the validators and `[self.formController validate]` will return `YES`: 229 | 230 | ```objc 231 | if ([self.formController validate]) { 232 | NSDictionary *loginInfo = [self.formController allKeyValuePairs]; 233 | // Send it to server 234 | } 235 | ``` 236 | 237 |

238 | 239 | 240 |

241 | 242 | ## Notes 243 | 244 | * Example project files will open properly only in Xcode 8. 245 | 246 | ## TODO 247 | 248 | - [ ] Remove limit of nesting 249 | - [x] Keyboard avoiding (issue #2) 250 | - [ ] Targets for buttons 251 | - [ ] Support of UITextView 252 | - [ ] Wrapper for editing common properties 253 | -------------------------------------------------------------------------------- /Screenshots/centered_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unboxme/PureForm/84c77c143b4249c19e11d0728bcb23cd0adffb21/Screenshots/centered_text.png -------------------------------------------------------------------------------- /Screenshots/clean_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unboxme/PureForm/84c77c143b4249c19e11d0728bcb23cd0adffb21/Screenshots/clean_form.png -------------------------------------------------------------------------------- /Screenshots/filled_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unboxme/PureForm/84c77c143b4249c19e11d0728bcb23cd0adffb21/Screenshots/filled_form.png -------------------------------------------------------------------------------- /Screenshots/filled_form_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unboxme/PureForm/84c77c143b4249c19e11d0728bcb23cd0adffb21/Screenshots/filled_form_error.png --------------------------------------------------------------------------------