diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index a545216c..ff46aef9 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -157,11 +157,10 @@ D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; }; D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; - D2C5001D21F8EE67001DA659 /* LabelWithInternalButton.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001B21F8EE66001DA659 /* LabelWithInternalButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D2C5001E21F8EE67001DA659 /* LabelWithInternalButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001C21F8EE66001DA659 /* LabelWithInternalButton.m */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391A224421A0001AB423 /* CaretButton.swift */; }; + DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */; }; DBEFFA04225A829700230692 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB891E822253FA8500022516 /* Label.swift */; }; /* End PBXBuildFile section */ @@ -325,6 +324,7 @@ DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = ""; }; DBC4391A224421A0001AB423 /* CaretButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretButton.swift; sourceTree = ""; }; + DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelWithInternalButton.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -570,8 +570,6 @@ D29DF28621E7AC2B003B2FB9 /* MFLabel.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, D29DF31F21ED0CBA003B2FB9 /* LabelView.m */, - D2C5001B21F8EE66001DA659 /* LabelWithInternalButton.h */, - D2C5001C21F8EE66001DA659 /* LabelWithInternalButton.m */, D29DF28721E7AC2B003B2FB9 /* ViewConstrainingView.h */, D29DF28821E7AC2B003B2FB9 /* ViewConstrainingView.m */, D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */, @@ -593,6 +591,7 @@ D22D1F19220341F50077CEC0 /* MVMCoreUICheckBox.m */, D22D1F44220496A30077CEC0 /* MVMCoreUISwitch.h */, D22D1F45220496A30077CEC0 /* MVMCoreUISwitch.m */, + DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */, DB891E822253FA8500022516 /* Label.swift */, 0198F7A02256A80A0066C936 /* MFRadioButton.h */, 0198F7A22256A80A0066C936 /* MFRadioButton.m */, @@ -756,7 +755,6 @@ D29DF17521E69E1F003B2FB9 /* ButtonDelegateProtocol.h in Headers */, D29DF18221E69E54003B2FB9 /* SeparatorView.h in Headers */, D29DF26E21E6AA0B003B2FB9 /* FLAnimatedImage.h in Headers */, - D2C5001D21F8EE67001DA659 /* LabelWithInternalButton.h in Headers */, D29DF11621E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.h in Headers */, D29DF17721E69E1F003B2FB9 /* MFTextButton.h in Headers */, 01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */, @@ -877,6 +875,7 @@ D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */, D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */, + DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */, D29DF17C21E69E1F003B2FB9 /* MFTextButton.m in Sources */, D29DF2C521E7BF57003B2FB9 /* MFTabBarSwipeAnimator.m in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, @@ -933,7 +932,6 @@ DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */, 0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, - D2C5001E21F8EE67001DA659 /* LabelWithInternalButton.m in Sources */, D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */, 0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */, D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */, diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h index fb93c33a..771f8c01 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h @@ -62,8 +62,8 @@ static CGFloat const PrimaryButtonSmallHeight = 30.0; + (nullable instancetype)primaryGraySmallRedButton; + (nullable instancetype)primaryWhiteSmallRedButton; - - +// Returns the current height of the button. +- (CGFloat)getHeight; #pragma mark - For Subclassing diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index 5a708f2c..fd7ad2b9 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -86,6 +86,10 @@ #pragma mark - Sizing +- (CGFloat)getHeight { + return self.height.constant; +} + - (MFSizeObject *)innerPadding { return [MFSizeObject sizeObjectWithStandardSize:24.0 standardiPadPortraitSize:32.0 iPadProLandscapeSize:36.0]; } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.h b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.h deleted file mode 100644 index 10c0bfdd..00000000 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.h +++ /dev/null @@ -1,94 +0,0 @@ -// -// TextMixedWithButtonView.h -// mobilefirst -// -// Created by Chris Yang on 2/25/16. -// Copyright © 2016 Verizon Wireless. All rights reserved. -// - -#import -#import -#import -#import -#import -#import - -typedef void (^ActionBlock)(void); - -@interface LabelWithInternalButton : UIControl - -@property (nullable, copy, nonatomic) ActionBlock actionBlock; -@property (nullable, weak, nonatomic) MFLabel *label; - -@property (nullable, strong, nonatomic) NSString *frontText; -@property (nullable, strong, nonatomic) NSString *actionText; -@property (nullable, strong, nonatomic) NSString *backText; -@property (nullable, strong, nonatomic) NSAttributedString *attributedText; - -//by default, it will follow most of the font standard in zepplin, and should need to be changed -@property (nullable, strong, nonatomic) UIFont *normalTextFont; -@property (nullable, strong, nonatomic) UIFont *actionTextFont; -@property (nullable, strong, nonatomic) UIColor *normalTextColor; -@property (nullable, strong, nonatomic) UIColor *actionTextColor; - -@property (nullable, strong, nonatomic) NSString *alternateAttributeForActionText; - -@property (assign, nonatomic) BOOL makeWholeViewClickable; - -// with button delegate -- (nullable instancetype)initWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate; -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate; -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText actionText:(nullable NSString *)actionText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate; -// set with button delegate -- (void)setActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate; -- (void)setFrontText:(nullable NSString *)frontText actionMap:(nullable NSDictionary *)actionMap backText:(nullable NSString *)backText additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate; -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backText additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate; -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backAttributedText addNewLine:(BOOL) addNewLine additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate; - - -// legacy -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText actionText:(nullable NSString *)actionText backText:(nullable NSString *)backText actionBlock:(nullable ActionBlock)block; -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText actionText:(nullable NSString *)actionText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; - -- (nullable instancetype)initWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; - -//this assume that the action text is the "title" key in action map -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; - -// Convenience Initializer which assumes that the clickable text will be embedded in curly braces {}. -- (nullable instancetype)initWithClickableTextEmbeddedInCurlyBraces:(nullable NSString *)fullText actionMapForClickableText:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; -// Convenience Initializer which assumes that the clickable text will be embedded in any tag. -- (nullable instancetype)initWithText:(nullable NSString *)fullText startTag:(nullable NSString *)startTag endTag:(nullable NSString *)endTag actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; - - -//set action map -- (void)setActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; - -//set text with curly braces -- (void)setCurlyBracedText:(nonnull NSString *)text; -// set text with any tags -- (void)setWithText:(nullable NSString *)fullText startTag:(nullable NSString *)startTag endTag:(nullable NSString *)endTag actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; - -// Reset the text, action map and delegate -- (void)setTextWithClickableTextEmbeddedInCurlyBraces:(nullable NSString *)text textAttributes:(nullable NSDictionary *)attributes actionMapForClickableText:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate; - -// Reset the front text, back text, action map -- (void)setFrontText:(nullable NSString *)frontText actionMap:(nullable NSDictionary *)actionMap backText:(nullable NSString *)backText additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; - -// Reset the front text, back text, action map -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backText additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backAttributedText addNewLine:(BOOL) addNewLine additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; - -- (void)resetWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary * )additionalData delegate:(nullable NSObject *)delegate; - -- (void)setAlignment:(NSTextAlignment)textAlignment; - -- (void)setEnabled:(BOOL)enabled; - -- (void)setAlternateActionTextAttributes:(nullable NSDictionary *)attributes; - -- (void)setActionTextString:(nullable NSString *)actionText; - -- (nonnull UIAccessibilityCustomAction *)accessibilityCustomAction; - -@end diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.m b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.m deleted file mode 100644 index c4c509c5..00000000 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.m +++ /dev/null @@ -1,557 +0,0 @@ -// -// TextMixedWithButtonView.m -// mobilefirst -// -// Created by Chris Yang on 2/25/16. -// Copyright © 2016 Verizon Wireless. All rights reserved. -// - -#import "LabelWithInternalButton.h" -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@interface LabelWithInternalButton () - -@property (nullable, strong, nonatomic) NSString *text; - -@end - -@implementation LabelWithInternalButton - - - -- (nullable instancetype)initWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - return [self initWithFrontText:[actionMap stringForKey:KeyTitlePrefix] actionText:[actionMap stringForKey:KeyTitle] backText:[actionMap stringForKey:KeyTitlePostfix] actionMap:actionMap additionalData:additionalData actionDelegate:delegate buttonDelegate:buttonDelegate]; -} - -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - return [self initWithFrontText:frontText actionText:[actionMap stringForKey:KeyTitle] backText:backText actionMap:actionMap additionalData:additionalData actionDelegate:delegate buttonDelegate:buttonDelegate]; -} - -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText actionText:(nullable NSString *)actionText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - if (self = [super init]) { - [self setFrontText:frontText actionText:actionText actionMap:actionMap backText:backText additionalData:additionalData delegate:delegate buttonDelegate:buttonDelegate]; - } - return self; -} - -- (void)setActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - __weak typeof(self) weakSelf = self; - __weak typeof(delegate) weakDelegate = delegate; - __weak typeof(buttonDelegate) weakButtonDelegate = buttonDelegate; - self.actionBlock = ^{ - BOOL performAction = YES; - if (weakButtonDelegate && [weakButtonDelegate respondsToSelector:@selector(button:shouldPerformActionWithMap:additionalData:)]) { - performAction = [weakButtonDelegate button:weakSelf shouldPerformActionWithMap:actionMap additionalData:additionalData]; - } - - if (performAction) { - [[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegate:weakDelegate]; - } - }; -} - -- (void)setFrontText:(NSString *)frontText actionMap:(NSDictionary *)actionMap backText:(NSString *)backText additionalData:(NSDictionary *)additionalData delegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - [self setFrontText:frontText actionText:[actionMap stringForKey:KeyTitle] actionMap:actionMap backText:backText additionalData:additionalData delegate:delegate buttonDelegate:buttonDelegate]; -} - -- (void)setFrontText:(NSString *)frontText actionText:(NSString *)actionText actionMap:(NSDictionary *)actionMap backText:(NSString *)backText additionalData:(NSDictionary *)additionalData delegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - self.frontText = frontText; - [self setActionMap:actionMap additionalData:additionalData actionDelegate:delegate buttonDelegate:buttonDelegate]; - self.actionText = actionText; - self.backText = backText; - self.text = [self getTextFromStringComponents]; - [self setup]; -} - -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backText additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - [self setFrontAttributedText:frontAttributedText actionMap:actionMap backAttributedText:backText addNewLine:NO additionalData:additionalData delegate:delegate buttonDelegate:buttonDelegate]; -} - -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backAttributedText addNewLine:(BOOL) addNewLine additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate buttonDelegate:(nullable NSObject *)buttonDelegate { - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init]; - if (frontAttributedText) { - [attributedString appendAttributedString:frontAttributedText]; - // need to do this to fix the range issue - self.frontText = frontAttributedText.string; - } - - NSString *newLineAttributed = [[NSString alloc] initWithString:addNewLine?@"\n":@" "]; - - if (actionMap.allKeys.count > 0) { - - NSMutableString *actionString = [[actionMap stringForKey:KeyTitle] mutableCopy]; - - if (actionString.length > 0) { - - [actionString appendString:newLineAttributed]; - - NSAttributedString *actionAttributedString = [MFStyler styleGetAttributedString:actionString font:[MFStyler fontB2] color:[UIColor blackColor]]; - - self.actionText = actionString; - [self setActionMap:actionMap additionalData:additionalData actionDelegate:delegate buttonDelegate:buttonDelegate]; - - [attributedString appendAttributedString:actionAttributedString]; - } - } else { - self.actionText = nil; - self.actionBlock = nil; - } - - if (backAttributedText) { - [attributedString appendAttributedString:backAttributedText]; - } - self.attributedText = attributedString; - - //added this line for underlining - [self setAlternateActionTextAttributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)}]; -} - -#pragma mark - legacy - -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText actionText:(nullable NSString *)actionText backText:(nullable NSString *)backText actionBlock:(nullable ActionBlock)block { - if (self = [super init]) { - self.frontText = frontText; - self.actionText = actionText; - self.backText = backText; - self.actionBlock = block; - self.text = [self getTextFromStringComponents]; - [self setup]; - } - return self; -} -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText actionText:(nullable NSString *)actionText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate { - return [self initWithFrontText:frontText actionText:actionText backText:backText actionMap:actionMap additionalData:additionalData actionDelegate:delegate buttonDelegate:nil]; -} - -- (nullable instancetype)initWithActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate { - return [self initWithFrontText:[actionMap stringForKey:KeyTitlePrefix] actionText:[actionMap stringForKey:KeyTitle] backText:[actionMap stringForKey:KeyTitlePostfix] actionMap:actionMap additionalData:additionalData actionDelegate:delegate]; -} - -- (instancetype)init -{ - self = [super init]; - if (self) { - [self setup]; - } - return self; -} - - -- (instancetype)initWithCoder:(NSCoder *)coder -{ - self = [super initWithCoder:coder]; - if (self) { - [self setup]; - } - return self; -} - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if (self) { - [self setup]; - } - return self; -} - -- (nullable instancetype)initWithFrontText:(nullable NSString *)frontText backText:(nullable NSString *)backText actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate { - return [self initWithFrontText:frontText actionText:[actionMap stringForKey:KeyTitle] backText:backText actionMap:actionMap additionalData:additionalData actionDelegate:delegate]; -} - -// Convenience Initializer which assumes that the clickable text will be embedded in curly braces {}. -- (nullable instancetype)initWithClickableTextEmbeddedInCurlyBraces:(nullable NSString *)fullText actionMapForClickableText:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate { - return [self initWithText:fullText startTag:@"{" endTag:@"}" actionMap:actionMap additionalData:additionalData actionDelegate:delegate]; -} - -- (instancetype)initWithText:(nullable NSString *)fullText startTag:(nullable NSString *)startTag endTag:(nullable NSString *)endTag actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate -{ - self = [super init]; - if (self) { - [self setText:fullText startTag:startTag endTag:endTag]; - __weak typeof(delegate) weakDelegate = delegate; - self.actionBlock = ^{ - [[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegate:weakDelegate]; - }; - } - return self; -} - -- (void)setup { - if (!self.label) { - MFLabel *label = [[MFLabel alloc] initWithFrame:CGRectZero]; - - self.backgroundColor = [UIColor clearColor]; - label.translatesAutoresizingMaskIntoConstraints = NO; - label.userInteractionEnabled = NO; - label.numberOfLines = 0; - label.lineBreakMode = NSLineBreakByWordWrapping; - [label setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; - [self addSubview:label]; - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[label]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(label)]]; - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[label]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(label)]]; - - - self.label = label; - [label sizeToFit]; - } - - if (!self.normalTextColor) { - self.normalTextColor = [UIColor blackColor]; - } - if (!self.actionTextColor) { - self.actionTextColor = [UIColor blackColor]; - } - if (!self.normalTextFont) { - self.normalTextFont = [MFStyler fontB2]; - } - if (!self.actionTextFont) { - self.actionTextFont = [MFStyler fontB2]; - } - - //adding the underline - [self setAlternateActionTextAttributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)}]; - - self.label.attributedText = self.attributedText; - [self.label setAccessibilityTraits:UIAccessibilityTraitButton]; -} - -- (void)updateView:(CGFloat)size { - //reset font for app size change - self.normalTextFont = [MFStyler fontB2]; - self.actionTextFont = [MFStyler fontB2]; - self.label.attributedText = self.attributedText; -} - -#pragma mark - UIControl overide - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - if ([self areTouchesInActionString:touches]) { - [self sendActionsForControlEvents:UIControlEventTouchUpInside]; - [self performAction]; - } else { - [self sendActionsForControlEvents:UIControlEventTouchUpOutside]; - } -} - -#pragma mark - helper - -- (NSString *)getTextFromStringComponents { - if (self.frontText.length == 0) { - self.frontText = @""; - } else { - self.frontText = [self.frontText stringByAppendingString:[self spaceBetweenPartOne:self.frontText andPartTwo:self.actionText]]; - } - if (self.actionText.length == 0) { - self.actionText = @""; - } else { - self.actionText = [self.actionText stringByAppendingString:[self spaceBetweenPartOne:self.actionText andPartTwo:self.backText]]; - } - if (self.backText.length == 0) { - self.backText = @""; - } - return [[NSString alloc] initWithFormat:@"%@%@%@",self.frontText, self.actionText, self.backText]; -} - -- (NSString *)spaceBetweenPartOne:(NSString *)partOne andPartTwo:(NSString *)partTwo { - /*if (!partTwo || partTwo.length == 0 || [MFUtility validateString:partTwo withRegularExpression:@"[.,?!;:]"] || [partOne hasSuffix:@" "]) { - return @""; - }*/ - return @" "; -} - -- (NSRange)getActionRange { - return NSMakeRange(self.frontText.length, self.actionText.length); -} - -- (NSRange)getFrontRange { - return NSMakeRange(0, self.frontText.length); -} - -- (NSRange)getBackRange { - return NSMakeRange(self.frontText.length + self.actionText.length, self.backText.length);} - -- (void)performAction { - if (self.actionBlock) { - self.actionBlock(); - } -} - -- (NSArray *)getRangeArrayOfWordsInString:(NSString *)string withInitalIndex:(NSInteger)index { - NSArray *words = [string componentsSeparatedByString:@" "]; - NSMutableArray *rangeArray = [[NSMutableArray alloc] init]; - for (NSString *subString in words) { - NSString *finalSubString = [subString stringByAppendingString:@" "]; - NSInteger wordIndex = index; - NSInteger length = finalSubString.length; - NSRange subStringRange = NSMakeRange(wordIndex, length); - NSValue *rangeValue = [NSValue valueWithRange:subStringRange]; - [rangeArray addObject:rangeValue]; - index += length; - } - return rangeArray; -} - -- (NSArray *)getRectArrayFromRangeArray:(NSArray *)rangeArray { - NSMutableArray *rectArray = [[NSMutableArray alloc] init]; - for (NSValue *aValueOfRange in rangeArray) { - NSRange wordRange = [aValueOfRange rangeValue]; - CGRect rect = [self.label boundingRectForCharacterRange:wordRange]; - NSValue *rectValue = [NSValue valueWithCGRect:rect]; - [rectArray addObject:rectValue]; - } - return rectArray; -} - -- (BOOL)areTouchesInActionString:(NSSet *)touches { - if (UIAccessibilityIsVoiceOverRunning() || self.makeWholeViewClickable) { - return YES; - } - CGPoint location = [[touches anyObject] locationInView:self.label]; - NSString *actionString = self.actionText; - NSInteger index = [self getActionRange].location; - NSArray *rangeArray = [self getRangeArrayOfWordsInString:actionString withInitalIndex:index]; - NSArray *rectArray = [self getRectArrayFromRangeArray:rangeArray]; - BOOL result = NO; - for (NSValue *aValueOfRect in rectArray) { - CGRect wordRect = [aValueOfRect CGRectValue]; - if (CGRectContainsPoint(wordRect, location)){ - result = YES; - break; - } else if (wordRect.origin.x == 0 && wordRect.origin.y == 0 && wordRect.size.height == 0 && wordRect.size.width == 0) { - //incase word rect is not found for any reason, make the whole label to be clicable to avoid non functioning link in production. - result = YES; - break; - } - } - return result; -} - - -#pragma mark - setter - -- (void)setText:(NSString *)text { - if (text) { - _text = text; - - self.attributedText = [[NSAttributedString alloc] initWithString:text]; - //call the setters to properly set the attributes - [self setNormalTextFont:self.normalTextFont]; - [self setActionTextFont:self.actionTextFont]; - [self setNormalTextColor:self.normalTextColor]; - [self setActionTextColor:self.actionTextColor]; - - } -} - - -- (void)setCurlyBracedText:(nonnull NSString *)text { - [self setText:text startTag:@"{" endTag:@"}"]; -} - -- (void)setText:(NSString *)text startTag:(NSString *)startTag endTag:(NSString *)endTag { - NSRange actionRange = [self rangeOfText:&text startTag:startTag endTag:endTag]; - self.frontText = [text substringWithRange:NSMakeRange(0,actionRange.location)]; - self.actionText = [text substringWithRange:actionRange]; - self.backText = [text substringWithRange:NSMakeRange(self.frontText.length+self.actionText.length, text.length - self.frontText.length-self.actionText.length)]; - self.text = [self getTextFromStringComponents]; - [self setup]; -} - -- (NSRange)rangeOfCurlyBracedText:(NSString **)text { - return [self rangeOfText:text startTag:@"{" endTag:@"}"]; -} - -- (NSRange)rangeOfText:(NSString **)text startTag:(NSString *)startTag endTag:(NSString *)endTag { - - NSString *fullText = *text; - NSRange range = NSMakeRange(0, 0); - NSRange rangeOfLeftBrace = [fullText rangeOfString:startTag]; - - if (rangeOfLeftBrace.location != NSNotFound) { - - fullText = [fullText stringByReplacingCharactersInRange:rangeOfLeftBrace withString:@""]; - NSRange rangeOfRightBrace = [fullText rangeOfString:endTag]; - - if (rangeOfRightBrace.location != NSNotFound) { - - NSInteger length = (rangeOfRightBrace.location - rangeOfLeftBrace.location); - - if (length > 0) { - range = NSMakeRange(rangeOfLeftBrace.location, length); - fullText = [fullText stringByReplacingCharactersInRange:rangeOfRightBrace withString:@""]; - } - } - } - *text = fullText; - - return range; -} - -- (void)setNormalTextColor:(UIColor *)normalTextColor { - if (normalTextColor) { - _normalTextColor = normalTextColor; - [self setAlternateNormalTextAttributes:@{NSForegroundColorAttributeName:normalTextColor}]; - } -} - -- (void)setActionTextColor:(UIColor *)actionTextColor { - if (actionTextColor) { - _actionTextColor = actionTextColor; - [self setAlternateActionTextAttributes:@{NSForegroundColorAttributeName:actionTextColor}]; - } -} - -- (void)setAttributedText:(NSAttributedString *)attributedText { - _attributedText = attributedText; - NSMutableAttributedString *mutableAttributedText = [attributedText mutableCopy]; - NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - paragraphStyle.lineSpacing = LabelWithInternalButtonLineSpace; - [mutableAttributedText addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributedText.length)]; - - if (self.label && mutableAttributedText) { - self.label.attributedText = mutableAttributedText; - } - -} - -- (void)setNormalTextFont:(UIFont *)normalTextFont { - if (normalTextFont) { - _normalTextFont = normalTextFont; - [self setAlternateNormalTextAttributes:@{NSFontAttributeName:normalTextFont}]; - } -} - -- (void)setActionTextFont:(UIFont *)actionTextFont { - if (actionTextFont) { - _actionTextFont = actionTextFont; - [self setAlternateActionTextAttributes:@{NSFontAttributeName:actionTextFont}]; - } -} - -- (void)setActionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate { - [self setActionMap:actionMap additionalData:additionalData actionDelegate:delegate buttonDelegate:nil]; -} - -// Reset the text and action map -- (void)setTextWithClickableTextEmbeddedInCurlyBraces:(nullable NSString *)text textAttributes:(nullable NSDictionary *)attributes actionMapForClickableText:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate { - - self.attributedText = [[NSAttributedString alloc] initWithString:text attributes:attributes]; - [self setActionMap:actionMap additionalData:additionalData actionDelegate:delegate]; -} - -- (void)setWithText:(nullable NSString *)fullText startTag:(nullable NSString *)startTag endTag:(nullable NSString *)endTag actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)delegate { - [self setText:fullText startTag:startTag endTag:endTag]; - [self setActionMap:actionMap additionalData:additionalData actionDelegate:delegate]; -} - -- (void)setEnabled:(BOOL)enabled { - [super setEnabled:enabled]; - if (enabled) { - self.alpha = 1; - } else { - self.alpha = DisableOppacity; - } -} - -- (void)setFrontText:(NSString *)frontText actionMap:(NSDictionary *)actionMap backText:(NSString *)backText additionalData:(NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - [self setFrontText:frontText actionMap:actionMap backText:backText additionalData:additionalData delegate:delegate buttonDelegate:nil]; -} - -// Reset the front text, back text, action map -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backAttributedText additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - [self setFrontAttributedText:frontAttributedText actionMap:actionMap backAttributedText:backAttributedText addNewLine:NO additionalData:additionalData delegate:delegate]; -} - -- (void)setFrontAttributedText:(nullable NSAttributedString *)frontAttributedText actionMap:(nullable NSDictionary *)actionMap backAttributedText:(nullable NSAttributedString *)backAttributedText addNewLine:(BOOL) addNewLine additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - [self setFrontAttributedText:frontAttributedText actionMap:actionMap backAttributedText:backAttributedText addNewLine:addNewLine additionalData:additionalData delegate:delegate buttonDelegate:nil]; -} - -- (void)setAlignment:(NSTextAlignment)textAlignment { - self.label.textAlignment = textAlignment; -} - -- (void)setAlternateActionTextAttributes:(nullable NSDictionary *)attributes { - if (attributes) { - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; - [attributedString addAttributes:attributes range:[self getActionRange]]; - self.attributedText = attributedString; - } -} - -- (void)setAlternateNormalTextAttributes:(nullable NSDictionary *)attributes { - if (attributes) { - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; - [attributedString addAttributes:attributes range:[self getFrontRange]]; - [attributedString addAttributes:attributes range:[self getBackRange]]; - - self.attributedText = attributedString; - } -} - -- (void)setActionTextString:(nullable NSString *)actionText { - self.actionText = actionText; - self.text = [self getTextFromStringComponents]; - [self setup]; -} - -/// Used to just reset the texts and actions if already initialized -- (void)resetWithActionMap:(NSDictionary *)actionMap additionalData:(NSDictionary *)additionalData delegate:(NSObject *)delegate { - - self.frontText = [actionMap stringForKey:KeyTitlePrefix]; - self.actionText = [actionMap stringForKey:KeyTitle]; - self.backText = [actionMap stringForKey:KeyTitlePostfix]; - - - __weak typeof(delegate) weakDelegate = delegate; - self.actionBlock = ^{ - [[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegate:weakDelegate]; - }; - - self.text = [self getTextFromStringComponents]; - [self setup]; -} - -//#pragma mark - Accessibility -// -//-(BOOL)isAccessibilityElement{ -// return YES; -//} -// -//-(UIAccessibilityTraits)accessibilityTraits{ -// return UIAccessibilityTraitLink; -//} - -- (NSString *)replaceSpaceWithFakeSpace:(NSString *)string { - NSArray *words = [string componentsSeparatedByString:@" "]; - return [words componentsJoinedByString:@"\u00a0"]; -} -- (BOOL)accessibilityActivate { - if (self.actionBlock) { - self.actionBlock(); - return YES; - } else { - return NO; - } - -} - -- (UIAccessibilityCustomAction *)accessibilityCustomAction { - if (self.actionText.length) { - NSString *name = self.actionText; - return [[UIAccessibilityCustomAction alloc] initWithName:name target:self selector:@selector(accessibilityActivate)]; - } else { - return [[UIAccessibilityCustomAction alloc] init]; - } - -} -@end diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift new file mode 100644 index 00000000..3810294a --- /dev/null +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -0,0 +1,602 @@ +// +// LabelWithInternalButton.swift +// MVMCoreUI +// +// Created by Chris Yang on 2/25/16. +// Converted by Christiano, Kevin on 3/22/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import MVMCore + +public typealias ActionBlock = () -> Void +private typealias ActionIndiciesTuple = (startIndex: String.Index?, endIndex: String.Index?, revisedText: String?) +public typealias ActionObjectDelegate = (NSObjectProtocol & MVMCoreActionDelegateProtocol) +public typealias ButtonObjectDelegate = (NSObjectProtocol & ButtonDelegateProtocol) +public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProtocol & MVMCoreLoadDelegateProtocol & MVMCorePresentationDelegateProtocol & NSObjectProtocol + + +@objcMembers open class LabelWithInternalButton: UIControl, MVMCoreViewProtocol, MFButtonProtocol { + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + + public var actionBlock: ActionBlock? + public weak var label: MFLabel? + + public var attributedText: NSAttributedString? { + willSet(newAttributedText) { + if let newAttribText = newAttributedText, !newAttribText.string.isEmpty { + + let mutableAttributedText = NSMutableAttributedString(attributedString: newAttribText) + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = CGFloat(LabelWithInternalButtonLineSpace) + mutableAttributedText.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: newAttribText.length)) + label?.attributedText = mutableAttributedText + } + } + } + + // By default, it will follow most of the font standard in zepplin, and should need to be changed + public var normalTextFont: UIFont? = MFStyler.fontB2() { + willSet(newNormalFont) { + setAlternateNormalTextAttributes([NSAttributedString.Key.font: newNormalFont as Any]) + } + } + + public var actionTextFont: UIFont? = MFStyler.fontB2() { + willSet(newActionFont) { + setAlternateActionTextAttributes([NSAttributedString.Key.font: newActionFont as Any]) + } + } + + public var normalTextColor: UIColor = .black { + willSet(newNormalColor) { + setAlternateNormalTextAttributes([NSAttributedString.Key.foregroundColor: newNormalColor as Any]) + } + } + + public var actionTextColor: UIColor = .black { + willSet(newActionColor) { + setAlternateActionTextAttributes([NSAttributedString.Key.foregroundColor: newActionColor as Any]) + } + } + + public var makeWholeViewClickable = false + + override open var isEnabled: Bool { + didSet { + alpha = isEnabled ? 1 : DisableOppacity + } + } + + public var frontText: String? + public var actionText: String? + public var backText: String? + + private var text: String? { + willSet(newText) { + attributedText = NSAttributedString(string: newText ?? "") + setAlternateNormalTextAttributes([NSAttributedString.Key.font: normalTextFont as Any]) + setAlternateActionTextAttributes([NSAttributedString.Key.font: actionTextFont as Any]) + setAlternateNormalTextAttributes([NSAttributedString.Key.foregroundColor: normalTextColor as Any]) + setAlternateActionTextAttributes([NSAttributedString.Key.foregroundColor: actionTextColor as Any]) + } + } + + //------------------------------------------------------ + // MARK: - Initializaers + //------------------------------------------------------ + + public init() { + super.init(frame: CGRect.zero) + setup() + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + override public init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public init(frontText: String?, actionText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + super.init(frame: CGRect.zero) + + setFrontText(frontText, actionText: actionText, actionMap: actionMap, backText: backText, additionalData: additionalData, delegate: delegate, buttonDelegate: buttonDelegate) + } + + // MARK: - legacy + public init(frontText: String?, actionText: String?, backText: String?, actionBlock block: ActionBlock?) { + super.init(frame: CGRect.zero) + + self.frontText = frontText + self.actionText = actionText + self.backText = backText + actionBlock = block + text = getTextFromStringComponents() + setup() + } + + public convenience init(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + + self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix), actionText: actionMap?.optionalStringForKey(KeyTitle), backText: actionMap?.optionalStringForKey(KeyTitlePostfix), actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) + } + + public convenience init(frontText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + + self.init(frontText: frontText, actionText: actionMap?.optionalStringForKey(KeyTitle), backText: backText, actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) + } + + public convenience init(frontText: String?, actionText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + self.init(frontText: frontText, actionText: actionText, backText: backText, actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: nil) + } + + public convenience init(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix), actionText: actionMap?.optionalStringForKey(KeyTitle), backText: actionMap?.optionalStringForKey(KeyTitlePostfix), actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate) + } + + public convenience init(frontText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + self.init(frontText: frontText, actionText: actionMap?.optionalStringForKey(KeyTitle), backText: backText, actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate) + } + + // Convenience Initializer which assumes that the clickable text will be embedded in curly braces {}. + public convenience init(clickableTextEmbeddedInCurlyBraces fullText: String?, actionMapForClickableText actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + self.init(text: fullText, startTag: "{", endTag: "}", actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate) + } + + public init(text fullText: String?, startTag: String?, endTag: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + super.init(frame: CGRect.zero) + + setText(fullText, startTag: startTag, endTag: endTag) + + weak var weakDelegate: ActionObjectDelegate? = delegate + + actionBlock = { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: weakDelegate as? CoreObjectActionLoadPresentDelegate) + } + } + + //------------------------------------------------------ + // MARK: - Configuration + //------------------------------------------------------ + + private func setup() { + + if self.label == nil { + let label = MFLabel(frame: CGRect.zero) + + backgroundColor = .clear + label.isUserInteractionEnabled = false + label.setContentCompressionResistancePriority(.required, for: .vertical) + addSubview(label) + NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[label]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["label": label])) + NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[label]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["label": label])) + + self.label = label + label.sizeToFit() + } + + // Adding the underline + setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) + + self.label?.attributedText = attributedText + self.label?.accessibilityTraits = .button + } + + private func + setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + + weak var weakSelf: LabelWithInternalButton? = self + weak var weakDelegate: ActionObjectDelegate? = delegate + weak var weakButtonDelegate: ButtonObjectDelegate? = buttonDelegate + + actionBlock = { + var performAction = true + + if let wSelf = weakSelf, let wButtonDelegate = weakButtonDelegate, wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false + } + + if let wDelegate = weakDelegate as? CoreObjectActionLoadPresentDelegate, performAction { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: wDelegate) + } + } + } + + //------------------------------------------------------ + // MARK: - Methods + //------------------------------------------------------ + + @objc public func setFrontText(_ frontText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + setFrontText(frontText, actionText: actionMap?.optionalStringForKey(KeyTitle), actionMap: actionMap, backText: backText, additionalData: additionalData, delegate: delegate, buttonDelegate: buttonDelegate) + } + + @objc public func setFrontText(_ frontText: String?, actionText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + + self.frontText = frontText + setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) + self.actionText = actionText + self.backText = backText + text = getTextFromStringComponents() + setup() + } + + @objc public func setFrontAttributedText(_ frontAttributedText: NSAttributedString?, actionMap: [AnyHashable: Any]?, backAttributedText backText: NSAttributedString?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + + setFrontAttributedText(frontAttributedText, actionMap: actionMap, backAttributedText: backText, addNewLine: false, additionalData: additionalData, delegate: delegate, buttonDelegate: buttonDelegate) + } + + private func setFrontAttributedText(_ frontAttributedText: NSAttributedString?, actionMap: [AnyHashable: Any]?, backAttributedText: NSAttributedString?, addNewLine: Bool, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + + let mutableAttributedString = NSMutableAttributedString() + + if let frontAttributedText = frontAttributedText { + mutableAttributedString.append(frontAttributedText) + // Need to do this to fix the range issue + frontText = frontAttributedText.string + } + + if let b2Font = MFStyler.fontB2(), + let actions = actionMap, + actions.keys.count > 0, + let actionString = actions.optionalStringForKey(KeyTitle), + !actionString.isEmpty { + + let actionStringOnLine = actionString + (addNewLine ? "\n" : " ") + actionText = actionStringOnLine + setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) + mutableAttributedString.append(MFStyler.styleGetAttributedString(actionStringOnLine, font: b2Font, color: .black)) + + } else { + actionText = nil + actionBlock = nil + } + + if let backAttributedText = backAttributedText { + mutableAttributedString.append(backAttributedText) + } + + attributedText = mutableAttributedString + // Added this line for underlining + setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) + } + + @objc public func updateView(_ size: CGFloat) { + + // Reset font for app size change + normalTextFont = MFStyler.fontB2() + actionTextFont = MFStyler.fontB2() + label?.attributedText = attributedText + } + + //------------------------------------------------------ + // MARK: - UIControl Override + //------------------------------------------------------ + + override open func touchesEnded(_ touches: Set, with event: UIEvent?) { + + if areTouches(inActionString: touches) { + sendActions(for: .touchUpInside) + if let action = actionBlock { + action() + } + } else { + sendActions(for: .touchUpOutside) + } + } + + private func areTouches(inActionString touches: Set?) -> Bool { + + if UIAccessibility.isVoiceOverRunning || makeWholeViewClickable { + return true + } + + let location: CGPoint? = touches?.first?.location(in: label) + let actionString = actionText + let index: Int = getActionRange().location + let rangeArray = getRangeArrayOfWords(in: actionString, withInitalIndex: index) + let rectArray = getRectArray(fromRangeArray: rangeArray) + var result = false + + for aValueOfRect in rectArray as? [NSValue] ?? [] { + let wordRect: CGRect = aValueOfRect.cgRectValue + + if let position = location, wordRect.contains(position) { + result = true + break + } else if wordRect.origin.x == 0 && wordRect.origin.y == 0 && wordRect.size.height == 0 && wordRect.size.width == 0 { + // Incase word rect is not found for any reason, make the whole label to be clicable to avoid non functioning link in production. + result = true + break + } + } + + return result + } + + //------------------------------------------------------ + // MARK: - Helper + //------------------------------------------------------ + + private func getTextFromStringComponents() -> String { + + if let frontTxt = frontText, !frontTxt.isEmpty { + frontText?.append(" ") + } + + if let actionTxt = actionText, !actionTxt.isEmpty, let backTxt = backText, !backTxt.isEmpty { + actionText?.append(" ") + } + + return "\(frontText ?? "")\(actionText ?? "")\(backText ?? "")" + } + + private func getActionRange() -> NSRange { + + return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0) + } + + private func getRangeArrayOfWords(in string: String?, withInitalIndex index: Int) -> [Any]? { + + var index = index + let words = string?.components(separatedBy: " ") + var rangeArray = [AnyHashable]() + + for subString in words ?? [] { + let finalSubString = subString + " " + let wordIndex: Int = index + let length: Int = finalSubString.count + let subStringRange = NSRange(location: wordIndex, length: length) + let rangeValue = NSValue(range: subStringRange) + rangeArray.append(rangeValue) + index += length + } + + return rangeArray + } + + private func getRectArray(fromRangeArray rangeArray: [Any]?) -> [Any]? { + + var rectArray = [AnyHashable]() + + for aValueOfRange in rangeArray as? [NSValue] ?? [] { + let wordRange: NSRange = aValueOfRange.rangeValue + if let rect: CGRect = label?.boundingRect(forCharacterRange: wordRange) { + let rectValue = NSValue(cgRect: rect) + rectArray.append(rectValue) + } + } + + return rectArray + } + + @objc public func setCurlyBracedText(_ text: String) { + + setText(text, startTag: "{", endTag: "}") + } + + private func setText(_ text: String?, startTag: String?, endTag: String?) { + + let actionRange: ActionIndiciesTuple = rangeOfText(text, startTag: startTag, endTag: endTag) + + if let revisedText = actionRange.revisedText, let startBraceIndex = actionRange.startIndex, let endBraceIndex = actionRange.endIndex { + + frontText = String(revisedText[revisedText.startIndex.. ActionIndiciesTuple { + + var fullText = text ?? "" + var actionRange: ActionIndiciesTuple = (startIndex: nil, endIndex: nil, revisedText: nil) + + if let leftBrace = startTag, let rightBrace = endTag, fullText.contains(Character(leftBrace)) && fullText.contains(Character(rightBrace)) { + actionRange.startIndex = fullText.firstIndex(of: Character(leftBrace)) + fullText = fullText.replacingOccurrences(of: leftBrace, with: "") + + actionRange.endIndex = fullText.firstIndex(of: Character(rightBrace)) + fullText = fullText.replacingOccurrences(of: rightBrace, with: "") + } + + actionRange.revisedText = fullText + return actionRange + } + + @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + + setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: nil) + } + + // Reset the text and action map + @objc public func setTextWithClickableTextEmbeddedInCurlyBraces(_ text: String?, textAttributes attributes: [AnyHashable: Any]?, actionMapForClickableText actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + + attributedText = NSAttributedString(string: text ?? "", attributes: attributes as? [NSAttributedString.Key: Any]) + setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate) + } + + @objc public func setWithText(_ fullText: String?, startTag: String?, endTag: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?) { + + setText(fullText, startTag: startTag, endTag: endTag) + setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate) + } + + @objc public func setFrontText(_ frontText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?) { + + setFrontText(frontText, actionMap: actionMap, backText: backText, additionalData: additionalData, delegate: delegate, buttonDelegate: nil) + } + + // Reset the front text, back text, action map + @objc public func setFrontAttributedText(_ frontAttributedText: NSAttributedString?, actionMap: [AnyHashable: Any]?, backAttributedText: NSAttributedString?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?) { + + setFrontAttributedText(frontAttributedText, actionMap: actionMap, backAttributedText: backAttributedText, addNewLine: false, additionalData: additionalData, delegate: delegate) + } + + @objc public func setFrontAttributedText(_ frontAttributedText: NSAttributedString?, actionMap: [AnyHashable: Any]?, backAttributedText: NSAttributedString?, addNewLine: Bool, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?) { + + setFrontAttributedText(frontAttributedText, actionMap: actionMap, backAttributedText: backAttributedText, addNewLine: addNewLine, additionalData: additionalData, delegate: delegate, buttonDelegate: nil) + } + + @objc public func setAlignment(_ textAlignment: NSTextAlignment) { + + label?.textAlignment = textAlignment + } + + @objc public func setAlternateActionTextAttributes(_ attributes: [AnyHashable: Any]?) { + + guard let theseAttributes = attributes as? [NSAttributedString.Key: Any], let thisAttributedText = attributedText else { return } + let attributedString = NSMutableAttributedString(attributedString: thisAttributedText) + + if !attributedString.string.isEmpty { + attributedString.addAttributes(theseAttributes, range: getActionRange()) + } + attributedText = attributedString + } + + public func setAlternateNormalTextAttributes(_ attributes: [AnyHashable: Any]?) { + + guard let _attributedText = attributedText, let _attributes = attributes as? [NSAttributedString.Key: Any] else { return } + let attributedString = NSMutableAttributedString(attributedString: _attributedText) + + if !attributedString.string.isEmpty { + attributedString.addAttributes(_attributes, range: getFrontRange()) + attributedString.addAttributes(_attributes, range: getBackRange()) + } + attributedText = attributedString + } + + private func getFrontRange() -> NSRange { + + return NSRange(location: 0, length: frontText?.count ?? 0) + } + + private func getBackRange() -> NSRange { + + let textLocation: Int = (frontText?.count ?? 0) + (actionText?.count ?? 0) + let rangeLength: Int = backText?.count ?? 0 + + return NSRange(location: textLocation, length: rangeLength) + } + + @objc public func setActionTextString(_ actionText: String?) { + + self.actionText = actionText + text = getTextFromStringComponents() + setup() + } + + /// Used to just reset the texts and actions if already initialized + @objc public func reset(withActionMap actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?) { + + frontText = actionMap?.optionalStringForKey(KeyTitlePrefix) + actionText = actionMap?.optionalStringForKey(KeyTitle) + backText = actionMap?.optionalStringForKey(KeyTitlePostfix) + + weak var weakDelegate: ActionObjectDelegate? = delegate + + actionBlock = { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: weakDelegate as? CoreObjectActionLoadPresentDelegate) + } + + text = getTextFromStringComponents() + setup() + } + + //------------------------------------------------------ + // MARK: - Accessibility + //------------------------------------------------------ + + // Not Used + @objc public func replaceSpace(withFakeSpace string: String?) -> String? { + + return string?.components(separatedBy: " ").joined(separator: "\u{00a0}") + } + + @objc override open func accessibilityActivate() -> Bool { + + if let action = actionBlock { + action() + return true + } + + return false + } + + @objc public func accessibilityCustomAction() -> UIAccessibilityCustomAction? { + + if let actionTxt = actionText, !actionTxt.isEmpty { + return UIAccessibilityCustomAction(name: actionTxt, target: self, selector: #selector(LabelWithInternalButton.accessibilityCustomActions)) + } + + return UIAccessibilityCustomAction() + } +} + +extension LabelWithInternalButton: MVMCoreUIMoleculeViewProtocol { + //------------------------------------------------------ + // MARK: - Atomization + //------------------------------------------------------ + + // Default values for view. + @objc open func setAsMolecule() { + + } + + @objc open func setWithJSON(_ json: [AnyHashable: Any]?, delegate: NSObject?, additionalData: [AnyHashable: Any]?) { + + // Configure class properties with JSON values + guard let dictionary = json else { return } + + if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { + self.backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) + } + + if let normalTextFont = dictionary["normalTextFont"] as? String, let normalSize = dictionary["normalSize"] as? CGFloat { + self.normalTextFont = UIFont(name: normalTextFont, size: normalSize) + } + + if let actionTextFont = dictionary["actionTextFont"] as? String, let actionSize = dictionary["actionSize"] as? CGFloat { + self.actionTextFont = UIFont(name: actionTextFont, size: actionSize) + } + + if let normalTextColorHex = dictionary["normalTextColor"] as? String { + self.normalTextColor = UIColor.mfGet(forHex: normalTextColorHex) + } + + if let actionTextColorHex = dictionary["actionTextColor"] as? String { + self.actionTextColor = UIColor.mfGet(forHex: actionTextColorHex) + } + + if let makeWholeViewClickable = dictionary["makeWholeViewClickable"] as? Bool { + self.makeWholeViewClickable = makeWholeViewClickable + } + + if let frontText = dictionary["frontText"] as? String { + self.frontText = frontText + } + + if let backText = dictionary["backText"] as? String { + self.backText = backText + } + + if let actionText = dictionary["actionText"] as? String { + self.actionText = actionText + } + + // Want this to be last because it has a willSet feature. + if let text = dictionary["text"] as? String { + self.text = text + } + } +} diff --git a/MVMCoreUI/MVMCoreUI.h b/MVMCoreUI/MVMCoreUI.h index e9e7c92a..10fe0a36 100644 --- a/MVMCoreUI/MVMCoreUI.h +++ b/MVMCoreUI/MVMCoreUI.h @@ -74,7 +74,6 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[]; #pragma mark Views #import #import -#import #import #import #import diff --git a/MVMCoreUI/Molecules/TwoButtonView.swift b/MVMCoreUI/Molecules/TwoButtonView.swift index 9e6cf4ee..ee03ff49 100644 --- a/MVMCoreUI/Molecules/TwoButtonView.swift +++ b/MVMCoreUI/Molecules/TwoButtonView.swift @@ -41,11 +41,7 @@ import UIKit } let primaryButtonMap = json?.optionalDictionaryForKey("primaryButton") let secondaryButtonMap = json?.optionalDictionaryForKey("secondaryButton") - setupUI(withPrimaryButtonMap: primaryButtonMap, secondaryButtonMap: secondaryButtonMap, legacy: false) - primaryButton?.setAsStandardCustom() - secondaryButton?.setAsSecondaryCustom() - primaryButton?.setWithJSON(primaryButtonMap, delegate: delegate, additionalData: additionalData) - secondaryButton?.setWithJSON(secondaryButtonMap, delegate: delegate, additionalData: additionalData) + set(primaryButtonJSON: primaryButtonMap, secondaryButtonJSON: secondaryButtonMap, actionDelegate: delegate, additionalData: additionalData, buttonDelegate: delegate) } // MARK: - Constraining @@ -141,6 +137,14 @@ import UIKit } } + open func set(primaryButtonJSON: [AnyHashable: Any]?, secondaryButtonJSON: [AnyHashable: Any]?, actionDelegate: NSObjectProtocol?, additionalData: [AnyHashable: Any]?, buttonDelegate: Any?) { + setupUI(withPrimaryButtonMap: primaryButtonJSON, secondaryButtonMap: secondaryButtonJSON, legacy: false) + primaryButton?.setAsStandardCustom() + secondaryButton?.setAsSecondaryCustom() + primaryButton?.setWithJSON(primaryButtonJSON, delegate: actionDelegate as? NSObject, additionalData: additionalData) + secondaryButton?.setWithJSON(secondaryButtonJSON, delegate: actionDelegate as? NSObject, additionalData: additionalData) + } + // MARK: - Legacy open func setup(withButtonMap buttonMap: [AnyHashable: Any]?, actionDelegate: NSObjectProtocol?, additionalData: [AnyHashable: Any]?, buttonDelegate: Any?) { let secondaryButtonMap = buttonMap?.optionalDictionaryForKey(KeySecondaryButton)