From 151003d8e5e55e039a4c9db479411f0130861771 Mon Sep 17 00:00:00 2001 From: Liraz S Date: Mon, 25 Mar 2019 09:20:16 -0600 Subject: [PATCH] Memory and stability fixes to XLForm (#1045) * selectorCell display repair Unable to display when value than apples first selectorOptions * fixed crash * fixed memory leaks * Cell disappears while inline picker is open * Fixes crash on update * check object object equal * codereview * fixed memory leaks * fixed memory leaks [2] * updated logic * fixed memory leaks * fix for crash #1043 * fix for crash #1044 * fix for issue #1044 * fix for issue #1044 * fixed mistake * fixed observation if lib uses in drawer controller * fixed kvo crash * reverted last fix * cleanup * fixed kvo * fix for crash #1043 * reverted https://github.com/xmartlabs/XLForm/pull/1045#discussion_r255965033 * reverted #948 * fixed firstResponder logic * codereview in Descriptors * codereview --- .gitignore | 6 +- .../Formatters/FormattersViewController.m | 4 +- Examples/Objective-C/Podfile.lock | 15 +- .../XLForm.xcodeproj/project.pbxproj | 28 +- .../AppIcon.appiconset/Contents.json | 64 +++- Tests/XLForm Tests.xcodeproj/project.pbxproj | 22 +- XLForm.podspec | 2 +- XLForm.xcodeproj/project.pbxproj | 6 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/XLForm.xcscheme | 4 +- XLForm/XL/Cell/XLFormBaseCell.h | 4 +- XLForm/XL/Cell/XLFormDateCell.h | 10 +- XLForm/XL/Cell/XLFormDatePickerCell.m | 17 +- XLForm/XL/Cell/XLFormImageCell.m | 7 +- XLForm/XL/Cell/XLFormLeftRightSelectorCell.m | 8 +- XLForm/XL/Cell/XLFormPickerCell.h | 2 +- XLForm/XL/Cell/XLFormPickerCell.m | 20 +- XLForm/XL/Cell/XLFormSegmentedCell.m | 5 +- XLForm/XL/Cell/XLFormTextFieldCell.h | 8 +- XLForm/XL/Cell/XLFormTextFieldCell.m | 44 +-- XLForm/XL/Cell/XLFormTextViewCell.h | 8 +- XLForm/XL/Cell/XLFormTextViewCell.m | 30 +- XLForm/XL/Controllers/XLFormOptionsObject.h | 4 +- XLForm/XL/Controllers/XLFormOptionsObject.m | 5 + .../Controllers/XLFormOptionsViewController.m | 4 +- .../XLFormRowDescriptorViewController.h | 2 +- XLForm/XL/Controllers/XLFormViewController.h | 4 +- XLForm/XL/Controllers/XLFormViewController.m | 139 ++++--- XLForm/XL/Descriptors/XLFormDescriptor.h | 18 +- XLForm/XL/Descriptors/XLFormDescriptor.m | 357 ++++++++++-------- XLForm/XL/Descriptors/XLFormRowDescriptor.h | 80 ++-- XLForm/XL/Descriptors/XLFormRowDescriptor.m | 259 +++++++------ .../XL/Descriptors/XLFormSectionDescriptor.h | 20 +- .../XL/Descriptors/XLFormSectionDescriptor.m | 152 ++++---- XLForm/XL/Helpers/UIView+XLFormAdditions.h | 2 +- XLForm/XL/Helpers/UIView+XLFormAdditions.m | 49 ++- .../Views/XLFormRowNavigationAccessoryView.h | 6 +- .../Views/XLFormRowNavigationAccessoryView.m | 69 +--- XLForm/XL/Helpers/Views/XLFormTextView.h | 6 +- XLForm/XL/Helpers/Views/XLFormTextView.m | 19 +- XLForm/XL/Validation/XLFormRegexValidator.h | 4 +- XLForm/XL/Validation/XLFormValidationStatus.h | 4 +- 42 files changed, 842 insertions(+), 683 deletions(-) create mode 100644 XLForm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/.gitignore b/.gitignore index b17b9336..7b4b8045 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,5 @@ DerivedData #CocoaPods Pods -Tests/Pods -Tests/Podfile.lock -Examples/Objective-C/Podfile.lock -Examples/Swift/Podfile.lock \ No newline at end of file +Podfile.lock +*.xcworkspace diff --git a/Examples/Objective-C/Examples/Formatters/FormattersViewController.m b/Examples/Objective-C/Examples/Formatters/FormattersViewController.m index 47e7bde4..14bf87a0 100644 --- a/Examples/Objective-C/Examples/Formatters/FormattersViewController.m +++ b/Examples/Objective-C/Examples/Formatters/FormattersViewController.m @@ -21,7 +21,7 @@ // http://stackoverflow.com/questions/12580162/nsstring-to-nsdate-conversion-issue @interface CurrencyFormatter : NSNumberFormatter -@property (readonly) NSDecimalNumberHandler *roundingBehavior; +@property (readonly, strong) NSDecimalNumberHandler *roundingBehavior; @end @@ -117,4 +117,4 @@ -(id)init } -@end \ No newline at end of file +@end diff --git a/Examples/Objective-C/Podfile.lock b/Examples/Objective-C/Podfile.lock index daedd938..70445203 100644 --- a/Examples/Objective-C/Podfile.lock +++ b/Examples/Objective-C/Podfile.lock @@ -43,7 +43,7 @@ PODS: - XLData/RemoteDataStore (2.0.0): - XLData/CoreRemote - XLData/DataStore - - XLForm (4.0.0) + - XLForm (4.0.1) DEPENDENCIES: - AFNetworking (~> 2.0) @@ -53,12 +53,19 @@ DEPENDENCIES: - XLData (from `https://github.com/xmartlabs/XLData.git`, commit `1f9019b56242a2019c7f7e11ec4ef823c397ebcf`) - XLForm (from `../../`) +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - AFNetworking + - AXRatingView + - JVFloatLabeledTextField + - SHSPhoneComponent + EXTERNAL SOURCES: XLData: :commit: 1f9019b56242a2019c7f7e11ec4ef823c397ebcf :git: https://github.com/xmartlabs/XLData.git XLForm: - :path: ../../ + :path: "../../" CHECKOUT OPTIONS: XLData: @@ -71,8 +78,8 @@ SPEC CHECKSUMS: JVFloatLabeledTextField: 58a3a32cfb800e5b224f676987e7c13abf50a14d SHSPhoneComponent: 4cec0653a150ad63cbc52b0c8b29ce2d3c9c26f0 XLData: df725c6179e2e0c80bf56a1ecad9afd169707a6d - XLForm: 04110197c12187dd28a6c7295d3d8b95f4fdb4de + XLForm: b8d47a9a00fb6166981cb40de7169d70d611e9be PODFILE CHECKSUM: 64fbcd03a2c13762b2c18e3938cc8008807937c9 -COCOAPODS: 1.3.1 +COCOAPODS: 1.6.0 diff --git a/Examples/Objective-C/XLForm.xcodeproj/project.pbxproj b/Examples/Objective-C/XLForm.xcodeproj/project.pbxproj index 5113bf54..b105fa52 100644 --- a/Examples/Objective-C/XLForm.xcodeproj/project.pbxproj +++ b/Examples/Objective-C/XLForm.xcodeproj/project.pbxproj @@ -486,7 +486,6 @@ 2850C5F418D0F706002B7D0A /* Sources */, 2850C5F518D0F706002B7D0A /* Frameworks */, 2850C5F618D0F706002B7D0A /* Resources */, - 32EB30E0A90A4E91864B1D3C /* [CP] Embed Pods Frameworks */, DE3D12F5596780A66C6C3D4A /* [CP] Copy Pods Resources */, ); buildRules = ( @@ -504,7 +503,7 @@ 2850C5F018D0F706002B7D0A /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Xmartlabs; }; buildConfigurationList = 2850C5F318D0F706002B7D0A /* Build configuration list for PBXProject "XLForm" */; @@ -542,37 +541,22 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 32EB30E0A90A4E91864B1D3C /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-XLForm/Pods-XLForm-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; DE3D12F5596780A66C6C3D4A /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-XLForm/Pods-XLForm-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-XLForm/Pods-XLForm-resources.sh", "${PODS_ROOT}/../../../XLForm/XLForm.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/XLForm.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-XLForm/Pods-XLForm-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-XLForm/Pods-XLForm-resources.sh\"\n"; showEnvVarsInLog = 0; }; EBE0335CCC7588D83B89A92A /* [CP] Check Pods Manifest.lock */ = { @@ -668,12 +652,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -718,12 +704,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/Examples/Objective-C/XLForm/Images.xcassets/AppIcon.appiconset/Contents.json b/Examples/Objective-C/XLForm/Images.xcassets/AppIcon.appiconset/Contents.json index ce597e2c..a76f48d9 100644 --- a/Examples/Objective-C/XLForm/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Examples/Objective-C/XLForm/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", @@ -48,6 +58,16 @@ "size" : "60x60", "scale" : "3x" }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, { "idiom" : "ipad", "size" : "29x29", @@ -101,8 +121,13 @@ "scale" : "2x" }, { - "idiom" : "car", - "size" : "120x120", + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", "scale" : "1x" }, { @@ -142,8 +167,15 @@ "size" : "44x44", "idiom" : "watch", "scale" : "2x", - "role" : "longLook", - "subtype" : "42mm" + "role" : "appLauncher", + "subtype" : "40mm" + }, + { + "size" : "50x50", + "idiom" : "watch", + "scale" : "2x", + "role" : "appLauncher", + "subtype" : "44mm" }, { "size" : "86x86", @@ -158,6 +190,30 @@ "scale" : "2x", "role" : "quickLook", "subtype" : "42mm" + }, + { + "size" : "108x108", + "idiom" : "watch", + "scale" : "2x", + "role" : "quickLook", + "subtype" : "44mm" + }, + { + "idiom" : "watch-marketing", + "size" : "1024x1024", + "scale" : "1x" + }, + { + "idiom" : "car", + "size" : "120x120", + "scale" : "1x" + }, + { + "size" : "44x44", + "idiom" : "watch", + "scale" : "2x", + "role" : "longLook", + "subtype" : "42mm" } ], "info" : { diff --git a/Tests/XLForm Tests.xcodeproj/project.pbxproj b/Tests/XLForm Tests.xcodeproj/project.pbxproj index c8fd9858..d9ee4d88 100644 --- a/Tests/XLForm Tests.xcodeproj/project.pbxproj +++ b/Tests/XLForm Tests.xcodeproj/project.pbxproj @@ -150,7 +150,6 @@ 28657A361990879200CE8180 /* Sources */, 28657A371990879200CE8180 /* Frameworks */, 28657A381990879200CE8180 /* Resources */, - 51F3E228D166F2E8BF8C889E /* [CP] Embed Pods Frameworks */, 7E96395A3A94BDD9487AE471 /* [CP] Copy Pods Resources */, ); buildRules = ( @@ -217,37 +216,22 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 51F3E228D166F2E8BF8C889E /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-XLForm Tests/Pods-XLForm Tests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 7E96395A3A94BDD9487AE471 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-XLForm Tests/Pods-XLForm Tests-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-XLForm Tests/Pods-XLForm Tests-resources.sh", "${PODS_ROOT}/../../XLForm/XLForm.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/XLForm.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-XLForm Tests/Pods-XLForm Tests-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-XLForm Tests/Pods-XLForm Tests-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ diff --git a/XLForm.podspec b/XLForm.podspec index 9acf914a..72e5158f 100644 --- a/XLForm.podspec +++ b/XLForm.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'XLForm' - s.version = '4.0.0' + s.version = '4.0.1' s.license = { :type => 'MIT' } s.summary = 'XLForm is the most flexible and powerful iOS library to create dynamic table-view forms.' s.description = <<-DESC diff --git a/XLForm.xcodeproj/project.pbxproj b/XLForm.xcodeproj/project.pbxproj index ad0b977b..e082eb0d 100644 --- a/XLForm.xcodeproj/project.pbxproj +++ b/XLForm.xcodeproj/project.pbxproj @@ -414,7 +414,7 @@ E267FD171BE8048900F86B42 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = XLForm; TargetAttributes = { E267FD1F1BE8048900F86B42 = { @@ -509,12 +509,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -564,12 +566,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/XLForm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/XLForm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/XLForm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/XLForm.xcodeproj/xcshareddata/xcschemes/XLForm.xcscheme b/XLForm.xcodeproj/xcshareddata/xcschemes/XLForm.xcscheme index 1c1c2367..20fc64ab 100644 --- a/XLForm.xcodeproj/xcshareddata/xcschemes/XLForm.xcscheme +++ b/XLForm.xcodeproj/xcshareddata/xcschemes/XLForm.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/XLForm/XL/Cell/XLFormBaseCell.h b/XLForm/XL/Cell/XLFormBaseCell.h index 591907a0..a11ba2cb 100644 --- a/XLForm/XL/Cell/XLFormBaseCell.h +++ b/XLForm/XL/Cell/XLFormBaseCell.h @@ -42,8 +42,8 @@ @protocol XLFormReturnKeyProtocol -@property UIReturnKeyType returnKeyType; -@property UIReturnKeyType nextReturnKeyType; +@property (nonatomic, assign) UIReturnKeyType returnKeyType; +@property (nonatomic, assign) UIReturnKeyType nextReturnKeyType; @end diff --git a/XLForm/XL/Cell/XLFormDateCell.h b/XLForm/XL/Cell/XLFormDateCell.h index de8f2a96..da53e81d 100644 --- a/XLForm/XL/Cell/XLFormDateCell.h +++ b/XLForm/XL/Cell/XLFormDateCell.h @@ -34,10 +34,10 @@ typedef NS_ENUM(NSUInteger, XLFormDateDatePickerMode) { @interface XLFormDateCell : XLFormBaseCell -@property (nonatomic) XLFormDateDatePickerMode formDatePickerMode; -@property (nonatomic) NSDate *minimumDate; -@property (nonatomic) NSDate *maximumDate; -@property (nonatomic) NSInteger minuteInterval; -@property (nonatomic) NSLocale *locale; +@property (nonatomic, assign) XLFormDateDatePickerMode formDatePickerMode; +@property (nonatomic, copy ) NSDate *minimumDate; +@property (nonatomic, copy ) NSDate *maximumDate; +@property (nonatomic, assign) NSInteger minuteInterval; +@property (nonatomic, copy ) NSLocale *locale; @end diff --git a/XLForm/XL/Cell/XLFormDatePickerCell.m b/XLForm/XL/Cell/XLFormDatePickerCell.m index 9f9f7aa1..bc277509 100644 --- a/XLForm/XL/Cell/XLFormDatePickerCell.m +++ b/XLForm/XL/Cell/XLFormDatePickerCell.m @@ -37,16 +37,6 @@ -(BOOL)canResignFirstResponder return YES; } -#pragma mark - Properties - --(UIDatePicker *)datePicker -{ - if (_datePicker) return _datePicker; - _datePicker = [UIDatePicker autolayoutView]; - [_datePicker addTarget:self action:@selector(datePickerValueChanged:) forControlEvents:UIControlEventValueChanged]; - return _datePicker; -} - #pragma mark- Target Action - (void)datePickerValueChanged:(UIDatePicker *)sender @@ -66,7 +56,12 @@ - (void)datePickerValueChanged:(UIDatePicker *)sender -(void)configure { [super configure]; - [self.contentView addSubview:self.datePicker]; + + UIDatePicker *datePicker = [UIDatePicker autolayoutView]; + [datePicker addTarget:self action:@selector(datePickerValueChanged:) forControlEvents:UIControlEventValueChanged]; + [self.contentView addSubview:datePicker]; + _datePicker = datePicker; + [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.datePicker attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[datePicker]-0-|" options:0 metrics:0 views:@{@"datePicker" : self.datePicker}]]; } diff --git a/XLForm/XL/Cell/XLFormImageCell.m b/XLForm/XL/Cell/XLFormImageCell.m index 25de18c7..3ce8ddf8 100644 --- a/XLForm/XL/Cell/XLFormImageCell.m +++ b/XLForm/XL/Cell/XLFormImageCell.m @@ -71,6 +71,7 @@ - (UIImageView *)imageView - (void)formDescriptorCellDidSelectedWithFormController:(XLFormViewController *)controller { + __weak typeof(self) weak = self; alertController = [UIAlertController alertControllerWithTitle: self.rowDescriptor.title message: nil preferredStyle: UIAlertControllerStyleActionSheet]; @@ -78,14 +79,14 @@ - (void)formDescriptorCellDidSelectedWithFormController:(XLFormViewController *) [alertController addAction:[UIAlertAction actionWithTitle: NSLocalizedString(@"Choose From Library", nil) style: UIAlertActionStyleDefault handler: ^(UIAlertAction * _Nonnull action) { - [self openImage:UIImagePickerControllerSourceTypePhotoLibrary]; + [weak openImage:UIImagePickerControllerSourceTypePhotoLibrary]; }]]; if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { [alertController addAction:[UIAlertAction actionWithTitle: NSLocalizedString(@"Take Photo", nil) style: UIAlertActionStyleDefault handler: ^(UIAlertAction * _Nonnull action) { - [self openImage:UIImagePickerControllerSourceTypeCamera]; + [weak openImage:UIImagePickerControllerSourceTypeCamera]; }]]; } @@ -100,7 +101,7 @@ - (void)formDescriptorCellDidSelectedWithFormController:(XLFormViewController *) } dispatch_async(dispatch_get_main_queue(), ^{ - [self.formViewController presentViewController:alertController animated: true completion: nil]; + [weak.formViewController presentViewController:self->alertController animated: true completion: nil]; }); } diff --git a/XLForm/XL/Cell/XLFormLeftRightSelectorCell.m b/XLForm/XL/Cell/XLFormLeftRightSelectorCell.m index ab7d44e3..9cd497ad 100644 --- a/XLForm/XL/Cell/XLFormLeftRightSelectorCell.m +++ b/XLForm/XL/Cell/XLFormLeftRightSelectorCell.m @@ -196,14 +196,14 @@ -(void)leftButtonPressed:(UIButton *)leftButton [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]]; - __weak __typeof(self)weakSelf = self; + __weak typeof(self) weak = self; for (XLFormLeftRightSelectorOption * leftOption in self.rowDescriptor.selectorOptions) { [alertController addAction:[UIAlertAction actionWithTitle:[leftOption.leftValue displayText] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { - weakSelf.rowDescriptor.value = [self chooseNewRightValueFromOption:leftOption]; - weakSelf.rowDescriptor.leftRightSelectorLeftOptionSelected = [self leftOptionForDescription:[leftOption.leftValue displayText]].leftValue; - [weakSelf.formViewController updateFormRow:weakSelf.rowDescriptor]; + weak.rowDescriptor.value = [weak chooseNewRightValueFromOption:leftOption]; + weak.rowDescriptor.leftRightSelectorLeftOptionSelected = [weak leftOptionForDescription:[leftOption.leftValue displayText]].leftValue; + [weak.formViewController updateFormRow:weak.rowDescriptor]; }]]; } diff --git a/XLForm/XL/Cell/XLFormPickerCell.h b/XLForm/XL/Cell/XLFormPickerCell.h index 3b2194a2..d238ee4a 100644 --- a/XLForm/XL/Cell/XLFormPickerCell.h +++ b/XLForm/XL/Cell/XLFormPickerCell.h @@ -28,6 +28,6 @@ @interface XLFormPickerCell : XLFormBaseCell -@property (nonatomic) UIPickerView * pickerView; +@property (nonatomic, weak) UIPickerView * pickerView; @end diff --git a/XLForm/XL/Cell/XLFormPickerCell.m b/XLForm/XL/Cell/XLFormPickerCell.m index fed9dc17..cfe4a3a8 100644 --- a/XLForm/XL/Cell/XLFormPickerCell.m +++ b/XLForm/XL/Cell/XLFormPickerCell.m @@ -56,23 +56,19 @@ -(BOOL)canBecomeFirstResponder return [self formDescriptorCellCanBecomeFirstResponder]; } -#pragma mark - Properties - --(UIPickerView *)pickerView -{ - if (_pickerView) return _pickerView; - _pickerView = [UIPickerView autolayoutView]; - _pickerView.delegate = self; - _pickerView.dataSource = self; - return _pickerView; -} - #pragma mark - XLFormDescriptorCell -(void)configure { [super configure]; - [self.contentView addSubview:self.pickerView]; + + UIPickerView *pickerView = [UIPickerView autolayoutView]; + pickerView.delegate = self; + pickerView.dataSource = self; + + [self.contentView addSubview:pickerView]; + _pickerView = pickerView; + [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.pickerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[pickerView]-0-|" options:0 metrics:0 views:@{@"pickerView" : self.pickerView}]]; } diff --git a/XLForm/XL/Cell/XLFormSegmentedCell.m b/XLForm/XL/Cell/XLFormSegmentedCell.m index cb965084..f98fe356 100644 --- a/XLForm/XL/Cell/XLFormSegmentedCell.m +++ b/XLForm/XL/Cell/XLFormSegmentedCell.m @@ -30,7 +30,7 @@ @interface XLFormSegmentedCell() -@property NSMutableArray * dynamicCustomConstraints; +@property (nonatomic, strong) NSMutableArray * dynamicCustomConstraints; @end @@ -168,6 +168,9 @@ -(void)updateConstraints -(void)dealloc { [self.textLabel removeObserver:self forKeyPath:@"text"]; + + [self.dynamicCustomConstraints removeAllObjects]; + self.dynamicCustomConstraints = nil; } @end diff --git a/XLForm/XL/Cell/XLFormTextFieldCell.h b/XLForm/XL/Cell/XLFormTextFieldCell.h index 4e625010..19294bdf 100644 --- a/XLForm/XL/Cell/XLFormTextFieldCell.h +++ b/XLForm/XL/Cell/XLFormTextFieldCell.h @@ -31,10 +31,10 @@ extern NSString *const XLFormTextFieldMaxNumberOfCharacters; @interface XLFormTextFieldCell : XLFormBaseCell -@property (nonatomic, readonly) UILabel * textLabel; -@property (nonatomic, readonly) UITextField * textField; +@property (nonatomic, readonly, weak) UILabel * textLabel; +@property (nonatomic, readonly, weak) UITextField * textField; -@property (nonatomic) NSNumber *textFieldLengthPercentage; -@property (nonatomic) NSNumber *textFieldMaxNumberOfCharacters; +@property (nonatomic, copy) NSNumber *textFieldLengthPercentage; +@property (nonatomic, copy) NSNumber *textFieldMaxNumberOfCharacters; @end diff --git a/XLForm/XL/Cell/XLFormTextFieldCell.m b/XLForm/XL/Cell/XLFormTextFieldCell.m index 556493c4..7683328e 100644 --- a/XLForm/XL/Cell/XLFormTextFieldCell.m +++ b/XLForm/XL/Cell/XLFormTextFieldCell.m @@ -34,7 +34,7 @@ @interface XLFormTextFieldCell() -@property NSMutableArray * dynamicCustomConstraints; +@property (nonatomic, strong) NSMutableArray * dynamicCustomConstraints; @end @@ -69,8 +69,14 @@ -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStri -(void)dealloc { + [self.textField removeTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; [self.textLabel removeObserver:self forKeyPath:@"text"]; [self.imageView removeObserver:self forKeyPath:@"image"]; + + self.textField.delegate = nil; + + [self.dynamicCustomConstraints removeAllObjects]; + self.dynamicCustomConstraints = nil; } #pragma mark - XLFormDescriptorCell @@ -79,8 +85,14 @@ -(void)configure { [super configure]; [self setSelectionStyle:UITableViewCellSelectionStyleNone]; - [self.contentView addSubview:self.textLabel]; - [self.contentView addSubview:self.textField]; + UILabel *textLabel = [UILabel autolayoutView]; + [self.contentView addSubview:textLabel]; + _textLabel = textLabel; + + UITextField *textField = [UITextField autolayoutView]; + [self.contentView addSubview:textField]; + _textField = textField; + [self.contentView addConstraints:[self layoutConstraints]]; [self.textLabel addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; [self.imageView addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; @@ -176,22 +188,6 @@ -(void)unhighlight [self.formViewController updateFormRow:self.rowDescriptor]; } -#pragma mark - Properties - --(UILabel *)textLabel -{ - if (_textLabel) return _textLabel; - _textLabel = [UILabel autolayoutView]; - return _textLabel; -} - --(UITextField *)textField -{ - if (_textField) return _textField; - _textField = [UITextField autolayoutView]; - return _textField; -} - #pragma mark - LayoutConstraints -(NSArray *)layoutConstraints @@ -225,10 +221,10 @@ -(void)updateConstraints self.dynamicCustomConstraints = [NSMutableArray arrayWithArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[image]-[label]-[textField]-|" options:0 metrics:nil views:views]]; [self.dynamicCustomConstraints addObject:[NSLayoutConstraint constraintWithItem:_textField attribute:NSLayoutAttributeWidth - relatedBy:self.textFieldLengthPercentage ? NSLayoutRelationEqual : NSLayoutRelationGreaterThanOrEqual + relatedBy:self.textFieldLengthPercentage != nil ? NSLayoutRelationEqual : NSLayoutRelationGreaterThanOrEqual toItem:self.contentView attribute:NSLayoutAttributeWidth - multiplier:self.textFieldLengthPercentage ? [self.textFieldLengthPercentage floatValue] : 0.3 + multiplier:self.textFieldLengthPercentage != nil ? [self.textFieldLengthPercentage floatValue] : 0.3 constant:0.0]]; } else{ @@ -240,10 +236,10 @@ -(void)updateConstraints self.dynamicCustomConstraints = [NSMutableArray arrayWithArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[label]-[textField]-|" options:0 metrics:nil views:views]]; [self.dynamicCustomConstraints addObject:[NSLayoutConstraint constraintWithItem:_textField attribute:NSLayoutAttributeWidth - relatedBy:self.textFieldLengthPercentage ? NSLayoutRelationEqual : NSLayoutRelationGreaterThanOrEqual + relatedBy:self.textFieldLengthPercentage != nil ? NSLayoutRelationEqual : NSLayoutRelationGreaterThanOrEqual toItem:self.contentView attribute:NSLayoutAttributeWidth - multiplier:self.textFieldLengthPercentage ? [self.textFieldLengthPercentage floatValue] : 0.3 + multiplier:self.textFieldLengthPercentage != nil ? [self.textFieldLengthPercentage floatValue] : 0.3 constant:0.0]]; } else{ @@ -279,7 +275,7 @@ - (BOOL)textFieldShouldEndEditing:(UITextField *)textField } - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { - if (self.textFieldMaxNumberOfCharacters) { + if (self.textFieldMaxNumberOfCharacters != nil) { // Check maximum length requirement NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string]; if (newString.length > self.textFieldMaxNumberOfCharacters.integerValue) { diff --git a/XLForm/XL/Cell/XLFormTextViewCell.h b/XLForm/XL/Cell/XLFormTextViewCell.h index 84f1620f..7f6e4f06 100644 --- a/XLForm/XL/Cell/XLFormTextViewCell.h +++ b/XLForm/XL/Cell/XLFormTextViewCell.h @@ -33,10 +33,10 @@ extern NSString *const XLFormTextViewMaxNumberOfCharacters; @interface XLFormTextViewCell : XLFormBaseCell @property (nonatomic, readonly) UILabel * label DEPRECATED_ATTRIBUTE DEPRECATED_MSG_ATTRIBUTE("Use textLabel instead"); -@property (nonatomic, readonly) UILabel * textLabel; -@property (nonatomic, readonly) XLFormTextView * textView; +@property (nonatomic, readonly, weak) UILabel * textLabel; +@property (nonatomic, readonly, weak) XLFormTextView * textView; -@property (nonatomic) NSNumber *textViewLengthPercentage; -@property (nonatomic) NSNumber *textViewMaxNumberOfCharacters; +@property (nonatomic, copy) NSNumber *textViewLengthPercentage; +@property (nonatomic, copy) NSNumber *textViewMaxNumberOfCharacters; @end diff --git a/XLForm/XL/Cell/XLFormTextViewCell.m b/XLForm/XL/Cell/XLFormTextViewCell.m index b02c14b6..abbe6b73 100644 --- a/XLForm/XL/Cell/XLFormTextViewCell.m +++ b/XLForm/XL/Cell/XLFormTextViewCell.m @@ -70,34 +70,26 @@ -(void)dealloc #pragma mark - Properties --(UILabel *)textLabel -{ - if (_textLabel) return _textLabel; - _textLabel = [UILabel autolayoutView]; - [_textLabel setContentHuggingPriority:500 forAxis:UILayoutConstraintAxisHorizontal]; - return _textLabel; -} - -(UILabel *)label { return self.textLabel; } --(XLFormTextView *)textView -{ - if (_textView) return _textView; - _textView = [XLFormTextView autolayoutView]; - return _textView; -} - #pragma mark - XLFormDescriptorCell -(void)configure { [super configure]; [self setSelectionStyle:UITableViewCellSelectionStyleNone]; - [self.contentView addSubview:self.textLabel]; - [self.contentView addSubview:self.textView]; + UILabel *textLabel = [UILabel autolayoutView]; + [textLabel setContentHuggingPriority:500 forAxis:UILayoutConstraintAxisHorizontal]; + [self.contentView addSubview:textLabel]; + _textLabel = textLabel; + + XLFormTextView *textView = [XLFormTextView autolayoutView]; + [self.contentView addSubview:textView]; + _textView = textView; + [self.textLabel addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; NSDictionary * views = @{@"label": self.textLabel, @"textView": self.textView}; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[label]" options:0 metrics:0 views:views]]; @@ -160,7 +152,7 @@ -(void)updateConstraints } else{ [_dynamicCustomConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[label]-[textView]-|" options:0 metrics:0 views:views]]; - if (self.textViewLengthPercentage) { + if (self.textViewLengthPercentage != nil) { [_dynamicCustomConstraints addObject:[NSLayoutConstraint constraintWithItem:_textView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual @@ -207,7 +199,7 @@ -(void)textViewDidChange:(UITextView *)textView{ } - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { - if (self.textViewMaxNumberOfCharacters) { + if (self.textViewMaxNumberOfCharacters != nil) { // Check maximum length requirement NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text]; if (newText.length > self.textViewMaxNumberOfCharacters.integerValue) { diff --git a/XLForm/XL/Controllers/XLFormOptionsObject.h b/XLForm/XL/Controllers/XLFormOptionsObject.h index de2382b4..4cd69b05 100644 --- a/XLForm/XL/Controllers/XLFormOptionsObject.h +++ b/XLForm/XL/Controllers/XLFormOptionsObject.h @@ -28,8 +28,8 @@ @interface XLFormOptionsObject : NSObject -@property (nonatomic) NSString * formDisplaytext; -@property (nonatomic) id formValue; +@property (nonatomic, copy) NSString * formDisplaytext; +@property (nonatomic, strong) id formValue; +(XLFormOptionsObject *)formOptionsObjectWithValue:(id)value displayText:(NSString *)displayText; +(XLFormOptionsObject *)formOptionsOptionForValue:(id)value fromOptions:(NSArray *)options; diff --git a/XLForm/XL/Controllers/XLFormOptionsObject.m b/XLForm/XL/Controllers/XLFormOptionsObject.m index 05deb252..7ff7f393 100644 --- a/XLForm/XL/Controllers/XLFormOptionsObject.m +++ b/XLForm/XL/Controllers/XLFormOptionsObject.m @@ -107,4 +107,9 @@ -(instancetype)initWithCoder:(NSCoder *)decoder } +-(NSUInteger)hash +{ + return [[self formValue ] hash]; +} + @end diff --git a/XLForm/XL/Controllers/XLFormOptionsViewController.m b/XLForm/XL/Controllers/XLFormOptionsViewController.m index c8524394..a40e9e33 100644 --- a/XLForm/XL/Controllers/XLFormOptionsViewController.m +++ b/XLForm/XL/Controllers/XLFormOptionsViewController.m @@ -34,8 +34,8 @@ @interface XLFormOptionsViewController () -@property NSString * titleHeaderSection; -@property NSString * titleFooterSection; +@property (nonatomic, copy) NSString * titleHeaderSection; +@property (nonatomic, copy) NSString * titleFooterSection; @end diff --git a/XLForm/XL/Controllers/XLFormRowDescriptorViewController.h b/XLForm/XL/Controllers/XLFormRowDescriptorViewController.h index 2bfff341..1ad8944a 100644 --- a/XLForm/XL/Controllers/XLFormRowDescriptorViewController.h +++ b/XLForm/XL/Controllers/XLFormRowDescriptorViewController.h @@ -30,6 +30,6 @@ @protocol XLFormRowDescriptorViewController @required -@property (nonatomic) XLFormRowDescriptor * rowDescriptor; +@property (nonatomic, weak) XLFormRowDescriptor * rowDescriptor; @end diff --git a/XLForm/XL/Controllers/XLFormViewController.h b/XLForm/XL/Controllers/XLFormViewController.h index 82293dd6..d93fab8b 100644 --- a/XLForm/XL/Controllers/XLFormViewController.h +++ b/XLForm/XL/Controllers/XLFormViewController.h @@ -81,8 +81,8 @@ typedef NS_ENUM(NSUInteger, XLFormRowNavigationDirection) { @interface XLFormViewController : UIViewController -@property XLFormDescriptor * form; -@property IBOutlet UITableView * tableView; +@property (nonatomic, strong) XLFormDescriptor * form; +@property (nonatomic, weak) IBOutlet UITableView * tableView; -(instancetype)initWithForm:(XLFormDescriptor *)form; -(instancetype)initWithForm:(XLFormDescriptor *)form style:(UITableViewStyle)style; diff --git a/XLForm/XL/Controllers/XLFormViewController.m b/XLForm/XL/Controllers/XLFormViewController.m index 97180101..d112476e 100755 --- a/XLForm/XL/Controllers/XLFormViewController.m +++ b/XLForm/XL/Controllers/XLFormViewController.m @@ -33,7 +33,6 @@ @interface XLFormRowDescriptor(_XLFormViewController) -@property (readonly) NSArray * observers; -(BOOL)evaluateIsDisabled; -(BOOL)evaluateIsHidden; @@ -47,7 +46,7 @@ -(BOOL)evaluateIsHidden; @interface XLFormDescriptor (_XLFormViewController) -@property NSMutableDictionary* rowObservers; +@property (atomic, strong) NSMutableDictionary* rowObservers; @end @@ -57,8 +56,8 @@ @interface XLFormViewController() NSNumber *_oldBottomTableContentInset; CGRect _keyboardFrame; } -@property UITableViewStyle tableViewStyle; -@property (nonatomic) XLFormRowNavigationAccessoryView * navigationAccessoryView; +@property (nonatomic, assign) UITableViewStyle tableViewStyle; +@property (nonatomic, strong) XLFormRowNavigationAccessoryView * navigationAccessoryView; @end @@ -104,25 +103,34 @@ -(instancetype)initWithCoder:(NSCoder *)aDecoder return self; } -- (void)dealloc +-(void)dealloc { + [self removeObserverFromController]; + self.tableView.delegate = nil; self.tableView.dataSource = nil; + + self.form.delegate = nil; + + self.navigationAccessoryView = nil; } -- (void)viewDidLoad +-(void)viewDidLoad { [super viewDidLoad]; - if (!self.tableView){ - self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds + + UITableView *tableView = self.tableView; + if (!tableView){ + tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:self.tableViewStyle]; - self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - if([self.tableView respondsToSelector:@selector(cellLayoutMarginsFollowReadableWidth)]){ - self.tableView.cellLayoutMarginsFollowReadableWidth = NO; + tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + if([tableView respondsToSelector:@selector(cellLayoutMarginsFollowReadableWidth)]){ + tableView.cellLayoutMarginsFollowReadableWidth = NO; } } - if (!self.tableView.superview){ - [self.view addSubview:self.tableView]; + if (!tableView.superview){ + [self.view addSubview:tableView]; + self.tableView = tableView; } if (!self.tableView.delegate){ self.tableView.delegate = self; @@ -147,44 +155,18 @@ -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; NSIndexPath *selected = [self.tableView indexPathForSelectedRow]; - if (selected){ + if (selected) { // Trigger a cell refresh XLFormRowDescriptor * rowDescriptor = [self.form formRowAtIndex:selected]; [self updateFormRow:rowDescriptor]; [self.tableView selectRowAtIndexPath:selected animated:NO scrollPosition:UITableViewScrollPositionNone]; [self.tableView deselectRowAtIndexPath:selected animated:YES]; } - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(contentSizeCategoryChanged:) - name:UIContentSizeCategoryDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWillShow:) - name:UIKeyboardWillShowNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWillHide:) - name:UIKeyboardWillHideNotification - object:nil]; - -} - --(void)viewDidDisappear:(BOOL)animated -{ - [super viewDidDisappear:animated]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:UIContentSizeCategoryDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:UIKeyboardWillShowNotification - object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:UIKeyboardWillHideNotification - object:nil]; + + [self addObserverToController]; } -- (void)viewDidAppear:(BOOL)animated +-(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (self.form.assignFirstResponderOnShow) { @@ -193,11 +175,40 @@ - (void)viewDidAppear:(BOOL)animated } } +-(void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + [self removeObserverFromController]; +} + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } +-(void)addObserverToController { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self + selector:@selector(contentSizeCategoryChanged:) + name:UIContentSizeCategoryDidChangeNotification + object:nil]; + [nc addObserver:self + selector:@selector(keyboardWillShow:) + name:UIKeyboardWillShowNotification + object:nil]; + [nc addObserver:self + selector:@selector(keyboardWillHide:) + name:UIKeyboardWillHideNotification + object:nil]; +} + +-(void)removeObserverFromController { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil]; + [nc removeObserver:self name:UIKeyboardWillShowNotification object:nil]; + [nc removeObserver:self name:UIKeyboardWillHideNotification object:nil]; +} #pragma mark - CellClasses +(NSMutableDictionary *)cellClassesForRowDescriptorTypes @@ -314,8 +325,8 @@ -(void)formRowDescriptorPredicateHasChanged:(XLFormRowDescriptor *)formRow oldVa -(void)updateAfterDependentRowChanged:(XLFormRowDescriptor *)formRow { - NSMutableArray* revaluateHidden = self.form.rowObservers[[formRow.tag formKeyForPredicateType:XLPredicateTypeHidden]]; - NSMutableArray* revaluateDisabled = self.form.rowObservers[[formRow.tag formKeyForPredicateType:XLPredicateTypeDisabled]]; + NSMutableArray* revaluateHidden = [self.form.rowObservers[[formRow.tag formKeyForPredicateType:XLPredicateTypeHidden]] mutableCopy]; + NSMutableArray* revaluateDisabled = [self.form.rowObservers[[formRow.tag formKeyForPredicateType:XLPredicateTypeDisabled]] mutableCopy]; for (id object in revaluateDisabled) { if ([object isKindOfClass:[NSString class]]) { XLFormRowDescriptor* row = [self.form formRowWithTag:object]; @@ -434,9 +445,10 @@ -(void)multivaluedInsertButtonTapped:(XLFormRowDescriptor *)formRow XLFormSectionDescriptor * multivaluedFormSection = formRow.sectionDescriptor; XLFormRowDescriptor * formRowDescriptor = [self formRowFormMultivaluedFormSection:multivaluedFormSection]; [multivaluedFormSection addFormRow:formRowDescriptor]; + __weak typeof(self) weak = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - self.tableView.editing = !self.tableView.editing; - self.tableView.editing = !self.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; }); UITableViewCell * cell = (UITableViewCell *)[formRowDescriptor cellForFormController:self]; if ([cell formDescriptorCellCanBecomeFirstResponder]){ @@ -568,18 +580,24 @@ -(void)reloadFormRow:(XLFormRowDescriptor *)formRow -(XLFormBaseCell *)updateFormRow:(XLFormRowDescriptor *)formRow { XLFormBaseCell * cell = [formRow cellForFormController:self]; - [self configureCell:cell]; - [cell setNeedsUpdateConstraints]; - [cell setNeedsLayout]; + if (cell != nil) { + [self configureCell:cell]; + [cell setNeedsUpdateConstraints]; + [cell setNeedsLayout]; + } return cell; } -(void)configureCell:(XLFormBaseCell*) cell { [cell update]; - [cell.rowDescriptor.cellConfig enumerateKeysAndObjectsUsingBlock:^(NSString *keyPath, id value, BOOL * __unused stop) { - [cell setValue:(value == [NSNull null]) ? nil : value forKeyPath:keyPath]; - }]; + + if(cell.rowDescriptor != nil && cell.rowDescriptor.cellConfig != nil) { + [cell.rowDescriptor.cellConfig enumerateKeysAndObjectsUsingBlock:^(NSString *keyPath, id value, BOOL * __unused stop) { + [cell setValue:(value == [NSNull null]) ? nil : value forKeyPath:keyPath]; + }]; + } + if (cell.rowDescriptor.isDisabled){ [cell.rowDescriptor.cellConfigIfDisabled enumerateKeysAndObjectsUsingBlock:^(NSString *keyPath, id value, BOOL * __unused stop) { [cell setValue:(value == [NSNull null]) ? nil : value forKeyPath:keyPath]; @@ -650,9 +668,10 @@ - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sou #pragma GCC diagnostic pop // update the accessory view [self inputAccessoryViewForRowDescriptor:row]; + __weak typeof(self) weak = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - self.tableView.editing = !self.tableView.editing; - self.tableView.editing = !self.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; }); } @@ -667,9 +686,10 @@ -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEdi [self.tableView endEditing:YES]; } [multivaluedFormRow.sectionDescriptor removeFormRowAtIndex:indexPath.row]; + __weak typeof(self) weak = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - self.tableView.editing = !self.tableView.editing; - self.tableView.editing = !self.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; }); if (firstResponder){ UITableViewCell * firstResponderCell = [firstResponder formDescriptorCell]; @@ -686,9 +706,10 @@ -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEdi else{ XLFormRowDescriptor * formRowDescriptor = [self formRowFormMultivaluedFormSection:multivaluedFormSection]; [multivaluedFormSection addFormRow:formRowDescriptor]; + __weak typeof(self) weak = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - self.tableView.editing = !self.tableView.editing; - self.tableView.editing = !self.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; + weak.tableView.editing = !weak.tableView.editing; }); [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section] atScrollPosition:UITableViewScrollPositionBottom animated:YES]; UITableViewCell * cell = (UITableViewCell *)[formRowDescriptor cellForFormController:self]; diff --git a/XLForm/XL/Descriptors/XLFormDescriptor.h b/XLForm/XL/Descriptors/XLFormDescriptor.h index 938382ca..5d6a12e7 100644 --- a/XLForm/XL/Descriptors/XLFormDescriptor.h +++ b/XLForm/XL/Descriptors/XLFormDescriptor.h @@ -49,15 +49,15 @@ typedef NS_OPTIONS(NSUInteger, XLFormRowNavigationOptions) { @interface XLFormDescriptor : NSObject -@property (readonly, nonatomic, nonnull) NSMutableArray * formSections; -@property (readonly, nullable) NSString * title; -@property (nonatomic) BOOL endEditingTableViewOnScroll; -@property (nonatomic) BOOL assignFirstResponderOnShow; -@property (nonatomic) BOOL addAsteriskToRequiredRowsTitle; -@property (getter=isDisabled) BOOL disabled; -@property (nonatomic) XLFormRowNavigationOptions rowNavigationOptions; - -@property (weak, nullable) id delegate; +@property (nonatomic, strong, readonly, nonnull) NSMutableArray * formSections; +@property (nonatomic, readonly, nullable, copy) NSString * title; +@property (nonatomic, assign) BOOL endEditingTableViewOnScroll; +@property (nonatomic, assign) BOOL assignFirstResponderOnShow; +@property (nonatomic, assign) BOOL addAsteriskToRequiredRowsTitle; +@property (nonatomic, getter=isDisabled, assign) BOOL disabled; +@property (nonatomic, assign) XLFormRowNavigationOptions rowNavigationOptions; + +@property (nonatomic, weak, nullable) id delegate; +(nonnull instancetype)formDescriptor; +(nonnull instancetype)formDescriptorWithTitle:(nullable NSString *)title; diff --git a/XLForm/XL/Descriptors/XLFormDescriptor.m b/XLForm/XL/Descriptors/XLFormDescriptor.m index 1b6926c7..dc7021ca 100644 --- a/XLForm/XL/Descriptors/XLFormDescriptor.m +++ b/XLForm/XL/Descriptors/XLFormDescriptor.m @@ -32,10 +32,13 @@ NSString * const XLFormErrorDomain = @"XLFormErrorDomain"; NSString * const XLValidationStatusErrorKey = @"XLValidationStatusErrorKey"; +NSString * const XLFormSectionsKey = @"formSections"; + @interface XLFormSectionDescriptor (_XLFormDescriptor) -@property NSArray * allRows; +@property (nonatomic, weak) NSArray *allRows; + -(BOOL)evaluateIsHidden; @end @@ -51,11 +54,11 @@ -(BOOL)evaluateIsHidden; @interface XLFormDescriptor() -@property NSMutableArray * formSections; -@property (readonly) NSMutableArray * allSections; -@property NSString * title; -@property (readonly) NSMutableDictionary* allRowsByTag; -@property NSMutableDictionary* rowObservers; +@property (nonatomic, strong) NSMutableArray *formSections; +@property (nonatomic, strong, readonly) NSMutableArray *allSections; +@property (nonatomic, copy ) NSString *title; +@property (nonatomic, strong, readonly) NSMutableDictionary *allRowsByTag; +@property (atomic , strong) NSMutableDictionary *rowObservers; @end @@ -63,15 +66,14 @@ @implementation XLFormDescriptor -(instancetype)init { - return [self initWithTitle:nil]; + return [self initWithTitle:@""]; } -(instancetype)initWithTitle:(NSString *)title; { - self = [super init]; - if (self){ + if (self = [super init]) { _formSections = [NSMutableArray array]; - _allSections = [NSMutableArray array]; + _allSections = [NSMutableArray array]; _allRowsByTag = [NSMutableDictionary dictionary]; _rowObservers = [NSMutableDictionary dictionary]; _title = title; @@ -79,14 +81,19 @@ -(instancetype)initWithTitle:(NSString *)title; _disabled = NO; _endEditingTableViewOnScroll = YES; _rowNavigationOptions = XLFormRowNavigationOptionEnabled; - [self addObserver:self forKeyPath:@"formSections" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:0]; + + [self addObserver:self + forKeyPath:XLFormSectionsKey + options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) + context:0]; } + return self; } +(instancetype)formDescriptor { - return [[self class] formDescriptorWithTitle:nil]; + return [[self class] formDescriptorWithTitle:@""]; } +(instancetype)formDescriptorWithTitle:(NSString *)title @@ -101,11 +108,11 @@ -(void)addFormSection:(XLFormSectionDescriptor *)formSection -(void)addFormSection:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index { - if (index == 0){ + if (index == 0) { [self insertObject:formSection inAllSectionsAtIndex:0]; } - else{ - XLFormSectionDescriptor* previousSection = [self.formSections objectAtIndex:MIN(self.formSections.count, index-1)]; + else { + XLFormSectionDescriptor *previousSection = [self.formSections objectAtIndex:MIN(self.formSections.count, index-1)]; [self addFormSection:formSection afterSection:previousSection]; } } @@ -114,26 +121,28 @@ -(void)addFormSection:(XLFormSectionDescriptor *)formSection afterSection:(XLFor { NSUInteger sectionIndex; NSUInteger allSectionIndex; - if ((sectionIndex = [self.allSections indexOfObject:formSection]) == NSNotFound){ + if ((sectionIndex = [self.allSections indexOfObject:formSection]) == NSNotFound) { allSectionIndex = [self.allSections indexOfObject:afterSection]; if (allSectionIndex != NSNotFound) { [self insertObject:formSection inAllSectionsAtIndex:(allSectionIndex + 1)]; } else { //case when afterSection does not exist. Just insert at the end. [self addFormSection:formSection]; + return; } } + formSection.hidden = formSection.hidden; } -(void)addFormRow:(XLFormRowDescriptor *)formRow beforeRow:(XLFormRowDescriptor *)beforeRow { - if (beforeRow.sectionDescriptor){ + if (beforeRow.sectionDescriptor) { [beforeRow.sectionDescriptor addFormRow:formRow beforeRow:beforeRow]; } - else{ + else { [[self.allSections lastObject] addFormRow:formRow beforeRow:beforeRow]; } } @@ -144,27 +153,25 @@ -(void)addFormRow:(XLFormRowDescriptor *)formRow beforeRowTag:(NSString *)before [self addFormRow:formRow beforeRow:beforeRowForm]; } - - -(void)addFormRow:(XLFormRowDescriptor *)formRow afterRow:(XLFormRowDescriptor *)afterRow { - if (afterRow.sectionDescriptor){ + if (afterRow.sectionDescriptor) { [afterRow.sectionDescriptor addFormRow:formRow afterRow:afterRow]; } - else{ + else { [[self.allSections lastObject] addFormRow:formRow afterRow:afterRow]; } } -(void)addFormRow:(XLFormRowDescriptor *)formRow afterRowTag:(NSString *)afterRowTag { - XLFormRowDescriptor * afterRowForm = [self formRowWithTag:afterRowTag]; + XLFormRowDescriptor *afterRowForm = [self formRowWithTag:afterRowTag]; [self addFormRow:formRow afterRow:afterRowForm]; } -(void)removeFormSectionAtIndex:(NSUInteger)index { - if (self.formSections.count > index){ + if (self.formSections.count > index) { XLFormSectionDescriptor *formSection = [self.formSections objectAtIndex:index]; [self removeObjectFromFormSectionsAtIndex:index]; NSUInteger allSectionIndex = [self.allSections indexOfObject:formSection]; @@ -175,43 +182,47 @@ -(void)removeFormSectionAtIndex:(NSUInteger)index -(void)removeFormSection:(XLFormSectionDescriptor *)formSection { NSUInteger index = NSNotFound; - if ((index = [self.formSections indexOfObject:formSection]) != NSNotFound){ + if ((index = [self.formSections indexOfObject:formSection]) != NSNotFound) { [self removeFormSectionAtIndex:index]; } - else if ((index = [self.allSections indexOfObject:formSection]) != NSNotFound){ + if ((index = [self.allSections indexOfObject:formSection]) != NSNotFound) { [self removeObjectFromAllSectionsAtIndex:index]; - }; + } } -(void)removeFormRow:(XLFormRowDescriptor *)formRow { - for (XLFormSectionDescriptor * section in self.formSections){ - if ([section.formRows containsObject:formRow]){ + for (XLFormSectionDescriptor *section in self.formSections) { + if ([section.formRows containsObject:formRow]) { [section removeFormRow:formRow]; + + break; } } } --(void)showFormSection:(XLFormSectionDescriptor*)formSection +-(void)showFormSection:(XLFormSectionDescriptor *)formSection { NSUInteger formIndex = [self.formSections indexOfObject:formSection]; if (formIndex != NSNotFound) { return; } + NSUInteger index = [self.allSections indexOfObject:formSection]; - if (index != NSNotFound){ + if (index != NSNotFound) { while (formIndex == NSNotFound && index > 0) { XLFormSectionDescriptor* previous = [self.allSections objectAtIndex:--index]; formIndex = [self.formSections indexOfObject:previous]; } + [self insertObject:formSection inFormSectionsAtIndex:(formIndex == NSNotFound ? 0 : ++formIndex)]; } } --(void)hideFormSection:(XLFormSectionDescriptor*)formSection +-(void)hideFormSection:(XLFormSectionDescriptor *)formSection { NSUInteger index = [self.formSections indexOfObject:formSection]; - if (index != NSNotFound){ + if (index != NSNotFound) { [self removeObjectFromFormSectionsAtIndex:index]; } } @@ -224,28 +235,29 @@ -(XLFormRowDescriptor *)formRowWithTag:(NSString *)tag -(XLFormRowDescriptor *)formRowWithHash:(NSUInteger)hash { - for (XLFormSectionDescriptor * section in self.allSections){ - for (XLFormRowDescriptor * row in section.allRows) { - if ([row hash] == hash){ + for (XLFormSectionDescriptor *section in self.allSections) { + for (XLFormRowDescriptor *row in section.allRows) { + if ([row hash] == hash) { return row; } } } + return nil; } - -(void)removeFormRowWithTag:(NSString *)tag { - XLFormRowDescriptor * formRow = [self formRowWithTag:tag]; + XLFormRowDescriptor *formRow = [self formRowWithTag:tag]; [self removeFormRow:formRow]; } -(XLFormRowDescriptor *)formRowAtIndex:(NSIndexPath *)indexPath { - if ((self.formSections.count > indexPath.section) && [[self.formSections objectAtIndex:indexPath.section] formRows].count > indexPath.row){ + if ((self.formSections.count > indexPath.section) && [[self.formSections objectAtIndex:indexPath.section] formRows].count > indexPath.row) { return [[[self.formSections objectAtIndex:indexPath.section] formRows] objectAtIndex:indexPath.row]; } + return nil; } @@ -256,55 +268,62 @@ -(XLFormSectionDescriptor *)formSectionAtIndex:(NSUInteger)index -(NSIndexPath *)indexPathOfFormRow:(XLFormRowDescriptor *)formRow { - XLFormSectionDescriptor * section = formRow.sectionDescriptor; - if (section){ + NSIndexPath *result = nil; + XLFormSectionDescriptor *section = formRow.sectionDescriptor; + if (section) { NSUInteger sectionIndex = [self.formSections indexOfObject:section]; - if (sectionIndex != NSNotFound){ + if (sectionIndex != NSNotFound) { NSUInteger rowIndex = [section.formRows indexOfObject:formRow]; - if (rowIndex != NSNotFound){ - return [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; + if (rowIndex != NSNotFound) { + result = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; } } } - return nil; + + return result; } -(NSIndexPath *)globalIndexPathOfFormRow:(XLFormRowDescriptor *)formRow { - XLFormSectionDescriptor * section = formRow.sectionDescriptor; - if (section){ + NSIndexPath *result = nil; + XLFormSectionDescriptor *section = formRow.sectionDescriptor; + if (section) { NSUInteger sectionIndex = [self.allSections indexOfObject:section]; - if (sectionIndex != NSNotFound){ + if (sectionIndex != NSNotFound) { NSUInteger rowIndex = [section.allRows indexOfObject:formRow]; - if (rowIndex != NSNotFound){ - return [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; + if (rowIndex != NSNotFound) { + result = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; } } } - return nil; + + return result; } -(NSDictionary *)formValues { - NSMutableDictionary * result = [NSMutableDictionary dictionary]; - for (XLFormSectionDescriptor * section in self.formSections) { - if (section.multivaluedTag.length > 0){ - NSMutableArray * multiValuedValuesArray = [NSMutableArray new]; - for (XLFormRowDescriptor * row in section.formRows) { - if (row.value){ + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + for (XLFormSectionDescriptor *section in self.formSections) { + if (section.multivaluedTag.length > 0) { + NSMutableArray *multiValuedValuesArray = [NSMutableArray new]; + for (XLFormRowDescriptor *row in section.formRows) { + if (row.value && row.value != [NSNull null]) { [multiValuedValuesArray addObject:row.value]; } } + [result setObject:multiValuedValuesArray forKey:section.multivaluedTag]; } - else{ - for (XLFormRowDescriptor * row in section.formRows) { - if (row.tag.length > 0){ - [result setObject:(row.value ?: [NSNull null]) forKey:row.tag]; + else { + for (XLFormRowDescriptor *row in section.formRows) { + id value = [row.value valueData]; + if (row.tag.length > 0 && value != nil) { + [result setObject:value forKey:row.tag]; } } } } + return result; } @@ -312,49 +331,55 @@ -(NSDictionary *)httpParameters:(XLFormViewController *)formViewController { NSMutableDictionary * result = [NSMutableDictionary dictionary]; for (XLFormSectionDescriptor * section in self.formSections) { - if (section.multivaluedTag.length > 0){ - NSMutableArray * multiValuedValuesArray = [NSMutableArray new]; + if (section.multivaluedTag.length > 0) { + NSMutableArray *multiValuedValuesArray = [NSMutableArray new]; for (XLFormRowDescriptor * row in section.formRows) { - if ([row.value valueData]){ + if ([row.value valueData]) { [multiValuedValuesArray addObject:[row.value valueData]]; } } + [result setObject:multiValuedValuesArray forKey:section.multivaluedTag]; } - else{ + else { for (XLFormRowDescriptor * row in section.formRows) { - NSString * httpParameterKey = nil; - if ((httpParameterKey = [self httpParameterKeyForRow:row cell:[row cellForFormController:formViewController]])){ + NSString *httpParameterKey = nil; + if ((httpParameterKey = [self httpParameterKeyForRow:row cell:[row cellForFormController:formViewController]])) { id parameterValue = [row.value valueData] ?: [NSNull null]; [result setObject:parameterValue forKey:httpParameterKey]; } } } } + return result; } -(NSString *)httpParameterKeyForRow:(XLFormRowDescriptor *)row cell:(UITableViewCell *)descriptorCell { - if ([descriptorCell respondsToSelector:@selector(formDescriptorHttpParameterName)]){ - return [descriptorCell formDescriptorHttpParameterName]; + NSString *result = nil; + + if ([descriptorCell respondsToSelector:@selector(formDescriptorHttpParameterName)]) { + result = [descriptorCell formDescriptorHttpParameterName]; } - if (row.tag.length > 0){ - return row.tag; + else if (row.tag.length > 0) { + result = row.tag; } - return nil; + + return result; } --(NSArray *)localValidationErrors:(XLFormViewController *)formViewController { - NSMutableArray * result = [NSMutableArray array]; - for (XLFormSectionDescriptor * section in self.formSections) { - for (XLFormRowDescriptor * row in section.formRows) { - XLFormValidationStatus* status = [row doValidation]; +-(NSArray *)localValidationErrors:(XLFormViewController *)formViewController +{ + NSMutableArray *result = [NSMutableArray array]; + for (XLFormSectionDescriptor *section in self.formSections) { + for (XLFormRowDescriptor *row in section.formRows) { + XLFormValidationStatus *status = [row doValidation]; if (status != nil && (![status isValid])) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: status.msg, XLValidationStatusErrorKey: status }; - NSError * error = [[NSError alloc] initWithDomain:XLFormErrorDomain code:XLFormErrorCodeGen userInfo:userInfo]; - if (error){ + NSError *error = [[NSError alloc] initWithDomain:XLFormErrorDomain code:XLFormErrorCodeGen userInfo:userInfo]; + if (error) { [result addObject:error]; } } @@ -367,11 +392,11 @@ -(NSArray *)localValidationErrors:(XLFormViewController *)formViewController { - (void)setFirstResponder:(XLFormViewController *)formViewController { - for (XLFormSectionDescriptor * formSection in self.formSections) { - for (XLFormRowDescriptor * row in formSection.formRows) { - UITableViewCell * cell = [row cellForFormController:formViewController]; - if ([cell formDescriptorCellCanBecomeFirstResponder]){ - if ([cell formDescriptorCellBecomeFirstResponder]){ + for (XLFormSectionDescriptor *formSection in self.formSections) { + for (XLFormRowDescriptor *row in formSection.formRows) { + UITableViewCell *cell = [row cellForFormController:formViewController]; + if ([cell formDescriptorCellCanBecomeFirstResponder]) { + if ([cell formDescriptorCellBecomeFirstResponder]) { return; } } @@ -382,18 +407,23 @@ - (void)setFirstResponder:(XLFormViewController *)formViewController #pragma mark - KVO --(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +- (void)observeValueForKeyPath:(nullable NSString *)keyPath + ofObject:(nullable id)object + change:(nullable NSDictionary *)change + context:(nullable void *)context { - if (!self.delegate) return; - if ([keyPath isEqualToString:@"formSections"]){ - if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeInsertion)]){ - NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; - XLFormSectionDescriptor * section = [self.formSections objectAtIndex:indexSet.firstIndex]; + if (!self.delegate) { + return; + } + else if ([keyPath isEqualToString:XLFormSectionsKey]) { + if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeInsertion)]) { + NSIndexSet *indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; + XLFormSectionDescriptor *section = [self.formSections objectAtIndex:indexSet.firstIndex]; [self.delegate formSectionHasBeenAdded:section atIndex:indexSet.firstIndex]; } - else if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeRemoval)]){ - NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; - XLFormSectionDescriptor * removedSection = [[change objectForKey:NSKeyValueChangeOldKey] objectAtIndex:0]; + else if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeRemoval)]) { + NSIndexSet *indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; + XLFormSectionDescriptor *removedSection = [[change objectForKey:NSKeyValueChangeOldKey] objectAtIndex:0]; [self.delegate formSectionHasBeenRemoved:removedSection atIndex:indexSet.firstIndex]; } } @@ -401,10 +431,16 @@ -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS -(void)dealloc { - @try { - [self removeObserver:self forKeyPath:@"formSections"]; - } - @catch (NSException * __unused exception) {} + [self removeObserver:self forKeyPath:XLFormSectionsKey]; + + [_formSections removeAllObjects]; + _formSections = nil; + [_allSections removeAllObjects]; + _allSections = nil; + [_allRowsByTag removeAllObjects]; + _allRowsByTag = nil; + [_rowObservers removeAllObjects]; + _rowObservers = nil; } #pragma mark - KVC @@ -414,19 +450,23 @@ -(NSUInteger)countOfFormSections return self.formSections.count; } -- (id)objectInFormSectionsAtIndex:(NSUInteger)index { +- (id)objectInFormSectionsAtIndex:(NSUInteger)index +{ return [self.formSections objectAtIndex:index]; } -- (NSArray *)formSectionsAtIndexes:(NSIndexSet *)indexes { +- (NSArray *)formSectionsAtIndexes:(NSIndexSet *)indexes +{ return [self.formSections objectsAtIndexes:indexes]; } -- (void)insertObject:(XLFormSectionDescriptor *)formSection inFormSectionsAtIndex:(NSUInteger)index { +- (void)insertObject:(XLFormSectionDescriptor *)formSection inFormSectionsAtIndex:(NSUInteger)index +{ [self.formSections insertObject:formSection atIndex:index]; } -- (void)removeObjectFromFormSectionsAtIndex:(NSUInteger)index { +- (void)removeObjectFromFormSectionsAtIndex:(NSUInteger)index +{ [self.formSections removeObjectAtIndex:index]; } @@ -445,7 +485,8 @@ - (NSArray *)allSectionsAtIndexes:(NSIndexSet *)indexes { return [self.allSections objectsAtIndexes:indexes]; } -- (void)removeObjectFromAllSectionsAtIndex:(NSUInteger)index { +- (void)removeObjectFromAllSectionsAtIndex:(NSUInteger)index +{ XLFormSectionDescriptor* section = [self.allSections objectAtIndex:index]; [section.allRows enumerateObjectsUsingBlock:^(id obj, NSUInteger __unused idx, BOOL *stop) { XLFormRowDescriptor * row = (id)obj; @@ -456,7 +497,8 @@ - (void)removeObjectFromAllSectionsAtIndex:(NSUInteger)index { [self.allSections removeObjectAtIndex:index]; } -- (void)insertObject:(XLFormSectionDescriptor *)section inAllSectionsAtIndex:(NSUInteger)index { +- (void)insertObject:(XLFormSectionDescriptor *)section inAllSectionsAtIndex:(NSUInteger)index +{ section.formDescriptor = self; [self.allSections insertObject:section atIndex:index]; section.hidden = section.hidden; @@ -466,24 +508,23 @@ - (void)insertObject:(XLFormSectionDescriptor *)section inAllSectionsAtIndex:(NS row.hidden = row.hidden; row.disabled = row.disabled; }]; - - } #pragma mark - EvaluateForm -(void)forceEvaluate { - for (XLFormSectionDescriptor* section in self.allSections){ - for (XLFormRowDescriptor* row in section.allRows) { + for (XLFormSectionDescriptor *section in self.allSections) { + for (XLFormRowDescriptor *row in section.allRows) { [self addRowToTagCollection:row]; } } - for (XLFormSectionDescriptor* section in self.allSections){ - for (XLFormRowDescriptor* row in section.allRows) { + for (XLFormSectionDescriptor *section in self.allSections) { + for (XLFormRowDescriptor *row in section.allRows) { [row evaluateIsDisabled]; [row evaluateIsHidden]; } + [section evaluateIsHidden]; } } @@ -500,60 +541,65 @@ -(NSMutableArray *)formSections -(XLFormRowDescriptor *)nextRowDescriptorForRow:(XLFormRowDescriptor *)row { + XLFormRowDescriptor *result = nil; NSUInteger indexOfRow = [row.sectionDescriptor.formRows indexOfObject:row]; - if (indexOfRow != NSNotFound){ - if (indexOfRow + 1 < row.sectionDescriptor.formRows.count){ - return [row.sectionDescriptor.formRows objectAtIndex:++indexOfRow]; + if (indexOfRow != NSNotFound) { + if (indexOfRow + 1 < row.sectionDescriptor.formRows.count) { + result = [row.sectionDescriptor.formRows objectAtIndex:++indexOfRow]; } - else{ + else { NSUInteger sectionIndex = [self.formSections indexOfObject:row.sectionDescriptor]; NSUInteger numberOfSections = [self.formSections count]; - if (sectionIndex != NSNotFound && sectionIndex < numberOfSections - 1){ + if (sectionIndex != NSNotFound && sectionIndex < numberOfSections - 1) { sectionIndex++; - XLFormSectionDescriptor * sectionDescriptor; - while ([[(sectionDescriptor = [row.sectionDescriptor.formDescriptor.formSections objectAtIndex:sectionIndex]) formRows] count] == 0 && sectionIndex < numberOfSections - 1){ + XLFormSectionDescriptor *sectionDescriptor; + while ([[(sectionDescriptor = [row.sectionDescriptor.formDescriptor.formSections objectAtIndex:sectionIndex]) formRows] count] == 0 && sectionIndex < numberOfSections - 1) { sectionIndex++; } - return [sectionDescriptor.formRows firstObject]; + + result = [sectionDescriptor.formRows firstObject]; } } } - return nil; + + return result; } -(XLFormRowDescriptor *)previousRowDescriptorForRow:(XLFormRowDescriptor *)row { + XLFormRowDescriptor *result = nil; NSUInteger indexOfRow = [row.sectionDescriptor.formRows indexOfObject:row]; - if (indexOfRow != NSNotFound){ - if (indexOfRow > 0 ){ - return [row.sectionDescriptor.formRows objectAtIndex:--indexOfRow]; + if (indexOfRow != NSNotFound) { + if (indexOfRow > 0 ) { + result = [row.sectionDescriptor.formRows objectAtIndex:--indexOfRow]; } - else{ + else { NSUInteger sectionIndex = [self.formSections indexOfObject:row.sectionDescriptor]; - if (sectionIndex != NSNotFound && sectionIndex > 0){ + if (sectionIndex != NSNotFound && sectionIndex > 0) { sectionIndex--; XLFormSectionDescriptor * sectionDescriptor; - while ([[(sectionDescriptor = [row.sectionDescriptor.formDescriptor.formSections objectAtIndex:sectionIndex]) formRows] count] == 0 && sectionIndex > 0 ){ + while ([[(sectionDescriptor = [row.sectionDescriptor.formDescriptor.formSections objectAtIndex:sectionIndex]) formRows] count] == 0 && sectionIndex > 0 ) { sectionIndex--; } - return [sectionDescriptor.formRows lastObject]; + result = [sectionDescriptor.formRows lastObject]; } } } - return nil; + + return result; } --(void)addRowToTagCollection:(XLFormRowDescriptor*) rowDescriptor +-(void)addRowToTagCollection:(XLFormRowDescriptor *)rowDescriptor { - if (rowDescriptor.tag) { + if (rowDescriptor.tag.length) { self.allRowsByTag[rowDescriptor.tag] = rowDescriptor; } } -(void)removeRowFromTagCollection:(XLFormRowDescriptor *)rowDescriptor { - if (rowDescriptor.tag){ + if (rowDescriptor.tag.length) { [self.allRowsByTag removeObjectForKey:rowDescriptor.tag]; } } @@ -561,32 +607,36 @@ -(void)removeRowFromTagCollection:(XLFormRowDescriptor *)rowDescriptor -(void)addObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType { - NSPredicate* predicate; + NSPredicate *predicate; id descriptor; - switch(predicateType){ + + switch (predicateType) { case XLPredicateTypeHidden: if ([sectionOrRow isKindOfClass:([XLFormRowDescriptor class])]) { - descriptor = ((XLFormRowDescriptor*)sectionOrRow).tag; - predicate = ((XLFormRowDescriptor*)sectionOrRow).hidden; + descriptor = ((XLFormRowDescriptor *)sectionOrRow).tag; + predicate = ((XLFormRowDescriptor *)sectionOrRow).hidden; } else if ([sectionOrRow isKindOfClass:([XLFormSectionDescriptor class])]) { descriptor = sectionOrRow; - predicate = ((XLFormSectionDescriptor*)sectionOrRow).hidden; + predicate = ((XLFormSectionDescriptor *)sectionOrRow).hidden; } break; case XLPredicateTypeDisabled: if ([sectionOrRow isKindOfClass:([XLFormRowDescriptor class])]) { - descriptor = ((XLFormRowDescriptor*)sectionOrRow).tag; - predicate = ((XLFormRowDescriptor*)sectionOrRow).disabled; + descriptor = ((XLFormRowDescriptor *)sectionOrRow).tag; + predicate = ((XLFormRowDescriptor *)sectionOrRow).disabled; + } + else { + return; } - else return; break; } - NSMutableArray* tags = [predicate getPredicateVars]; - for (NSString* tag in tags) { - NSString* auxTag = [tag formKeyForPredicateType:predicateType]; - if (!self.rowObservers[auxTag]){ + + NSMutableArray *tags = [predicate getPredicateVars]; + for (NSString *tag in tags) { + NSString *auxTag = [tag formKeyForPredicateType:predicateType]; + if (!self.rowObservers[auxTag]) { self.rowObservers[auxTag] = [NSMutableArray array]; } if (![self.rowObservers[auxTag] containsObject:descriptor]) @@ -597,31 +647,32 @@ -(void)addObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)pred -(void)removeObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType { - NSPredicate* predicate; + NSPredicate *predicate; id descriptor; - switch(predicateType){ + + switch(predicateType) { case XLPredicateTypeHidden: if ([sectionOrRow isKindOfClass:([XLFormRowDescriptor class])]) { - descriptor = ((XLFormRowDescriptor*)sectionOrRow).tag; - predicate = ((XLFormRowDescriptor*)sectionOrRow).hidden; + descriptor = ((XLFormRowDescriptor *)sectionOrRow).tag; + predicate = ((XLFormRowDescriptor *)sectionOrRow).hidden; } else if ([sectionOrRow isKindOfClass:([XLFormSectionDescriptor class])]) { descriptor = sectionOrRow; - predicate = ((XLFormSectionDescriptor*)sectionOrRow).hidden; + predicate = ((XLFormSectionDescriptor *)sectionOrRow).hidden; } break; case XLPredicateTypeDisabled: if ([sectionOrRow isKindOfClass:([XLFormRowDescriptor class])]) { - descriptor = ((XLFormRowDescriptor*)sectionOrRow).tag; - predicate = ((XLFormRowDescriptor*)sectionOrRow).disabled; + descriptor = ((XLFormRowDescriptor *)sectionOrRow).tag; + predicate = ((XLFormRowDescriptor *)sectionOrRow).disabled; } break; } - if (descriptor && [predicate isKindOfClass:[NSPredicate class] ]) { - NSMutableArray* tags = [predicate getPredicateVars]; - for (NSString* tag in tags) { - NSString* auxTag = [tag formKeyForPredicateType:predicateType]; - if (self.rowObservers[auxTag]){ + if (descriptor && [predicate isKindOfClass:[NSPredicate class]]) { + NSMutableArray *tags = [predicate getPredicateVars]; + for (NSString *tag in tags) { + NSString *auxTag = [tag formKeyForPredicateType:predicateType]; + if (self.rowObservers[auxTag]) { [self.rowObservers[auxTag] removeObject:descriptor]; } } diff --git a/XLForm/XL/Descriptors/XLFormRowDescriptor.h b/XLForm/XL/Descriptors/XLFormRowDescriptor.h index 1e7a4b71..088790f6 100644 --- a/XLForm/XL/Descriptors/XLFormRowDescriptor.h +++ b/XLForm/XL/Descriptors/XLFormRowDescriptor.h @@ -43,22 +43,22 @@ typedef NS_ENUM(NSUInteger, XLFormPresentationMode) { XLFormPresentationModePresent }; -typedef void(^XLOnChangeBlock)(id __nullable oldValue,id __nullable newValue,XLFormRowDescriptor* __nonnull rowDescriptor); +typedef void(^XLOnChangeBlock)(id __nullable oldValue, id __nullable newValue, XLFormRowDescriptor * __nonnull rowDescriptor); @interface XLFormRowDescriptor : NSObject -@property (nullable) id cellClass; -@property (readwrite, nullable) NSString * tag; -@property (readonly, nonnull) NSString * rowType; -@property (nullable) NSString * title; -@property (nonatomic, nullable) id value; -@property (nullable) Class valueTransformer; -@property UITableViewCellStyle cellStyle; -@property (nonatomic) CGFloat height; +@property (nonatomic, nullable, strong) id cellClass; +@property (nonatomic, nullable, copy , readwrite) NSString * tag; +@property (nonatomic, nonnull , copy , readonly) NSString * rowType; +@property (nonatomic, nullable, copy ) NSString * title; +@property (nonatomic, nullable, strong) id value; +@property (nonatomic, nullable, strong) Class valueTransformer; +@property (nonatomic, assign ) UITableViewCellStyle cellStyle; +@property (nonatomic, assign ) CGFloat height; -@property (copy, nullable) XLOnChangeBlock onChangeBlock; -@property BOOL useValueFormatterDuringInput; -@property (nullable) NSFormatter *valueFormatter; +@property (nonatomic, copy , nullable) XLOnChangeBlock onChangeBlock; +@property (nonatomic, assign) BOOL useValueFormatterDuringInput; +@property (nonatomic, strong, nullable) NSFormatter *valueFormatter; // returns the display text for the row descriptor, taking into account NSFormatters and default placeholder values - (nonnull NSString *) displayTextValue; @@ -66,20 +66,22 @@ typedef void(^XLOnChangeBlock)(id __nullable oldValue,id __nullable newValue,XLF // returns the editing text value for the row descriptor, taking into account NSFormatters. - (nonnull NSString *) editTextValue; -@property (nonatomic, readonly, nonnull) NSMutableDictionary * cellConfig; -@property (nonatomic, readonly, nonnull) NSMutableDictionary * cellConfigForSelector; -@property (nonatomic, readonly, nonnull) NSMutableDictionary * cellConfigIfDisabled; -@property (nonatomic, readonly, nonnull) NSMutableDictionary * cellConfigAtConfigure; +@property (nonatomic, readonly, nonnull, strong) NSMutableDictionary * cellConfig; +@property (nonatomic, readonly, nonnull, strong) NSMutableDictionary * cellConfigForSelector; +@property (nonatomic, readonly, nonnull, strong) NSMutableDictionary * cellConfigIfDisabled; +@property (nonatomic, readonly, nonnull, strong) NSMutableDictionary * cellConfigAtConfigure; -@property (nonnull) id disabled; +@property (nonatomic, nonnull, strong) id disabled; -(BOOL)isDisabled; -@property (nonnull) id hidden; + +@property (nonatomic, nonnull, strong) id hidden; -(BOOL)isHidden; -@property (getter=isRequired) BOOL required; -@property (nonnull) XLFormAction * action; +@property (getter=isRequired, nonatomic, assign) BOOL required; + +@property (nonatomic, nonnull, strong) XLFormAction * action; -@property (weak, null_unspecified) XLFormSectionDescriptor * sectionDescriptor; +@property (nonatomic, weak, null_unspecified) XLFormSectionDescriptor * sectionDescriptor; +(nonnull instancetype)formRowDescriptorWithTag:(nullable NSString *)tag rowType:(nonnull NSString *)rowType; +(nonnull instancetype)formRowDescriptorWithTag:(nullable NSString *)tag rowType:(nonnull NSString *)rowType title:(nullable NSString *)title; @@ -87,7 +89,7 @@ typedef void(^XLOnChangeBlock)(id __nullable oldValue,id __nullable newValue,XLF -(nonnull XLFormBaseCell *)cellForFormController:(nonnull XLFormViewController *)formController; -@property (nullable) NSString *requireMsg; +@property (nonatomic, nullable, copy) NSString *requireMsg; -(void)addValidator:(nonnull id)validator; -(void)removeValidator:(nonnull id)validator; -(nullable XLFormValidationStatus *)doValidation; @@ -95,9 +97,9 @@ typedef void(^XLOnChangeBlock)(id __nullable oldValue,id __nullable newValue,XLF // =========================== // property used for Selectors // =========================== -@property (nullable) NSString * noValueDisplayText; -@property (nullable) NSString * selectorTitle; -@property (nullable) NSArray * selectorOptions; +@property (nonatomic, nullable, copy) NSString * noValueDisplayText; +@property (nonatomic, nullable, copy) NSString * selectorTitle; +@property (nonatomic, nullable, strong) NSArray * selectorOptions; @property (null_unspecified) id leftRightSelectorLeftOptionSelected; @@ -126,13 +128,13 @@ typedef NS_ENUM(NSUInteger, XLFormLeftRightSelectorOptionLeftValueChangePolicy) @interface XLFormLeftRightSelectorOption : NSObject @property (nonatomic, assign) XLFormLeftRightSelectorOptionLeftValueChangePolicy leftValueChangePolicy; -@property (readonly, nonnull) id leftValue; -@property (readonly, nonnull) NSArray * rightOptions; -@property (readonly, null_unspecified) NSString * httpParameterKey; -@property (nullable) Class rightSelectorControllerClass; +@property (nonatomic, readonly, nonnull) id leftValue; +@property (nonatomic, readonly, nonnull) NSArray * rightOptions; +@property (nonatomic, readonly, null_unspecified, copy) NSString * httpParameterKey; +@property (nonatomic, nullable) Class rightSelectorControllerClass; -@property (nullable) NSString * noValueDisplayText; -@property (nullable) NSString * selectorTitle; +@property (nonatomic, nullable, copy) NSString * noValueDisplayText; +@property (nonatomic, nullable, copy) NSString * selectorTitle; +(nonnull XLFormLeftRightSelectorOption *)formLeftRightSelectorOptionWithLeftValue:(nonnull id)leftValue @@ -154,16 +156,16 @@ typedef NS_ENUM(NSUInteger, XLFormLeftRightSelectorOptionLeftValueChangePolicy) @interface XLFormAction : NSObject -@property (nullable, nonatomic, strong) Class viewControllerClass; -@property (nullable, nonatomic, strong) NSString * viewControllerStoryboardId; -@property (nullable, nonatomic, strong) NSString * viewControllerNibName; +@property (nullable, nonatomic) Class viewControllerClass; +@property (nullable, nonatomic, copy) NSString * viewControllerStoryboardId; +@property (nullable, nonatomic, copy) NSString * viewControllerNibName; -@property (nonatomic) XLFormPresentationMode viewControllerPresentationMode; +@property (nonatomic, assign) XLFormPresentationMode viewControllerPresentationMode; -@property (nullable, nonatomic, strong) void (^formBlock)(XLFormRowDescriptor * __nonnull sender); +@property (nullable, nonatomic, copy) void (^formBlock)(XLFormRowDescriptor * __nonnull sender); @property (nullable, nonatomic) SEL formSelector; -@property (nullable, nonatomic, strong) NSString * formSegueIdenfifier DEPRECATED_ATTRIBUTE DEPRECATED_MSG_ATTRIBUTE("Use formSegueIdentifier instead"); -@property (nullable, nonatomic, strong) NSString * formSegueIdentifier; -@property (nullable, nonatomic, strong) Class formSegueClass; +@property (nullable, nonatomic, copy) NSString * formSegueIdenfifier DEPRECATED_ATTRIBUTE DEPRECATED_MSG_ATTRIBUTE("Use formSegueIdentifier instead"); +@property (nullable, nonatomic, copy) NSString * formSegueIdentifier; +@property (nullable, nonatomic) Class formSegueClass; @end diff --git a/XLForm/XL/Descriptors/XLFormRowDescriptor.m b/XLForm/XL/Descriptors/XLFormRowDescriptor.m index fdd9a114..945c9fd9 100644 --- a/XLForm/XL/Descriptors/XLFormRowDescriptor.m +++ b/XLForm/XL/Descriptors/XLFormRowDescriptor.m @@ -33,7 +33,7 @@ @interface XLFormDescriptor (_XLFormRowDescriptor) -@property (readonly) NSDictionary* allRowsByTag; +@property (nonatomic, readonly, strong) NSDictionary *allRowsByTag; -(void)addObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType; -(void)removeObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType; @@ -42,22 +42,26 @@ -(void)removeObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)p @interface XLFormSectionDescriptor (_XLFormRowDescriptor) --(void)showFormRow:(XLFormRowDescriptor*)formRow; --(void)hideFormRow:(XLFormRowDescriptor*)formRow; +-(void)showFormRow:(XLFormRowDescriptor *)formRow; +-(void)hideFormRow:(XLFormRowDescriptor *)formRow; @end #import "NSObject+XLFormAdditions.h" +NSString * const XLValueKey = @"value"; +NSString * const XLDisablePredicateCacheKey = @"disablePredicateCache"; +NSString * const XLHidePredicateCacheKey = @"hidePredicateCache"; + @interface XLFormRowDescriptor() -@property XLFormBaseCell * cell; -@property (nonatomic) NSMutableArray *validators; +@property (nonatomic, strong) XLFormBaseCell *cell; +@property (nonatomic, strong) NSMutableArray *validators; -@property BOOL isDirtyDisablePredicateCache; -@property (nonatomic) NSNumber* disablePredicateCache; -@property BOOL isDirtyHidePredicateCache; -@property (nonatomic) NSNumber* hidePredicateCache; +@property (nonatomic, assign) BOOL isDirtyDisablePredicateCache; +@property (nonatomic, copy ) NSNumber *disablePredicateCache; +@property (nonatomic, assign) BOOL isDirtyHidePredicateCache; +@property (nonatomic, copy ) NSNumber *hidePredicateCache; @end @@ -81,8 +85,7 @@ -(instancetype)init -(instancetype)initWithTag:(NSString *)tag rowType:(NSString *)rowType title:(NSString *)title; { - self = [super init]; - if (self){ + if (self = [super init]) { NSAssert(((![rowType isEqualToString:XLFormRowDescriptorTypeSelectorPopover] && ![rowType isEqualToString:XLFormRowDescriptorTypeMultipleSelectorPopover]) || (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) && ([rowType isEqualToString:XLFormRowDescriptorTypeSelectorPopover] || [rowType isEqualToString:XLFormRowDescriptorTypeMultipleSelectorPopover]))), @"You must be running under UIUserInterfaceIdiomPad to use either XLFormRowDescriptorTypeSelectorPopover or XLFormRowDescriptorTypeMultipleSelectorPopover rows."); _tag = tag; _disabled = @NO; @@ -99,11 +102,19 @@ -(instancetype)initWithTag:(NSString *)tag rowType:(NSString *)rowType title:(NS _isDirtyHidePredicateCache = YES; _hidePredicateCache = nil; _height = XLFormRowInitialHeight; - [self addObserver:self forKeyPath:@"value" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; - [self addObserver:self forKeyPath:@"disablePredicateCache" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; - [self addObserver:self forKeyPath:@"hidePredicateCache" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; + + [self addObserver:self + forKeyPath:XLValueKey + options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; + [self addObserver:self + forKeyPath:XLDisablePredicateCacheKey + options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; + [self addObserver:self + forKeyPath:XLHidePredicateCacheKey + options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:0]; } + return self; } @@ -161,72 +172,84 @@ -(XLFormBaseCell *)cellForFormController:(XLFormViewController * __unused)formCo - (void)configureCellAtCreationTime { [self.cellConfigAtConfigure enumerateKeysAndObjectsUsingBlock:^(NSString *keyPath, id value, __unused BOOL *stop) { - [_cell setValue:(value == [NSNull null]) ? nil : value forKeyPath:keyPath]; + [self.cell setValue:(value == [NSNull null]) ? nil : value forKeyPath:keyPath]; }]; } -(NSMutableDictionary *)cellConfig { - if (_cellConfig) return _cellConfig; - _cellConfig = [NSMutableDictionary dictionary]; + if (!_cellConfig) { + _cellConfig = [NSMutableDictionary dictionary]; + } + return _cellConfig; } -(NSMutableDictionary *)cellConfigForSelector { - if (_cellConfigForSelector) return _cellConfigForSelector; - _cellConfigForSelector = [NSMutableDictionary dictionary]; + if (!_cellConfigForSelector) { + _cellConfigForSelector = [NSMutableDictionary dictionary]; + } + return _cellConfigForSelector; } -(NSMutableDictionary *)cellConfigIfDisabled { - if (_cellConfigIfDisabled) return _cellConfigIfDisabled; - _cellConfigIfDisabled = [NSMutableDictionary dictionary]; + if (!_cellConfigIfDisabled) { + _cellConfigIfDisabled = [NSMutableDictionary dictionary]; + } + return _cellConfigIfDisabled; } -(NSMutableDictionary *)cellConfigAtConfigure { - if (_cellConfigAtConfigure) return _cellConfigAtConfigure; - _cellConfigAtConfigure = [NSMutableDictionary dictionary]; + if (!_cellConfigAtConfigure) { + _cellConfigAtConfigure = [NSMutableDictionary dictionary]; + } + return _cellConfigAtConfigure; } --(NSString*)editTextValue +-(NSString *)editTextValue { + NSString *result = @""; + if (self.value) { if (self.valueFormatter) { if (self.useValueFormatterDuringInput) { - return [self displayTextValue]; - }else{ + result = [self displayTextValue]; + } + else { // have formatter, but we don't want to use it during editing - return [self.value displayText]; + result = [self.value displayText]; } - }else{ + } + else { // have value, but no formatter, use the value's displayText - return [self.value displayText]; + result = [self.value displayText]; } - }else{ - // placeholder - return @""; } + + return result; } --(NSString*)displayTextValue +-(NSString *)displayTextValue { + NSString *result = self.noValueDisplayText; + if (self.value) { if (self.valueFormatter) { - return [self.valueFormatter stringForObjectValue:self.value]; + result = [self.valueFormatter stringForObjectValue:self.value]; } - else{ - return [self.value displayText]; + else { + result = [self.value displayText]; } } - else { - return self.noValueDisplayText; - } + + return result; } -(NSString *)description @@ -236,9 +259,10 @@ -(NSString *)description -(XLFormAction *)action { - if (!_action){ + if (!_action) { _action = [[XLFormAction alloc] init]; } + return _action; } @@ -249,13 +273,15 @@ -(void)setAction:(XLFormAction *)action -(CGFloat)height { - if (_height == XLFormRowInitialHeight){ + if (_height == XLFormRowInitialHeight) { if ([[self.cell class] respondsToSelector:@selector(formDescriptorCellHeightForRowDescriptor:)]){ return [[self.cell class] formDescriptorCellHeightForRowDescriptor:self]; - } else { + } + else { _height = XLFormUnspecifiedCellHeight; } } + return _height; } @@ -266,71 +292,76 @@ -(void)setHeight:(CGFloat)height { // In the implementation -(id)copyWithZone:(NSZone *)zone { - XLFormRowDescriptor * rowDescriptorCopy = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:[self.rowType copy] title:[self.title copy]]; + XLFormRowDescriptor *rowDescriptorCopy = [XLFormRowDescriptor formRowDescriptorWithTag:nil + rowType:[self.rowType copy] + title:[self.title copy]]; rowDescriptorCopy.cellClass = [self.cellClass copy]; [rowDescriptorCopy.cellConfig addEntriesFromDictionary:self.cellConfig]; [rowDescriptorCopy.cellConfigAtConfigure addEntriesFromDictionary:self.cellConfigAtConfigure]; rowDescriptorCopy.valueTransformer = [self.valueTransformer copy]; - rowDescriptorCopy->_hidden = _hidden; - rowDescriptorCopy->_disabled = _disabled; + rowDescriptorCopy.hidden = self.hidden; + rowDescriptorCopy.disabled = self.disabled; rowDescriptorCopy.required = self.isRequired; rowDescriptorCopy.isDirtyDisablePredicateCache = YES; rowDescriptorCopy.isDirtyHidePredicateCache = YES; rowDescriptorCopy.validators = [self.validators mutableCopy]; - + // ===================== // properties for Button // ===================== rowDescriptorCopy.action = [self.action copy]; - - + + // =========================== // property used for Selectors // =========================== - + rowDescriptorCopy.noValueDisplayText = [self.noValueDisplayText copy]; rowDescriptorCopy.selectorTitle = [self.selectorTitle copy]; rowDescriptorCopy.selectorOptions = [self.selectorOptions copy]; rowDescriptorCopy.leftRightSelectorLeftOptionSelected = [self.leftRightSelectorLeftOptionSelected copy]; - + return rowDescriptorCopy; } -(void)dealloc { + [self removeObserver:self forKeyPath:XLValueKey]; + [self removeObserver:self forKeyPath:XLDisablePredicateCacheKey]; + [self removeObserver:self forKeyPath:XLHidePredicateCacheKey]; + [self.sectionDescriptor.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeDisabled]; [self.sectionDescriptor.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden]; - @try { - [self removeObserver:self forKeyPath:@"value"]; - } - @catch (NSException * __unused exception) {} - @try { - [self removeObserver:self forKeyPath:@"disablePredicateCache"]; - } - @catch (NSException * __unused exception) {} - @try { - [self removeObserver:self forKeyPath:@"hidePredicateCache"]; - } - @catch (NSException * __unused exception) {} + + _cell = nil; + + [self.validators removeAllObjects]; + self.validators = nil; } #pragma mark - KVO -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (!self.sectionDescriptor) return; - if (object == self && ([keyPath isEqualToString:@"value"] || [keyPath isEqualToString:@"hidePredicateCache"] || [keyPath isEqualToString:@"disablePredicateCache"])){ + if (!self.sectionDescriptor) { + return; + } + else if (object == self && ([keyPath isEqualToString:XLValueKey] || + [keyPath isEqualToString:XLHidePredicateCacheKey] || [keyPath isEqualToString:XLDisablePredicateCacheKey])) { if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeSetting)]){ id newValue = [change objectForKey:NSKeyValueChangeNewKey]; id oldValue = [change objectForKey:NSKeyValueChangeOldKey]; - if ([keyPath isEqualToString:@"value"]){ + if ([keyPath isEqualToString:XLValueKey]) { [self.sectionDescriptor.formDescriptor.delegate formRowDescriptorValueHasChanged:object oldValue:oldValue newValue:newValue]; if (self.onChangeBlock) { self.onChangeBlock(oldValue, newValue, self); } } - else{ - [self.sectionDescriptor.formDescriptor.delegate formRowDescriptorPredicateHasChanged:object oldValue:oldValue newValue:newValue predicateType:([keyPath isEqualToString:@"hidePredicateCache"] ? XLPredicateTypeHidden : XLPredicateTypeDisabled)]; + else { + [self.sectionDescriptor.formDescriptor.delegate formRowDescriptorPredicateHasChanged:object + oldValue:oldValue + newValue:newValue + predicateType:([keyPath isEqualToString:XLHidePredicateCacheKey] ? XLPredicateTypeHidden : XLPredicateTypeDisabled)]; } } } @@ -340,12 +371,14 @@ -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS -(BOOL)isDisabled { - if (self.sectionDescriptor.formDescriptor.isDisabled){ + if (self.sectionDescriptor.formDescriptor.isDisabled) { return YES; } + if (self.isDirtyDisablePredicateCache) { [self evaluateIsDisabled]; } + return [self.disablePredicateCache boolValue]; } @@ -354,11 +387,12 @@ -(void)setDisabled:(id)disabled if ([_disabled isKindOfClass:[NSPredicate class]]){ [self.sectionDescriptor.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeDisabled]; } + _disabled = [disabled isKindOfClass:[NSString class]] ? [disabled formPredicate] : disabled; if ([_disabled isKindOfClass:[NSPredicate class]]){ [self.sectionDescriptor.formDescriptor addObserversOfObject:self predicateType:XLPredicateTypeDisabled]; } - + [self evaluateIsDisabled]; } @@ -367,7 +401,8 @@ -(BOOL)evaluateIsDisabled if ([_disabled isKindOfClass:[NSPredicate class]]) { if (!self.sectionDescriptor.formDescriptor) { self.isDirtyDisablePredicateCache = YES; - } else { + } + else { @try { self.disablePredicateCache = @([_disabled evaluateWithObject:self substitutionVariables:self.sectionDescriptor.formDescriptor.allRowsByTag ?: @{}]); } @@ -377,12 +412,14 @@ -(BOOL)evaluateIsDisabled }; } } - else{ + else { self.disablePredicateCache = _disabled; } - if ([self.disablePredicateCache boolValue]){ + + if ([self.disablePredicateCache boolValue]) { [self.cell resignFirstResponder]; } + return [self.disablePredicateCache boolValue]; } @@ -393,9 +430,9 @@ -(id)disabled -(void)setDisablePredicateCache:(NSNumber*)disablePredicateCache { - NSParameterAssert(disablePredicateCache); + NSParameterAssert(disablePredicateCache != nil); self.isDirtyDisablePredicateCache = NO; - if (!_disablePredicateCache || ![_disablePredicateCache isEqualToNumber:disablePredicateCache]){ + if (_disablePredicateCache == nil || ![_disablePredicateCache isEqualToNumber:disablePredicateCache]){ _disablePredicateCache = disablePredicateCache; } } @@ -414,9 +451,9 @@ -(NSNumber *)hidePredicateCache -(void)setHidePredicateCache:(NSNumber *)hidePredicateCache { - NSParameterAssert(hidePredicateCache); + NSParameterAssert(hidePredicateCache != nil); self.isDirtyHidePredicateCache = NO; - if (!_hidePredicateCache || ![_hidePredicateCache isEqualToNumber:hidePredicateCache]){ + if (_hidePredicateCache == nil || ![_hidePredicateCache isEqualToNumber:hidePredicateCache]){ _hidePredicateCache = hidePredicateCache; } } @@ -426,6 +463,7 @@ -(BOOL)isHidden if (self.isDirtyHidePredicateCache) { return [self evaluateIsHidden]; } + return [self.hidePredicateCache boolValue]; } @@ -434,7 +472,8 @@ -(BOOL)evaluateIsHidden if ([_hidden isKindOfClass:[NSPredicate class]]) { if (!self.sectionDescriptor.formDescriptor) { self.isDirtyHidePredicateCache = YES; - } else { + } + else { @try { self.hidePredicateCache = @([_hidden evaluateWithObject:self substitutionVariables:self.sectionDescriptor.formDescriptor.allRowsByTag ?: @{}]); } @@ -444,16 +483,18 @@ -(BOOL)evaluateIsHidden }; } } - else{ + else { self.hidePredicateCache = _hidden; } + if ([self.hidePredicateCache boolValue]){ [self.cell resignFirstResponder]; [self.sectionDescriptor hideFormRow:self]; } - else{ + else { [self.sectionDescriptor showFormRow:self]; } + return [self.hidePredicateCache boolValue]; } @@ -463,10 +504,12 @@ -(void)setHidden:(id)hidden if ([_hidden isKindOfClass:[NSPredicate class]]){ [self.sectionDescriptor.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden]; } + _hidden = [hidden isKindOfClass:[NSString class]] ? [hidden formPredicate] : hidden; if ([_hidden isKindOfClass:[NSPredicate class]]){ [self.sectionDescriptor.formDescriptor addObserversOfObject:self predicateType:XLPredicateTypeHidden]; } + [self evaluateIsHidden]; // check and update if this row should be hidden. } @@ -480,34 +523,35 @@ -(id)hidden -(void)addValidator:(id)validator { - if (validator == nil || ![validator conformsToProtocol:@protocol(XLFormValidatorProtocol)]) + if (validator == nil || ![validator conformsToProtocol:@protocol(XLFormValidatorProtocol)]) { return; - - if(![self.validators containsObject:validator]) { + } + else if (![self.validators containsObject:validator]) { [self.validators addObject:validator]; } } -(void)removeValidator:(id)validator { - if (validator == nil|| ![validator conformsToProtocol:@protocol(XLFormValidatorProtocol)]) + if (validator == nil || ![validator conformsToProtocol:@protocol(XLFormValidatorProtocol)]) { return; - - if ([self.validators containsObject:validator]) { + } + else if ([self.validators containsObject:validator]) { [self.validators removeObject:validator]; } } - (BOOL)valueIsEmpty { - return self.value == nil || [self.value isKindOfClass:[NSNull class]] || ([self.value respondsToSelector:@selector(length)] && [self.value length]==0) || - ([self.value respondsToSelector:@selector(count)] && [self.value count]==0); + return self.value == nil || [self.value isKindOfClass:[NSNull class]] || + ([self.value respondsToSelector:@selector(length)] && [self.value length] == 0) || + ([self.value respondsToSelector:@selector(count)] && [self.value count] == 0); } -(XLFormValidationStatus *)doValidation { XLFormValidationStatus *valStatus = nil; - + if (self.required) { // do required validation here if ([self valueIsEmpty]) { @@ -515,22 +559,24 @@ -(XLFormValidationStatus *)doValidation NSString *msg = nil; if (self.requireMsg != nil) { msg = self.requireMsg; - } else { + } + else { // default message for required msg msg = NSLocalizedString(@"%@ can't be empty", nil); } - - if (self.title != nil) { + + if (self.title.length) { valStatus.msg = [NSString stringWithFormat:msg, self.title]; - } else { + } + else { valStatus.msg = [NSString stringWithFormat:msg, self.tag]; } - + return valStatus; } } // custom validator - for(id v in self.validators) { + for (id v in self.validators) { if ([v conformsToProtocol:@protocol(XLFormValidatorProtocol)]) { XLFormValidationStatus *vStatus = [v isValid:self]; // fail validation @@ -538,10 +584,12 @@ -(XLFormValidationStatus *)doValidation return vStatus; } valStatus = vStatus; - } else { + } + else { valStatus = nil; } } + return valStatus; } @@ -597,13 +645,13 @@ +(XLFormLeftRightSelectorOption *)formLeftRightSelectorOptionWithLeftValue:(id)l -(instancetype)initWithLeftValue:(NSString *)leftValue httpParameterKey:(NSString *)httpParameterKey rightOptions:(NSArray *)rightOptions { - self = [super init]; - if (self){ + if (self = [super init]) { _selectorTitle = nil; _leftValue = leftValue; _rightOptions = rightOptions; _httpParameterKey = httpParameterKey; } + return self; } @@ -614,10 +662,10 @@ @implementation XLFormAction - (instancetype)init { - self = [super init]; - if (self) { + if (self = [super init]) { _viewControllerPresentationMode = XLFormPresentationModeDefault; } + return self; } @@ -629,24 +677,25 @@ -(id)copyWithZone:(NSZone *)zone if (self.viewControllerClass){ actionCopy.viewControllerClass = [self.viewControllerClass copy]; } - else if ([self.viewControllerStoryboardId length] != 0){ + else if ([self.viewControllerStoryboardId length] != 0) { actionCopy.viewControllerStoryboardId = [self.viewControllerStoryboardId copy]; } - else if ([self.viewControllerNibName length] != 0){ + else if ([self.viewControllerNibName length] != 0) { actionCopy.viewControllerNibName = [self.viewControllerNibName copy]; } - if (self.formBlock){ + if (self.formBlock) { actionCopy.formBlock = [self.formBlock copy]; } - else if (self.formSelector){ + else if (self.formSelector) { actionCopy.formSelector = self.formSelector; } - else if (self.formSegueIdentifier){ + else if (self.formSegueIdentifier) { actionCopy.formSegueIdentifier = [self.formSegueIdentifier copy]; } else if (self.formSegueClass){ actionCopy.formSegueClass = [self.formSegueClass copy]; } + return actionCopy; } @@ -671,7 +720,6 @@ -(void)setViewControllerStoryboardId:(NSString *)viewControllerStoryboardId _viewControllerStoryboardId = viewControllerStoryboardId; } - -(void)setFormSelector:(SEL)formSelector { _formBlock = nil; @@ -680,7 +728,6 @@ -(void)setFormSelector:(SEL)formSelector _formSelector = formSelector; } - -(void)setFormBlock:(void (^)(XLFormRowDescriptor *))formBlock { _formSegueClass = nil; diff --git a/XLForm/XL/Descriptors/XLFormSectionDescriptor.h b/XLForm/XL/Descriptors/XLFormSectionDescriptor.h index f0553fdb..f65c0844 100644 --- a/XLForm/XL/Descriptors/XLFormSectionDescriptor.h +++ b/XLForm/XL/Descriptors/XLFormSectionDescriptor.h @@ -42,19 +42,19 @@ typedef NS_ENUM(NSUInteger, XLFormSectionInsertMode) { @interface XLFormSectionDescriptor : NSObject -@property (nonatomic, nullable) NSString * title; -@property (nonatomic, nullable) NSString * footerTitle; -@property (readonly, nonnull) NSMutableArray * formRows; +@property (nonatomic, nullable, copy) NSString * title; +@property (nonatomic, nullable, copy) NSString * footerTitle; +@property (nonatomic, readonly, nonnull, strong) NSMutableArray * formRows; -@property (readonly) XLFormSectionInsertMode sectionInsertMode; -@property (readonly) XLFormSectionOptions sectionOptions; -@property (nullable) XLFormRowDescriptor * multivaluedRowTemplate; -@property (readonly, nullable) XLFormRowDescriptor * multivaluedAddButton; -@property (nonatomic, nullable) NSString * multivaluedTag; +@property (nonatomic, readonly, assign) XLFormSectionInsertMode sectionInsertMode; +@property (nonatomic, readonly, assign) XLFormSectionOptions sectionOptions; +@property (nonatomic, nullable, strong) XLFormRowDescriptor * multivaluedRowTemplate; +@property (nonatomic, readonly, nullable, strong) XLFormRowDescriptor * multivaluedAddButton; +@property (nonatomic, nullable, copy) NSString * multivaluedTag; -@property (weak, null_unspecified) XLFormDescriptor * formDescriptor; +@property (nonatomic, weak, null_unspecified) XLFormDescriptor * formDescriptor; -@property (nonnull) id hidden; +@property (nonatomic, nonnull, strong) id hidden; -(BOOL)isHidden; +(nonnull instancetype)formSection; diff --git a/XLForm/XL/Descriptors/XLFormSectionDescriptor.m b/XLForm/XL/Descriptors/XLFormSectionDescriptor.m index e51e7e52..ee8d5cc0 100644 --- a/XLForm/XL/Descriptors/XLFormSectionDescriptor.m +++ b/XLForm/XL/Descriptors/XLFormSectionDescriptor.m @@ -29,15 +29,16 @@ #import "NSString+XLFormAdditions.h" #import "UIView+XLFormAdditions.h" +NSString * const XLFormRowsKey = @"formRows"; @interface XLFormDescriptor (_XLFormSectionDescriptor) -@property (readonly) NSDictionary* allRowsByTag; +@property (nonatomic, weak, readonly) NSDictionary *allRowsByTag; --(void)addRowToTagCollection:(XLFormRowDescriptor*)rowDescriptor; --(void)removeRowFromTagCollection:(XLFormRowDescriptor*) rowDescriptor; --(void)showFormSection:(XLFormSectionDescriptor*)formSection; --(void)hideFormSection:(XLFormSectionDescriptor*)formSection; +-(void)addRowToTagCollection:(XLFormRowDescriptor *)rowDescriptor; +-(void)removeRowFromTagCollection:(XLFormRowDescriptor *) rowDescriptor; +-(void)showFormSection:(XLFormSectionDescriptor *)formSection; +-(void)hideFormSection:(XLFormSectionDescriptor *)formSection; -(void)addObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType; -(void)removeObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)predicateType; @@ -46,10 +47,11 @@ -(void)removeObserversOfObject:(id)sectionOrRow predicateType:(XLPredicateType)p @interface XLFormSectionDescriptor() -@property NSMutableArray * formRows; -@property NSMutableArray * allRows; -@property BOOL isDirtyHidePredicateCache; -@property (nonatomic) NSNumber* hidePredicateCache; +@property (nonatomic, strong) NSMutableArray *formRows; +@property (nonatomic, strong) NSMutableArray *allRows; + +@property (nonatomic, assign) BOOL isDirtyHidePredicateCache; +@property (nonatomic, copy ) NSNumber *hidePredicateCache; @end @@ -60,8 +62,7 @@ @implementation XLFormSectionDescriptor -(instancetype)init { - self = [super init]; - if (self){ + if (self = [super init]) { _formRows = [NSMutableArray array]; _allRows = [NSMutableArray array]; _sectionInsertMode = XLFormSectionInsertModeLastRow; @@ -71,25 +72,35 @@ -(instancetype)init _hidden = @NO; _hidePredicateCache = @NO; _isDirtyHidePredicateCache = YES; - [self addObserver:self forKeyPath:@"formRows" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:0]; + + [self addObserver:self + forKeyPath:XLFormRowsKey + options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:0]; } + return self; } --(instancetype)initWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode{ - self = [self init]; - if (self){ +-(instancetype)initWithTitle:(NSString *)title sectionOptions:(XLFormSectionOptions)sectionOptions sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode +{ + if (self = [self init]) { _sectionInsertMode = sectionInsertMode; _sectionOptions = sectionOptions; _title = title; - if ([self canInsertUsingButton]){ - _multivaluedAddButton = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeButton title:@"Add Item"]; + + if ([self canInsertUsingButton]) { + _multivaluedAddButton = [XLFormRowDescriptor formRowDescriptorWithTag:nil + rowType:XLFormRowDescriptorTypeButton + title:@"Add Item"]; + [_multivaluedAddButton.cellConfig setObject:@(NSTextAlignmentNatural) forKey:@"textLabel.textAlignment"]; _multivaluedAddButton.action.formSelector = NSSelectorFromString(@"multivaluedInsertButtonTapped:"); + [self insertObject:_multivaluedAddButton inFormRowsAtIndex:0]; [self insertObject:_multivaluedAddButton inAllRowsAtIndex:0]; } } + return self; } @@ -125,14 +136,12 @@ -(BOOL)isMultivaluedSection -(void)addFormRow:(XLFormRowDescriptor *)formRow { - NSUInteger index; + NSUInteger index = [self.allRows count]; if ([self canInsertUsingButton]) { index = ([self.formRows count] > 0) ? [self.formRows count] - 1 : 0; - } else { - index = [self.allRows count]; } - + [self insertObject:formRow inAllRowsAtIndex:index]; } @@ -144,26 +153,23 @@ -(void)addFormRow:(XLFormRowDescriptor *)formRow afterRow:(XLFormRowDescriptor * } else { //case when afterRow does not exist. Just insert at the end. [self addFormRow:formRow]; - return; } } -(void)addFormRow:(XLFormRowDescriptor *)formRow beforeRow:(XLFormRowDescriptor *)beforeRow { - NSUInteger allRowIndex = [self.allRows indexOfObject:beforeRow]; if (allRowIndex != NSNotFound) { [self insertObject:formRow inAllRowsAtIndex:allRowIndex]; } else { //case when afterRow does not exist. Just insert at the end. [self addFormRow:formRow]; - return; } } -(void)removeFormRowAtIndex:(NSUInteger)index { - if (self.formRows.count > index){ + if (self.formRows.count > index) { XLFormRowDescriptor *formRow = [self.formRows objectAtIndex:index]; NSUInteger allRowIndex = [self.allRows indexOfObject:formRow]; [self removeObjectFromFormRowsAtIndex:index]; @@ -174,21 +180,21 @@ -(void)removeFormRowAtIndex:(NSUInteger)index -(void)removeFormRow:(XLFormRowDescriptor *)formRow { NSUInteger index = NSNotFound; - if ((index = [self.formRows indexOfObject:formRow]) != NSNotFound){ + if ((index = [self.formRows indexOfObject:formRow]) != NSNotFound) { [self removeFormRowAtIndex:index]; } - else if ((index = [self.allRows indexOfObject:formRow]) != NSNotFound){ - if (self.allRows.count > index){ + else if ((index = [self.allRows indexOfObject:formRow]) != NSNotFound) { + if (self.allRows.count > index) { [self removeObjectFromAllRowsAtIndex:index]; } - }; + } } - (void)moveRowAtIndexPath:(NSIndexPath *)sourceIndex toIndexPath:(NSIndexPath *)destinationIndex { - if ((sourceIndex.row < self.formRows.count) && (destinationIndex.row < self.formRows.count) && (sourceIndex.row != destinationIndex.row)){ - XLFormRowDescriptor * row = [self objectInFormRowsAtIndex:sourceIndex.row]; - XLFormRowDescriptor * destRow = [self objectInFormRowsAtIndex:destinationIndex.row]; + if ((sourceIndex.row < self.formRows.count) && (destinationIndex.row < self.formRows.count) && (sourceIndex.row != destinationIndex.row)) { + XLFormRowDescriptor *row = [self objectInFormRowsAtIndex:sourceIndex.row]; + XLFormRowDescriptor *destRow = [self objectInFormRowsAtIndex:destinationIndex.row]; [self.formRows removeObjectAtIndex:sourceIndex.row]; [self.formRows insertObject:row atIndex:destinationIndex.row]; @@ -199,11 +205,15 @@ - (void)moveRowAtIndexPath:(NSIndexPath *)sourceIndex toIndexPath:(NSIndexPath * -(void)dealloc { + [self removeObserver:self forKeyPath:XLFormRowsKey]; + [self.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden]; - @try { - [self removeObserver:self forKeyPath:@"formRows"]; - } - @catch (NSException * __unused exception) {} + + [self.formRows removeAllObjects]; + self.formRows = nil; + + [self.allRows removeAllObjects]; + self.allRows = nil; } #pragma mark - Show/hide rows @@ -214,13 +224,15 @@ -(void)showFormRow:(XLFormRowDescriptor*)formRow{ if (formIndex != NSNotFound) { return; } + NSUInteger index = [self.allRows indexOfObject:formRow]; - if (index != NSNotFound){ + if (index != NSNotFound) { while (formIndex == NSNotFound && index > 0) { XLFormRowDescriptor* previous = [self.allRows objectAtIndex:--index]; formIndex = [self.formRows indexOfObject:previous]; } - if (formIndex == NSNotFound){ // index == 0 => insert at the beginning + + if (formIndex == NSNotFound) { // index == 0 => insert at the beginning [self insertObject:formRow inFormRowsAtIndex:0]; } else { @@ -232,36 +244,41 @@ -(void)showFormRow:(XLFormRowDescriptor*)formRow{ -(void)hideFormRow:(XLFormRowDescriptor*)formRow{ NSUInteger index = [self.formRows indexOfObject:formRow]; - if (index != NSNotFound){ + if (index != NSNotFound) { [self removeObjectFromFormRowsAtIndex:index]; } } #pragma mark - KVO --(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +- (void)observeValueForKeyPath:(nullable NSString *)keyPath + ofObject:(nullable id)object + change:(nullable NSDictionary *)change + context:(nullable void *)context { - if (!self.formDescriptor.delegate) return; - if ([keyPath isEqualToString:@"formRows"]){ - if ([self.formDescriptor.formSections containsObject:self]){ - if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeInsertion)]){ - NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; - XLFormRowDescriptor * formRow = [((XLFormSectionDescriptor *)object).formRows objectAtIndex:indexSet.firstIndex]; + if (!self.formDescriptor.delegate) { + return; + } + else if ([keyPath isEqualToString:XLFormRowsKey]) { + if ([self.formDescriptor.formSections containsObject:self]) { + if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeInsertion)]) { + NSIndexSet *indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; + XLFormRowDescriptor *formRow = [((XLFormSectionDescriptor *)object).formRows objectAtIndex:indexSet.firstIndex]; NSUInteger sectionIndex = [self.formDescriptor.formSections indexOfObject:object]; - [self.formDescriptor.delegate formRowHasBeenAdded:formRow atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]]; + [self.formDescriptor.delegate formRowHasBeenAdded:formRow + atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]]; } - else if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeRemoval)]){ - NSIndexSet * indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; - XLFormRowDescriptor * removedRow = [[change objectForKey:NSKeyValueChangeOldKey] objectAtIndex:0]; + else if ([[change objectForKey:NSKeyValueChangeKindKey] isEqualToNumber:@(NSKeyValueChangeRemoval)]) { + NSIndexSet *indexSet = [change objectForKey:NSKeyValueChangeIndexesKey]; + XLFormRowDescriptor *removedRow = [[change objectForKey:NSKeyValueChangeOldKey] objectAtIndex:0]; NSUInteger sectionIndex = [self.formDescriptor.formSections indexOfObject:object]; - [self.formDescriptor.delegate formRowHasBeenRemoved:removedRow atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]]; + [self.formDescriptor.delegate formRowHasBeenRemoved:removedRow + atIndexPath:[NSIndexPath indexPathForRow:indexSet.firstIndex inSection:sectionIndex]]; } } } } - - #pragma mark - KVC -(NSUInteger)countOfFormRows @@ -342,9 +359,9 @@ -(NSNumber *)hidePredicateCache -(void)setHidePredicateCache:(NSNumber *)hidePredicateCache { - NSParameterAssert(hidePredicateCache); + NSParameterAssert(hidePredicateCache != nil); self.isDirtyHidePredicateCache = NO; - if (!_hidePredicateCache || ![_hidePredicateCache isEqualToNumber:hidePredicateCache]){ + if (_hidePredicateCache == nil || ![_hidePredicateCache isEqualToNumber:hidePredicateCache]) { _hidePredicateCache = hidePredicateCache; } } @@ -354,6 +371,7 @@ -(BOOL)isHidden if (self.isDirtyHidePredicateCache) { return [self evaluateIsHidden]; } + return [self.hidePredicateCache boolValue]; } @@ -362,7 +380,8 @@ -(BOOL)evaluateIsHidden if ([_hidden isKindOfClass:[NSPredicate class]]) { if (!self.formDescriptor) { self.isDirtyHidePredicateCache = YES; - } else { + } + else { @try { self.hidePredicateCache = @([_hidden evaluateWithObject:self substitutionVariables:self.formDescriptor.allRowsByTag ?: @{}]); } @@ -372,21 +391,24 @@ -(BOOL)evaluateIsHidden }; } } - else{ + else { self.hidePredicateCache = _hidden; } - if ([self.hidePredicateCache boolValue]){ - if ([self.formDescriptor.delegate isKindOfClass:[XLFormViewController class]]){ - XLFormBaseCell* firtResponder = (XLFormBaseCell*) [((XLFormViewController*)self.formDescriptor.delegate).tableView findFirstResponder]; - if ([firtResponder isKindOfClass:[XLFormBaseCell class]] && firtResponder.rowDescriptor.sectionDescriptor == self){ + + if ([self.hidePredicateCache boolValue]) { + if ([self.formDescriptor.delegate isKindOfClass:[XLFormViewController class]]) { + XLFormBaseCell *firtResponder = (XLFormBaseCell *)[((XLFormViewController *)self.formDescriptor.delegate).tableView findFirstResponder]; + if ([firtResponder isKindOfClass:[XLFormBaseCell class]] && firtResponder.rowDescriptor.sectionDescriptor == self) { [firtResponder resignFirstResponder]; } } + [self.formDescriptor hideFormSection:self]; } - else{ + else { [self.formDescriptor showFormSection:self]; } + return [self.hidePredicateCache boolValue]; } @@ -398,13 +420,15 @@ -(id)hidden -(void)setHidden:(id)hidden { - if ([_hidden isKindOfClass:[NSPredicate class]]){ + if ([_hidden isKindOfClass:[NSPredicate class]]) { [self.formDescriptor removeObserversOfObject:self predicateType:XLPredicateTypeHidden]; } + _hidden = [hidden isKindOfClass:[NSString class]] ? [hidden formPredicate] : hidden; - if ([_hidden isKindOfClass:[NSPredicate class]]){ + if ([_hidden isKindOfClass:[NSPredicate class]]) { [self.formDescriptor addObserversOfObject:self predicateType:XLPredicateTypeHidden]; } + [self evaluateIsHidden]; // check and update if this row should be hidden. } diff --git a/XLForm/XL/Helpers/UIView+XLFormAdditions.h b/XLForm/XL/Helpers/UIView+XLFormAdditions.h index 406f7dd2..34c1cef6 100644 --- a/XLForm/XL/Helpers/UIView+XLFormAdditions.h +++ b/XLForm/XL/Helpers/UIView+XLFormAdditions.h @@ -28,7 +28,7 @@ @interface UIView (XLFormAdditions) -+(id)autolayoutView; ++(instancetype)autolayoutView; -(NSLayoutConstraint *)layoutConstraintSameHeightOf:(UIView *)view; -(UIView *)findFirstResponder; -(UITableViewCell *)formDescriptorCell; diff --git a/XLForm/XL/Helpers/UIView+XLFormAdditions.m b/XLForm/XL/Helpers/UIView+XLFormAdditions.m index 3113265d..c2bc45a6 100644 --- a/XLForm/XL/Helpers/UIView+XLFormAdditions.m +++ b/XLForm/XL/Helpers/UIView+XLFormAdditions.m @@ -27,47 +27,62 @@ @implementation UIView (XLFormAdditions) -+ (id)autolayoutView ++ (instancetype)autolayoutView { - UIView *view = [self new]; + __kindof UIView *view = [self new]; view.translatesAutoresizingMaskIntoConstraints = NO; + return view; } --(NSLayoutConstraint *)layoutConstraintSameHeightOf:(UIView *)view +- (NSLayoutConstraint *)layoutConstraintSameHeightOf:(UIView *)view { - return [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0.0f]; + return [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:view + attribute:NSLayoutAttributeHeight + multiplier:1.0 + constant:0.0]; } - (UIView *)findFirstResponder { + UIView *firstResponder = nil; if (self.isFirstResponder) { - return self; + firstResponder = self; } - for (UIView *subView in self.subviews) { - UIView *firstResponder = [subView findFirstResponder]; - if (firstResponder != nil) { - return firstResponder; + else { + for (UIView *subView in self.subviews) { + UIView *fr = [subView findFirstResponder]; + if (fr != nil) { + firstResponder = fr; + + break; + } } } - return nil; + + return firstResponder; } - (UITableViewCell *)formDescriptorCell { + UITableViewCell * tableViewCell = nil; + if ([self isKindOfClass:[UITableViewCell class]]) { if ([self conformsToProtocol:@protocol(XLFormDescriptorCell)]){ - return (UITableViewCell *)self; + tableViewCell = (UITableViewCell *)self; } - return nil; } - if (self.superview) { - UITableViewCell * tableViewCell = [self.superview formDescriptorCell]; - if (tableViewCell != nil) { - return tableViewCell; + else if (self.superview) { + UITableViewCell * cell = [self.superview formDescriptorCell]; + if (cell != nil) { + tableViewCell = cell; } } - return nil; + + return tableViewCell; } @end diff --git a/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.h b/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.h index 8e5c708d..54a076e0 100755 --- a/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.h +++ b/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.h @@ -27,8 +27,8 @@ @interface XLFormRowNavigationAccessoryView : UIToolbar -@property (nonatomic) UIBarButtonItem *previousButton; -@property (nonatomic) UIBarButtonItem *nextButton; -@property (nonatomic) UIBarButtonItem *doneButton; +@property (nonatomic, weak) UIBarButtonItem *previousButton; +@property (nonatomic, weak) UIBarButtonItem *nextButton; +@property (nonatomic, weak) UIBarButtonItem *doneButton; @end diff --git a/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.m b/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.m index f769a5ba..46ceeb76 100755 --- a/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.m +++ b/XLForm/XL/Helpers/Views/XLFormRowNavigationAccessoryView.m @@ -25,14 +25,6 @@ #import "XLFormRowNavigationAccessoryView.h" - -@interface XLFormRowNavigationAccessoryView () - -@property (nonatomic) UIBarButtonItem *fixedSpace; -@property (nonatomic) UIBarButtonItem *flexibleSpace; - -@end - @implementation XLFormRowNavigationAccessoryView @synthesize previousButton = _previousButton; @@ -44,55 +36,30 @@ - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 44.0)]; if (self) { + + UIBarButtonItem *previousButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:105 target:nil action:nil]; + _previousButton = previousButton; + UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:106 target:nil action:nil]; + _nextButton = nextButton; + UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:nil action:nil]; + _doneButton = doneButton; + + UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; + fixedSpace.width = 22.0; + + UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + self.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth); - NSArray * items = [NSArray arrayWithObjects:self.previousButton, - self.fixedSpace, - self.nextButton, - self.flexibleSpace, - self.doneButton, nil]; + NSArray * items = [NSArray arrayWithObjects:previousButton, + fixedSpace, + nextButton, + flexibleSpace, + doneButton, nil]; [self setItems:items]; } return self; } -#pragma mark - Properties - --(UIBarButtonItem *)previousButton -{ - if (_previousButton) return _previousButton; - _previousButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:105 target:nil action:nil]; - return _previousButton; -} - --(UIBarButtonItem *)fixedSpace -{ - if (_fixedSpace) return _fixedSpace; - _fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; - _fixedSpace.width = 22.0; - return _fixedSpace; -} - --(UIBarButtonItem *)nextButton -{ - if (_nextButton) return _nextButton; - _nextButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:106 target:nil action:nil]; - return _nextButton; -} - --(UIBarButtonItem *)flexibleSpace -{ - if (_flexibleSpace) return _flexibleSpace; - _flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; - return _flexibleSpace; -} - --(UIBarButtonItem *)doneButton -{ - if (_doneButton) return _doneButton; - _doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:nil action:nil]; - return _doneButton; -} - #pragma mark - Helpers - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event diff --git a/XLForm/XL/Helpers/Views/XLFormTextView.h b/XLForm/XL/Helpers/Views/XLFormTextView.h index f157ca67..546239d4 100644 --- a/XLForm/XL/Helpers/Views/XLFormTextView.h +++ b/XLForm/XL/Helpers/Views/XLFormTextView.h @@ -29,9 +29,9 @@ @interface XLFormTextView : UITextView -@property (nonatomic) NSString *placeholder; -@property (nonatomic) UIColor *placeholderColor; +@property (nonatomic, copy) NSString *placeholder; +@property (nonatomic, copy) UIColor *placeholderColor; -@property (readonly) UILabel *placeHolderLabel; +@property (nonatomic, readonly, weak) UILabel *placeHolderLabel; @end diff --git a/XLForm/XL/Helpers/Views/XLFormTextView.m b/XLForm/XL/Helpers/Views/XLFormTextView.m index a5b2a70f..df2cf897 100644 --- a/XLForm/XL/Helpers/Views/XLFormTextView.m +++ b/XLForm/XL/Helpers/Views/XLFormTextView.m @@ -30,7 +30,7 @@ @implementation XLFormTextView - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:nil]; } - (instancetype)initWithFrame:(CGRect)frame @@ -67,14 +67,15 @@ - (void)drawRect:(CGRect)rect { if([[self placeholder] length] > 0){ if (_placeHolderLabel == nil ){ - _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(4,8,self.bounds.size.width - 16,0)]; - _placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping; - _placeHolderLabel.numberOfLines = 0; - _placeHolderLabel.backgroundColor = [UIColor clearColor]; - _placeHolderLabel.textColor = self.placeholderColor; - _placeHolderLabel.alpha = 0; - _placeHolderLabel.tag = 999; - [self addSubview:_placeHolderLabel]; + UILabel *placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(4,8,self.bounds.size.width - 16,0)]; + placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping; + placeHolderLabel.numberOfLines = 0; + placeHolderLabel.backgroundColor = [UIColor clearColor]; + placeHolderLabel.textColor = self.placeholderColor; + placeHolderLabel.alpha = 0; + placeHolderLabel.tag = 999; + [self addSubview:placeHolderLabel]; + _placeHolderLabel = placeHolderLabel; } _placeHolderLabel.text = self.placeholder; _placeHolderLabel.font = self.font; diff --git a/XLForm/XL/Validation/XLFormRegexValidator.h b/XLForm/XL/Validation/XLFormRegexValidator.h index f2cc95b8..a20bf085 100644 --- a/XLForm/XL/Validation/XLFormRegexValidator.h +++ b/XLForm/XL/Validation/XLFormRegexValidator.h @@ -29,8 +29,8 @@ @interface XLFormRegexValidator : XLFormValidator -@property NSString *msg; -@property NSString *regex; +@property (nonatomic, copy) NSString *msg; +@property (nonatomic, copy) NSString *regex; - (instancetype)initWithMsg:(NSString*)msg andRegexString:(NSString*)regex; + (XLFormRegexValidator *)formRegexValidatorWithMsg:(NSString *)msg regex:(NSString *)regex; diff --git a/XLForm/XL/Validation/XLFormValidationStatus.h b/XLForm/XL/Validation/XLFormValidationStatus.h index 78fd8273..0dc4cc3d 100644 --- a/XLForm/XL/Validation/XLFormValidationStatus.h +++ b/XLForm/XL/Validation/XLFormValidationStatus.h @@ -29,8 +29,8 @@ @interface XLFormValidationStatus : NSObject -@property NSString *msg; -@property BOOL isValid; +@property (nonatomic, copy) NSString *msg; +@property (nonatomic, assign) BOOL isValid; @property (nonatomic, weak) XLFormRowDescriptor *rowDescriptor; //-(instancetype)initWithMsg:(NSString*)msg andStatus:(BOOL)isValid;