Merge branch 'develop' into feature/swiftified_textField

This commit is contained in:
Kevin G Christiano 2019-10-29 12:21:37 -04:00
commit 134ba463b9
10 changed files with 108 additions and 26 deletions

View File

@ -19,7 +19,8 @@ import MVMCore
// Form Validation // Form Validation
var isRequired = false var isRequired = false
var fieldKey: String? var fieldKey: String?
var delegateObject: DelegateObject? var groupName: String?
var delegateObject: MVMCoreUIDelegateObject?
public static let defaultHeightWidth: CGFloat = 18.0 public static let defaultHeightWidth: CGFloat = 18.0
@ -104,12 +105,8 @@ import MVMCore
layoutIfNeeded() layoutIfNeeded()
shapeLayer?.removeAllAnimations() shapeLayer?.removeAllAnimations()
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol)
if let delegate = delegateObject as? FormValidationProtocol {
delegate.formValidatorModel?()?.enableByValidation()
}
updateAccessibilityLabel() updateAccessibilityLabel()
} }
} }
@ -211,7 +208,6 @@ import MVMCore
/// This will toggle the state of the Checkbox and execute the actionBlock if provided. /// This will toggle the state of the Checkbox and execute the actionBlock if provided.
public func toggleAndAction() { public func toggleAndAction() {
isSelected.toggle() isSelected.toggle()
actionBlock?() actionBlock?()
} }
@ -308,8 +304,9 @@ import MVMCore
/// Adjust accessibility label based on state of Checkbox. /// Adjust accessibility label based on state of Checkbox.
func updateAccessibilityLabel() { func updateAccessibilityLabel() {
// Attention: This needs to be addressed with the accessibility team. // Attention: This needs to be addressed with the accessibility team.
// NOTE: Currently emptying description part of MVMCoreUICheckBox accessibility label to avoid crashing!
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") {
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state) accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@%@", "", state)
} }
} }
@ -408,7 +405,8 @@ import MVMCore
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
guard let dictionary = json else { return } guard let dictionary = json else { return }
groupName = dictionary.optionalStringForKey("groupName")
if let fieldKey = dictionary[KeyFieldKey] as? String { if let fieldKey = dictionary[KeyFieldKey] as? String {
self.fieldKey = fieldKey self.fieldKey = fieldKey
} }
@ -462,7 +460,11 @@ import MVMCore
} }
// MARK:- FormValidationProtocol // MARK:- FormValidationProtocol
extension Checkbox: FormValidationProtocol { extension Checkbox: FormValidationFormFieldProtocol {
public func formFieldGroupName() -> String? {
return groupName
}
public func isValidField() -> Bool { public func isValidField() -> Bool {
return isRequired ? isSelected : true return isRequired ? isSelected : true

View File

@ -266,7 +266,7 @@ public typealias ActionBlock = () -> ()
let length = attribute["length"] as? Int let length = attribute["length"] as? Int
else { continue } else { continue }
let range = NSRange(location: location, length: length) var range = NSRange(location: location, length: length)
switch attributeType { switch attributeType {
case "underline": case "underline":
@ -278,7 +278,17 @@ public typealias ActionBlock = () -> ()
case "color": case "color":
if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty { if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty {
attributedString.removeAttribute(.foregroundColor, range: range) // crash fix: removing attribute, even though it does not exists
let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in
if attribute.key == .foregroundColor {
return true
} else {
return false
}
}
if foregroundColorAttributesArray.isEmpty == false {
attributedString.removeAttribute(.foregroundColor, range: range)
}
attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range)
} }
case "image": case "image":
@ -300,8 +310,28 @@ public typealias ActionBlock = () -> ()
case "font": case "font":
if let fontStyle = attribute.optionalStringForKey("style") { if let fontStyle = attribute.optionalStringForKey("style") {
let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle)
attributedString.removeAttribute(.font, range: range) // crash fix: removing font attribute, even though it does not exists
attributedString.removeAttribute(.foregroundColor, range: range) let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in
if attribute.key == .font {
return true
} else {
return false
}
}
if fontAttributesArray.isEmpty == false {
attributedString.removeAttribute(.font, range: range)
}
// crash fix: removing attribute, even though it does not exists
let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in
if attribute.key == .foregroundColor {
return true
} else {
return false
}
}
if foregroundColorAttributesArray.isEmpty == false {
attributedString.removeAttribute(.foregroundColor, range: range)
}
attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range) attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range)
} else { } else {
let fontSize = attribute["size"] as? CGFloat let fontSize = attribute["size"] as? CGFloat
@ -314,7 +344,17 @@ public typealias ActionBlock = () -> ()
} }
if let font = font { if let font = font {
attributedString.removeAttribute(.font, range: range) // crash fix: removing font attribute, even though it does not exists
let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in
if attribute.key == .font {
return true
} else {
return false
}
}
if fontAttributesArray.isEmpty == false {
attributedString.removeAttribute(.font, range: range)
}
attributedString.addAttribute(.font, value: font, range: range) attributedString.addAttribute(.font, value: font, range: range)
} }
} }

