diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 40733e65..2cd04862 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -150,11 +150,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 */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -305,11 +304,10 @@ D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerViewController.swift; sourceTree = ""; }; D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; - D2C5001B21F8EE66001DA659 /* LabelWithInternalButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabelWithInternalButton.h; sourceTree = ""; }; - D2C5001C21F8EE66001DA659 /* LabelWithInternalButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LabelWithInternalButton.m; sourceTree = ""; }; 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 */ @@ -543,8 +541,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 */, @@ -566,6 +562,7 @@ D22D1F19220341F50077CEC0 /* MVMCoreUICheckBox.m */, D22D1F44220496A30077CEC0 /* MVMCoreUISwitch.h */, D22D1F45220496A30077CEC0 /* MVMCoreUISwitch.m */, + DBC4391C2245232D001AB423 /* LabelWithInternalButton.swift */, ); path = Views; sourceTree = ""; @@ -725,7 +722,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 */, D29DF16221E69996003B2FB9 /* MFViewController.h in Headers */, @@ -845,6 +841,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 */, @@ -897,7 +894,6 @@ D29DF25121E6A177003B2FB9 /* MFDigitTextBox.m in Sources */, DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */, D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */, - D2C5001E21F8EE67001DA659 /* LabelWithInternalButton.m in Sources */, D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */, D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */, D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */, 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..c87f0c54 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -0,0 +1,647 @@ +// +// 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 +public typealias ActionObjectDelegate = (NSObject & MVMCoreActionDelegateProtocol) +public typealias ButtonObjectDelegate = (NSObject & 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 alternateAttributeForActionText: String? + + public var attributedText: NSAttributedString? { + didSet(newAttributedText) { + if newAttributedText != nil { + let mutableAttributedText = newAttributedText as? NSMutableAttributedString + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = CGFloat(LabelWithInternalButtonLineSpace) + mutableAttributedText?.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: newAttributedText?.length ?? 0)) + + if let mutableAttText = mutableAttributedText { + label?.attributedText = mutableAttText + } + } + } + } + + // By default, it will follow most of the font standard in zepplin, and should need to be changed + public var normalTextFont: UIFont? + public var actionTextFont: UIFont? + public var normalTextColor: UIColor? + public var actionTextColor: UIColor? + + 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? + + public var text: String? { + didSet(newText) { + if newText != nil { + 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.translatesAutoresizingMaskIntoConstraints = false + label.isUserInteractionEnabled = false + label.numberOfLines = 0 + label.lineBreakMode = NSLineBreakMode.byWordWrapping + 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() + } + + if normalTextColor == nil { + normalTextColor = .black + } + + if actionTextColor == nil { + actionTextColor = .black + } + + if normalTextFont == nil { + normalTextFont = MFStyler.fontB2() + } + + if actionTextFont == nil { + actionTextFont = MFStyler.fontB2() + } + + //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) + } + + 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 + } + + let newLineAttributed = addNewLine ? "\n" : " " + + if let actions = actionMap, let actionMapCount = actionMap?.keys.count, actionMapCount > 0 { + + var actionString = actions.stringForkey(KeyTitle) + + if !actionString.isEmpty { + + actionString += newLineAttributed + + var actionAttributedString: NSAttributedString? + + if let b2Font = MFStyler.fontB2() { + actionAttributedString = MFStyler.styleGetAttributedString(actionString, font: b2Font, color: .black) + } + + actionText = actionString + setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) + + if let actionAttribString = actionAttributedString { + mutableAttributedString.append(actionAttribString) + } + } + } 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 { + actionText?.append(" ") + } + + return "\(frontText ?? "")\(actionText ?? "")\(backText ?? "")" + } + + private func getActionRange() -> NSRange { + + return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0) + } + + private func getFrontRange() -> NSRange { + + return NSRange(location: 0, length: frontText?.count ?? 0) + } + + private func getBackRange() -> NSRange { + + return NSRange(location: (frontText?.count ?? 0) + (actionText?.count ?? 0), length: backText?.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 _text = text + let actionRange: NSRange = rangeOfText(_text, startTag: startTag, endTag: endTag) + + frontText = (_text as NSString?)?.substring(with: NSRange(location: 0, length: actionRange.location)) + actionText = (_text as NSString?)?.substring(with: actionRange) + + if let frontTextCount = frontText?.count, let actiontextCount = actionText?.count { + backText = (_text as NSString?)?.substring(with: NSRange(location: frontTextCount + actiontextCount, length: (_text?.count ?? 0) - frontTextCount - actiontextCount)) + } + + self.text = getTextFromStringComponents() + setup() + } + + // Not used + private func rangeOfCurlyBracedText(_ text: String?) -> NSRange { + + return rangeOfText(text, startTag: "{", endTag: "}") + } + + // TODO: Review this + private func rangeOfText(_ text: String?, startTag: String?, endTag: String?) -> NSRange { + + var fullText = text + var range = NSRange(location: 0, length: 0) + let rangeOfLeftBrace: NSRange? = (fullText as NSString?)?.range(of: startTag ?? "") + + if rangeOfLeftBrace?.location != NSNotFound { + + if let rangeOfLeftBrace = rangeOfLeftBrace { + fullText = (fullText as NSString?)?.replacingCharacters(in: rangeOfLeftBrace, with: "") + } + let rangeOfRightBrace: NSRange? = (fullText as NSString?)?.range(of: endTag ?? "") + + if rangeOfRightBrace?.location != NSNotFound { + + let length: Int = (rangeOfRightBrace?.location ?? 0) - (rangeOfLeftBrace?.location ?? 0) + + if length > 0 { + range = NSRange(location: rangeOfLeftBrace?.location ?? 0, length: length) + if let rangeOfRightBrace = rangeOfRightBrace { + fullText = (fullText as NSString?)?.replacingCharacters(in: rangeOfRightBrace, with: "") + } + } + } + } + + return range + } + + @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]?) { + + if let theseAttributes = attributes as? [NSAttributedString.Key: Any], let thisAttributedText = attributedText { + let attributedString = NSMutableAttributedString(attributedString: thisAttributedText) + attributedString.addAttributes(theseAttributes, range: getActionRange()) + attributedText = attributedString + } + } + + @objc public func setAlternateNormalTextAttributes(_ attributes: [AnyHashable: Any]?) { + + if let thisAttributedText = attributedText, let theseAttributes = attributes as? [NSAttributedString.Key: Any] { + let attributedString = NSMutableAttributedString(attributedString: thisAttributedText) + attributedString.addAttributes(theseAttributes, range: getFrontRange()) + attributedString.addAttributes(theseAttributes, range: getBackRange()) + attributedText = attributedString + } + } + + @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 { + let name = actionTxt + return UIAccessibilityCustomAction(name: name, target: self, selector: #selector(LabelWithInternalButton.accessibilityCustomActions)) + } + + return UIAccessibilityCustomAction() + } + +} + + +extension LabelWithInternalButton: MVMCoreUIMoleculeViewProtocol { + //------------------------------------------------------ + // MARK: - Atomization + //------------------------------------------------------ + + // Default values for view. + @objc open func setAsMolecule() { + + /* + + var frontText: String? + var actionText: String? + var backText: String? + var attributedText: NSAttributedString? + // By default, it will follow most of the font standard in zepplin, and should need to be changed + var normalTextFont: UIFont? + var actionTextFont: UIFont? + var normalTextColor: UIColor? + var actionTextColor: UIColor? + var alternateAttributeForActionText: String? + var makeWholeViewClickable = false + */ + } + + @objc open func setWithJSON(_ json: [AnyHashable: Any]?, delegate: NSObject?, additionalData: [AnyHashable: Any]?) { + + // Configure class properties with JSON values + guard let jsonDictionary = json else { return } + + if let backgroundColorHex = jsonDictionary[KeyBackgroundColor] as? String { + backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) + } + + // if let strokeColorHex = jsonDictionary["strokeColor"] as? String { + // strokeColor = UIColor.mfGet(forHex: strokeColorHex) + // } + // + // if let isHiddenValue = jsonDictionary[KeyIsHidden] as? Bool { + // isHidden = isHiddenValue + // } + // + // if let isOpaqueValue = jsonDictionary[KeyIsOpaque] as? Bool { + // isOpaque = isOpaqueValue + // } + // + // if let lineWidthValue = jsonDictionary["lineWidth"] as? CGFloat { + // lineWidth = lineWidthValue + // } + + + } + +} diff --git a/MVMCoreUI/MVMCoreUI.h b/MVMCoreUI/MVMCoreUI.h index 53ceeb3e..fea15913 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