View File

@ -24,6 +24,7 @@ typedef void(^ValueChangeBlock)(void);
@property (nonatomic) BOOL shouldTouchToSwitch; @property (nonatomic) BOOL shouldTouchToSwitch;
@property (nullable, copy, nonatomic) ValueChangeBlock valueChangedBlock; @property (nullable, copy, nonatomic) ValueChangeBlock valueChangedBlock;
@property (nullable, copy, nonatomic) ValueChangeBlock actionBlock;
+ (nonnull instancetype)mvmSwitchDefault; + (nonnull instancetype)mvmSwitchDefault;
+ (nonnull instancetype)mvmSwitchDefaultWithValueChangeBlock:(nullable ValueChangeBlock)block; + (nonnull instancetype)mvmSwitchDefaultWithValueChangeBlock:(nullable ValueChangeBlock)block;

View File

@ -22,7 +22,7 @@ const CGFloat SwitchKnobWidth = 20;
const CGFloat SwitchKnobHeight = 20; const CGFloat SwitchKnobHeight = 20;
const CGFloat SwitchShakeIntensity = 2; const CGFloat SwitchShakeIntensity = 2;
@interface MVMCoreUISwitch () <FormValidationProtocol, MVMCoreUIViewConstrainingProtocol> @interface MVMCoreUISwitch () <FormValidationFormFieldProtocol, MVMCoreUIViewConstrainingProtocol>
@property (weak, nonatomic) UIView *baseView; @property (weak, nonatomic) UIView *baseView;
@property (weak, nonatomic) UIView *knobView; @property (weak, nonatomic) UIView *knobView;
@ -169,6 +169,18 @@ const CGFloat SwitchShakeIntensity = 2;
} }
[self setState:[json boolForKey:@"state"] animated:false]; [self setState:[json boolForKey:@"state"] animated:false];
self.delegate = delegateObject;
NSDictionary *actionMap = [json dict:@"actionMap"];
if (actionMap) {
[self addTarget:self action:@selector(addCustomAction) forControlEvents:UIControlEventTouchUpInside];
}
}
- (void)addCustomAction {
if (self.actionBlock) {
self.actionBlock();
}
} }
+ (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject { + (CGFloat)estimatedHeightForRow:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject {

View File

@ -61,6 +61,11 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
return nil return nil
} }
/// Space between the bottom view and the table sections, nil to fill. nil default
open func spaceBelowBottomView() -> CGFloat? {
return nil
}
/// can override to return a minimum fill space. /// can override to return a minimum fill space.
open func minimumFillSpace() -> CGFloat { open func minimumFillSpace() -> CGFloat {
return 0 return 0
@ -154,7 +159,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
bottomViewTopConstraint?.isActive = true bottomViewTopConstraint?.isActive = true
bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true
footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true
footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor, constant: spaceBelowBottomView() ?? 0).isActive = true
self.footerView = footerView self.footerView = footerView
showFooter(nil) showFooter(nil)
} }

View File

@ -330,7 +330,7 @@
bottomViewBot.active = YES; bottomViewBot.active = YES;
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view]; UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
safeAreaView.backgroundColor = footerView.backgroundColor; safeAreaView.backgroundColor = self.bottomView.backgroundColor;
self.safeAreaView = safeAreaView; self.safeAreaView = safeAreaView;
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]]; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]];

View File

@ -8,9 +8,9 @@
import UIKit import UIKit
@objcMembers public class HeadlineBodySwitch: ViewConstrainingView { @objcMembers open class HeadlineBodySwitch: ViewConstrainingView {
let headlineBody = HeadlineBody(frame: .zero) public let headlineBody = HeadlineBody(frame: .zero)
let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault() public let mvmSwitch = MVMCoreUISwitch.mvmSwitchDefault()
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
@ -35,7 +35,7 @@ import UIKit
} }
// MARK: - MVMCoreUIMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol
public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
headlineBody.setWithJSON(json?.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData) headlineBody.setWithJSON(json?.optionalDictionaryForKey("headlineBody"), delegateObject: delegateObject, additionalData: additionalData)
mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData)

View File

@ -33,7 +33,7 @@ import UIKit
} }
} }
// MARK: - FormValidationProtocol // MARK: - FormValidationFormFieldProtocol
extension RadioButtonModel: FormValidationFormFieldProtocol { extension RadioButtonModel: FormValidationFormFieldProtocol {
public func formFieldGroupName() -> String? { public func formFieldGroupName() -> String? {
return selectedRadioButton?.formFieldGroupName() ?? self.fieldGroupName return selectedRadioButton?.formFieldGroupName() ?? self.fieldGroupName

View File

@ -27,7 +27,7 @@
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
mapping = [@{ mapping = [@{
@"label": Label.class, @"label": Label.class,
@"separator": SeparatorView.class, @"line": SeparatorView.class,
@"button": ButtonView.class, @"button": ButtonView.class,
@"textButton": MFTextButton.class, @"textButton": MFTextButton.class,
@"header": StandardHeaderView.class, @"header": StandardHeaderView.class,
@ -43,7 +43,7 @@
@"checkbox" : Checkbox.class, @"checkbox" : Checkbox.class,
@"checkboxWithLabelView" : CheckboxWithLabelView.class, @"checkboxWithLabelView" : CheckboxWithLabelView.class,
@"cornerLabels" : CornerLabels.class, @"cornerLabels" : CornerLabels.class,
@"progressBar": ProgressBar.class, @"progressbar": ProgressBar.class,
@"multiProgressBar": MultiProgress.class, @"multiProgressBar": MultiProgress.class,
@"checkbox": MVMCoreUICheckBox.class, @"checkbox": MVMCoreUICheckBox.class,
@"radioButton": RadioButton.class, @"radioButton": RadioButton.class,

View File

@ -42,6 +42,11 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
return molecule return molecule
} }
// for bottom gutter/free space
open override func spaceBelowBottomView() -> CGFloat? {
return PaddingDefaultVerticalSpacing
}
open override func newDataBuildScreen() { open override func newDataBuildScreen() {
super.newDataBuildScreen() super.newDataBuildScreen()
setup() setup()
@ -153,6 +158,23 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
} }
} }
self.tableView?.deleteRows(at: indexPaths, with: animation) self.tableView?.deleteRows(at: indexPaths, with: animation)
// crash fix
self.tableView?.reloadData()
self.updateViewConstraints()
self.view.layoutIfNeeded()
}
public func removeListItem(_ molecule: [AnyHashable : Any], animation: UITableView.RowAnimation) {
var indexPaths: [IndexPath] = []
if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
return NSDictionary(dictionary: molecule).isEqual(to: moleculeInfo.molecule["molecule"] as? [AnyHashable : Any] ?? [:])
}) {
moleculesInfo?.remove(at: removeIndex)
indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0))
}
self.tableView?.deleteRows(at: indexPaths, with: animation)
// crash fix
self.tableView?.reloadData()
self.updateViewConstraints() self.updateViewConstraints()
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
} }