merge
This commit is contained in:
commit
e8b9de6d35
@ -19,7 +19,7 @@
|
||||
01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */; };
|
||||
01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; };
|
||||
B8200E192281DC1A007245F4 /* ProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E182281DC1A007245F4 /* ProgressBarView.swift */; };
|
||||
B8200E192281DC1A007245F4 /* ProgressBarWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E182281DC1A007245F4 /* ProgressBarWithLabel.swift */; };
|
||||
D206997721FB8A0B00CAE0DE /* MVMCoreUINavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = D206997521FB8A0B00CAE0DE /* MVMCoreUINavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D206997821FB8A0B00CAE0DE /* MVMCoreUINavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = D206997621FB8A0B00CAE0DE /* MVMCoreUINavigationController.m */; };
|
||||
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; };
|
||||
@ -167,6 +167,7 @@
|
||||
D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */; };
|
||||
D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; };
|
||||
D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; };
|
||||
DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; };
|
||||
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 */; };
|
||||
@ -186,7 +187,7 @@
|
||||
01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFTextFieldListView.swift; sourceTree = "<group>"; };
|
||||
01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldListFormViewController.swift; sourceTree = "<group>"; };
|
||||
B8200E142280C4CF007245F4 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
|
||||
B8200E182281DC1A007245F4 /* ProgressBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarView.swift; sourceTree = "<group>"; };
|
||||
B8200E182281DC1A007245F4 /* ProgressBarWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarWithLabel.swift; sourceTree = "<group>"; };
|
||||
D206997521FB8A0B00CAE0DE /* MVMCoreUINavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUINavigationController.h; sourceTree = "<group>"; };
|
||||
D206997621FB8A0B00CAE0DE /* MVMCoreUINavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUINavigationController.m; sourceTree = "<group>"; };
|
||||
D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = "<group>"; };
|
||||
@ -338,6 +339,7 @@
|
||||
D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = "<group>"; };
|
||||
D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = "<group>"; };
|
||||
DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = "<group>"; };
|
||||
DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
|
||||
DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = "<group>"; };
|
||||
DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = "<group>"; };
|
||||
@ -468,7 +470,7 @@
|
||||
D274CA322236A78900B01B62 /* StandardFooterView.swift */,
|
||||
D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */,
|
||||
B8200E142280C4CF007245F4 /* ProgressBar.swift */,
|
||||
B8200E182281DC1A007245F4 /* ProgressBarView.swift */,
|
||||
B8200E182281DC1A007245F4 /* ProgressBarWithLabel.swift */,
|
||||
);
|
||||
path = Molecules;
|
||||
sourceTree = "<group>";
|
||||
@ -590,6 +592,7 @@
|
||||
children = (
|
||||
DBC4391622442196001AB423 /* CaretView.swift */,
|
||||
DBC4391722442197001AB423 /* DashLine.swift */,
|
||||
DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */,
|
||||
D29DF17E21E69E2E003B2FB9 /* MFView.h */,
|
||||
D29DF17F21E69E2E003B2FB9 /* MFView.m */,
|
||||
D29DF31E21ED0CBA003B2FB9 /* LabelView.h */,
|
||||
@ -950,6 +953,7 @@
|
||||
D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */,
|
||||
D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */,
|
||||
D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */,
|
||||
DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */,
|
||||
D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */,
|
||||
D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */,
|
||||
D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */,
|
||||
@ -975,7 +979,7 @@
|
||||
D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */,
|
||||
D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */,
|
||||
D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */,
|
||||
B8200E192281DC1A007245F4 /* ProgressBarView.swift in Sources */,
|
||||
B8200E192281DC1A007245F4 /* ProgressBarWithLabel.swift in Sources */,
|
||||
D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */,
|
||||
0105618D224BBE7700E1557D /* FormValidator.swift in Sources */,
|
||||
D22D1F1B220341F60077CEC0 /* MVMCoreUICheckBox.m in Sources */,
|
||||
|
||||
@ -9,28 +9,49 @@
|
||||
|
||||
import MVMCore
|
||||
|
||||
public typealias ActionBlock = () -> Void
|
||||
|
||||
@objc open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol {
|
||||
|
||||
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {
|
||||
//------------------------------------------------------
|
||||
// MARK: - Properties
|
||||
// MARK: - General Properties
|
||||
//------------------------------------------------------
|
||||
|
||||
public var makeWholeViewClickable = false
|
||||
|
||||
// Set this property if you want updateView to update the font based on this standard and the size passed in.
|
||||
@objc public var standardFontSize: CGFloat = 0.0
|
||||
/// Set this property if you want updateView to update the font based on this standard and the size passed in.
|
||||
public var standardFontSize: CGFloat = 0.0
|
||||
|
||||
// Set this to use a custom sizing object during updateView instead of the standard.
|
||||
@objc public var sizeObject: MFSizeObject?
|
||||
|
||||
@objc public var scaleSize: NSNumber?
|
||||
/// Set this to use a custom sizing object during updateView instead of the standard.
|
||||
public var sizeObject: MFSizeObject?
|
||||
public var scaleSize: NSNumber?
|
||||
|
||||
// Used for scaling the font in updateView.
|
||||
private var originalAttributedString: NSAttributedString?
|
||||
|
||||
@objc public var hasText: Bool {
|
||||
public var hasText: Bool {
|
||||
guard let text = text, let attributedText = attributedText else { return false }
|
||||
return !text.isEmpty || !attributedText.string.isEmpty
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Multi-Action Text
|
||||
//------------------------------------------------------
|
||||
|
||||
public var clauses: [ActionableClause] = [] {
|
||||
didSet { isUserInteractionEnabled = !clauses.isEmpty }
|
||||
}
|
||||
|
||||
/// Used for tappable links in the text.
|
||||
public struct ActionableClause {
|
||||
var range: NSRange?
|
||||
var actionBlock: ActionBlock?
|
||||
|
||||
func performAction() {
|
||||
actionBlock?()
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Initialization
|
||||
//------------------------------------------------------
|
||||
@ -41,6 +62,10 @@ import MVMCore
|
||||
numberOfLines = 0
|
||||
lineBreakMode = .byWordWrapping
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped(_:)))
|
||||
tapGesture.numberOfTapsRequired = 1
|
||||
addGestureRecognizer(tapGesture)
|
||||
}
|
||||
|
||||
@objc public init() {
|
||||
@ -64,21 +89,24 @@ import MVMCore
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Functions
|
||||
// MARK: - Factory Functions
|
||||
//------------------------------------------------------
|
||||
|
||||
/// H1 -> HeadlineLarge
|
||||
@objc public static func commonLabelH1(_ scale: Bool) -> Label {
|
||||
let label = Label.label()
|
||||
label.styleH1(scale)
|
||||
return label
|
||||
}
|
||||
|
||||
/// H2 -> Headline
|
||||
@objc public static func commonLabelH2(_ scale: Bool) -> Label {
|
||||
let label = Label.label()
|
||||
label.styleH2(scale)
|
||||
return label
|
||||
}
|
||||
|
||||
/// H3 -> SubHead
|
||||
@objc public static func commonLabelH3(_ scale: Bool) -> Label {
|
||||
let label = Label.label()
|
||||
label.styleH3(scale)
|
||||
@ -91,18 +119,21 @@ import MVMCore
|
||||
return label
|
||||
}
|
||||
|
||||
/// B1 -> SubTitle
|
||||
@objc public static func commonLabelB1(_ scale: Bool) -> Label {
|
||||
let label = Label.label()
|
||||
label.styleB1(scale)
|
||||
return label
|
||||
}
|
||||
|
||||
/// B2 -> Body
|
||||
@objc public static func commonLabelB2(_ scale: Bool) -> Label {
|
||||
let label = Label.label()
|
||||
label.styleB2(scale)
|
||||
return label
|
||||
}
|
||||
|
||||
/// B3 -> Legal
|
||||
@objc public static func commonLabelB3(_ scale: Bool) -> Label {
|
||||
let label = Label.label()
|
||||
label.styleB3(scale)
|
||||
@ -142,9 +173,9 @@ import MVMCore
|
||||
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
|
||||
guard let label = label else { return }
|
||||
|
||||
label.attributedText = nil
|
||||
label.text = json?.optionalStringForKey(KeyText)
|
||||
|
||||
|
||||
setLabel(label, withHTML: json?.optionalStringForKey("html"))
|
||||
|
||||
if let backgroundColorHex = json?.optionalStringForKey(KeyBackgroundColor), !backgroundColorHex.isEmpty {
|
||||
@ -173,21 +204,20 @@ import MVMCore
|
||||
let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: label.font as UIFont,
|
||||
NSAttributedString.Key.foregroundColor: label.textColor as UIColor])
|
||||
for case let attribute as [String: Any] in attributes {
|
||||
|
||||
guard let attributeType = attribute.optionalStringForKey(KeyType),
|
||||
let location = attribute["location"] as? Int,
|
||||
let length = attribute["length"] as? Int
|
||||
else { continue }
|
||||
|
||||
|
||||
let range = NSRange(location: location, length: length)
|
||||
|
||||
|
||||
switch attributeType {
|
||||
case "underline":
|
||||
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
|
||||
|
||||
|
||||
case "strikethrough":
|
||||
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
|
||||
|
||||
|
||||
case "color":
|
||||
if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty {
|
||||
attributedString.removeAttribute(.foregroundColor, range: range)
|
||||
@ -214,6 +244,13 @@ import MVMCore
|
||||
attributedString.addAttribute(.font, value: font, range: range)
|
||||
}
|
||||
}
|
||||
case "action":
|
||||
guard let actionLabel = label as? Label else { continue }
|
||||
|
||||
actionLabel.addTappableLinkAttribute(range: range,
|
||||
actionMap: json,
|
||||
additionalData: additionalData,
|
||||
delegateObject: delegate)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
@ -222,6 +259,8 @@ import MVMCore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//------------------------------------------------------
|
||||
@ -301,10 +340,10 @@ import MVMCore
|
||||
standardFontSize = 0
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Atomization
|
||||
//------------------------------------------------------
|
||||
}
|
||||
|
||||
// MARK: - Atomization
|
||||
extension Label {
|
||||
|
||||
public func reset() {
|
||||
text = nil
|
||||
@ -314,6 +353,7 @@ import MVMCore
|
||||
}
|
||||
|
||||
@objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
clauses = []
|
||||
Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData)
|
||||
originalAttributedString = attributedText
|
||||
}
|
||||
@ -323,7 +363,7 @@ import MVMCore
|
||||
}
|
||||
|
||||
public func needsToBeConstrained() -> Bool {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
public func alignment() -> UIStackView.Alignment {
|
||||
@ -331,6 +371,111 @@ import MVMCore
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Multi-Action Functionality
|
||||
extension Label {
|
||||
|
||||
|
||||
/// Reseting to default Label values.
|
||||
@objc public func clearActionableClauses() {
|
||||
|
||||
guard let attributedText = attributedText else { return }
|
||||
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
|
||||
|
||||
clauses.forEach { clause in
|
||||
guard let range = clause.range else { return }
|
||||
mutableAttributedString.removeAttribute(NSAttributedString.Key.underlineStyle, range: range)
|
||||
}
|
||||
|
||||
self.attributedText = mutableAttributedString
|
||||
clauses = []
|
||||
}
|
||||
|
||||
public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock {
|
||||
return { [weak self] in
|
||||
if let wSelf = self, (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func setDefaultAttributes(range: NSRange) {
|
||||
|
||||
guard let attributedText = attributedText else { return }
|
||||
|
||||
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
|
||||
mutableAttributedString.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range)
|
||||
|
||||
self.attributedText = mutableAttributedString
|
||||
}
|
||||
|
||||
/**
|
||||
Provides an actionable range of text.
|
||||
|
||||
- Attention: This method expects text to be set first. Otherwise, it will do nothing.
|
||||
- Parameters:
|
||||
- range: The range of text to be tapped.
|
||||
- actionBlock: The code triggered when tapping the range of text.
|
||||
*/
|
||||
@objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) {
|
||||
|
||||
setDefaultAttributes(range: range)
|
||||
clauses.append(ActionableClause(range: range, actionBlock: actionBlock))
|
||||
}
|
||||
|
||||
/**
|
||||
Provides an actionable range of text.
|
||||
|
||||
- Attention: This method expects text to be set first. Otherwise, it will do nothing.
|
||||
- Parameters:
|
||||
- range: The range of text to be tapped.
|
||||
- actionMap:
|
||||
- delegate:
|
||||
- additionalData:
|
||||
*/
|
||||
@objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
|
||||
setDefaultAttributes(range: range)
|
||||
clauses.append(ActionableClause(range: range,
|
||||
actionBlock: createActionBlockFrom(actionMap: actionMap,
|
||||
additionalData: additionalData,
|
||||
delegateObject: delegateObject)))
|
||||
}
|
||||
|
||||
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
|
||||
|
||||
for clause in clauses {
|
||||
if let range = clause.range, gesture.didTapAttributedTextInLabel(self, inRange: range) {
|
||||
clause.performAction()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
extension UITapGestureRecognizer {
|
||||
|
||||
func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool {
|
||||
|
||||
if label.makeWholeViewClickable {
|
||||
return true
|
||||
}
|
||||
|
||||
guard let attributedText = label.attributedText else { return false }
|
||||
|
||||
let layoutManager = NSLayoutManager()
|
||||
let textContainer = NSTextContainer(size: .zero)
|
||||
let textStorage = NSTextStorage(attributedString: attributedText)
|
||||
|
||||
layoutManager.addTextContainer(textContainer)
|
||||
textStorage.addLayoutManager(layoutManager)
|
||||
|
||||
textContainer.lineFragmentPadding = 0.0
|
||||
textContainer.lineBreakMode = label.lineBreakMode
|
||||
textContainer.maximumNumberOfLines = label.numberOfLines
|
||||
textContainer.size = label.bounds.size
|
||||
|
||||
let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
|
||||
|
||||
return NSLocationInRange(indexOfCharacter, targetRange)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,25 +9,22 @@
|
||||
|
||||
import MVMCore
|
||||
|
||||
public typealias ActionBlock = () -> Void
|
||||
private typealias ActionableStringTuple = (front: String?, action: String?, end: 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 {
|
||||
@available(*, deprecated, message: "Use Label instead.")
|
||||
@objcMembers open class LabelWithInternalButton: UIControl, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol {
|
||||
//------------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//------------------------------------------------------
|
||||
|
||||
public var actionBlock: ActionBlock?
|
||||
public weak var label: Label?
|
||||
|
||||
public var attributedText: NSAttributedString? {
|
||||
willSet(newAttributedText) {
|
||||
if let newAttribText = newAttributedText {
|
||||
|
||||
let mutableAttributedText = NSMutableAttributedString(attributedString: newAttribText)
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.lineSpacing = CGFloat(LabelWithInternalButtonLineSpace)
|
||||
@ -62,16 +59,34 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
}
|
||||
}
|
||||
|
||||
public var actionBlock: ActionBlock? {
|
||||
willSet(newActionBlock) {
|
||||
if newActionBlock == nil {
|
||||
label?.clearActionableClauses()
|
||||
} else {
|
||||
label?.clauses = [Label.ActionableClause(range: actionRange, actionBlock: newActionBlock)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var frontRange: NSRange {
|
||||
return NSRange(location: 0, length: frontText?.count ?? 0)
|
||||
}
|
||||
|
||||
private var actionRange: NSRange {
|
||||
return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0)
|
||||
}
|
||||
|
||||
public var makeWholeViewClickable = false
|
||||
private var backRange: NSRange {
|
||||
return NSRange(location: (frontText?.count ?? 0) + (actionText?.count ?? 0), length: backText?.count ?? 0)
|
||||
}
|
||||
|
||||
public var makeWholeViewClickable = false {
|
||||
willSet(newBool) { label?.makeWholeViewClickable = newBool }
|
||||
}
|
||||
|
||||
override open var isEnabled: Bool {
|
||||
didSet {
|
||||
alpha = isEnabled ? 1 : DisableOppacity
|
||||
}
|
||||
didSet { alpha = isEnabled ? 1 : DisableOppacity }
|
||||
}
|
||||
|
||||
public var frontText: String?
|
||||
@ -81,9 +96,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
private var internalText: String = ""
|
||||
|
||||
public var text: String? {
|
||||
get {
|
||||
return internalText
|
||||
}
|
||||
get { return internalText }
|
||||
set {
|
||||
guard let text = newValue else { return }
|
||||
internalText = text
|
||||
@ -101,117 +114,92 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
|
||||
public init() {
|
||||
super.init(frame: .zero)
|
||||
setup()
|
||||
createLabel()
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setup()
|
||||
createLabel()
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup()
|
||||
createLabel()
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
// MARK: - legacy
|
||||
// Legacy
|
||||
public init(frontText: String?, actionText: String?, backText: String?, actionBlock block: ActionBlock?) {
|
||||
super.init(frame: .zero)
|
||||
|
||||
createLabel()
|
||||
self.frontText = frontText
|
||||
self.actionText = actionText
|
||||
self.backText = backText
|
||||
actionBlock = block
|
||||
text = getTextFromStringComponents()
|
||||
setup()
|
||||
actionBlock = block
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
public convenience init(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix),
|
||||
actionText: actionMap?.optionalStringForKey(KeyTitle),
|
||||
backText: actionMap?.optionalStringForKey(KeyTitlePostfix),
|
||||
actionMap: actionMap, additionalData: additionalData,
|
||||
delegateObject: delegateObject)
|
||||
self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix), actionText: actionMap?.optionalStringForKey(KeyTitle), backText: actionMap?.optionalStringForKey(KeyTitlePostfix), actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
public convenience init(frontText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
self.init(frontText: frontText,
|
||||
actionText: actionMap?.optionalStringForKey(KeyTitle),
|
||||
backText: backText,
|
||||
actionMap: actionMap,
|
||||
additionalData: additionalData,
|
||||
delegateObject: delegateObject)
|
||||
self.init(frontText: frontText, actionText: actionMap?.optionalStringForKey(KeyTitle), backText: backText, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
public init(frontText: String?, actionText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
super.init(frame: CGRect.zero)
|
||||
super.init(frame: .zero)
|
||||
|
||||
setFrontText(frontText, actionText: actionText, actionMap: actionMap, backText: backText, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
// 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]?, delegateObject: DelegateObject?) {
|
||||
self.init(text: fullText,
|
||||
startTag: "{",
|
||||
endTag: "}",
|
||||
actionMap: actionMap,
|
||||
additionalData: additionalData,
|
||||
delegateObject: delegateObject)
|
||||
self.init(text: fullText, startTag: "{", endTag: "}", actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
public init(text fullText: String?, startTag: String?, endTag: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
super.init(frame: CGRect.zero)
|
||||
super.init(frame: .zero)
|
||||
|
||||
setText(fullText, startTag: startTag, endTag: endTag)
|
||||
|
||||
actionBlock = {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Configuration
|
||||
//------------------------------------------------------
|
||||
|
||||
private func setup() {
|
||||
/// Creates the Label that will be interacted with.
|
||||
private func createLabel() {
|
||||
|
||||
if self.label == nil {
|
||||
let label = Label(frame: CGRect.zero)
|
||||
|
||||
backgroundColor = .clear
|
||||
label.isUserInteractionEnabled = false
|
||||
let label = Label(frame: .zero)
|
||||
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]))
|
||||
NSLayoutConstraint.constraintPinSubview(label, pinTop: true, pinBottom: true, pinLeft: true, pinRight: true)
|
||||
|
||||
self.label = label
|
||||
label.sizeToFit()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up label. Best to call sometime after setting the text.
|
||||
private func setLabelAttributes() {
|
||||
|
||||
// Adding the underline
|
||||
setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)])
|
||||
|
||||
self.label?.attributedText = attributedText
|
||||
self.label?.accessibilityTraits = .button
|
||||
label?.attributedText = attributedText
|
||||
label?.accessibilityTraits = actionText?.isEmpty ?? true ? .staticText : .button
|
||||
}
|
||||
|
||||
|
||||
@objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
|
||||
weak var weakSelf: LabelWithInternalButton? = self
|
||||
weak var weakButtonDelegate: ButtonDelegateProtocol? = (delegateObject as? MVMCoreUIDelegateObject)?.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 performAction {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
@ -220,12 +208,13 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
|
||||
@objc public func setFrontText(_ frontText: String?, actionText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
|
||||
createLabel()
|
||||
self.frontText = frontText
|
||||
setActionMap(actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
self.actionText = actionText
|
||||
self.backText = backText
|
||||
text = getTextFromStringComponents()
|
||||
setup()
|
||||
setActionMap(actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
@objc public func setFrontText(_ frontText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
@ -244,10 +233,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
}
|
||||
|
||||
if let b2Font = MFStyler.fontB2(),
|
||||
let actions = actionMap,
|
||||
actions.keys.count > 0,
|
||||
let actionString = actions.optionalStringForKey(KeyTitle),
|
||||
!actionString.isEmpty {
|
||||
let actions = actionMap, actions.keys.count > 0,
|
||||
let actionString = actions.optionalStringForKey(KeyTitle), !actionString.isEmpty {
|
||||
|
||||
let actionStringOnLine = actionString + (addNewLine ? "\n" : " ")
|
||||
actionText = actionStringOnLine
|
||||
@ -264,7 +251,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
}
|
||||
|
||||
attributedText = mutableAttributedString
|
||||
// Added this line for underlining
|
||||
setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)])
|
||||
}
|
||||
|
||||
@ -276,51 +262,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
label?.attributedText = attributedText
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - UIControl Override
|
||||
//------------------------------------------------------
|
||||
|
||||
override open func touchesEnded(_ touches: Set<UITouch>, 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<UITouch>?) -> Bool {
|
||||
|
||||
if UIAccessibility.isVoiceOverRunning || makeWholeViewClickable {
|
||||
return true
|
||||
}
|
||||
|
||||
let location: CGPoint? = touches?.first?.location(in: label)
|
||||
let actionString = actionText
|
||||
let index: Int = actionRange.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
|
||||
//------------------------------------------------------
|
||||
@ -338,40 +279,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
return "\(frontText ?? "")\(actionText ?? "")\(backText ?? "")"
|
||||
}
|
||||
|
||||
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: "}")
|
||||
@ -379,47 +286,24 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
|
||||
private func setText(_ text: String?, startTag: String?, endTag: String?) {
|
||||
|
||||
let actionableTuple: ActionableStringTuple = rangeOfText(text, startTag: startTag, endTag: endTag)
|
||||
createLabel()
|
||||
frontText = text
|
||||
|
||||
if let text = text,
|
||||
let leftTag = startTag,
|
||||
text.contains(leftTag),
|
||||
let rightTag = endTag,
|
||||
text.contains(rightTag),
|
||||
let front = actionableTuple.front,
|
||||
let middle = actionableTuple.action,
|
||||
let end = actionableTuple.end {
|
||||
|
||||
frontText = front.trimmingCharacters(in: .whitespaces)
|
||||
actionText = middle.trimmingCharacters(in: .whitespaces)
|
||||
backText = end.trimmingCharacters(in: .whitespaces)
|
||||
} else {
|
||||
frontText = text
|
||||
}
|
||||
|
||||
self.text = getTextFromStringComponents()
|
||||
setup()
|
||||
}
|
||||
|
||||
private func rangeOfText(_ text: String?, startTag: String?, endTag: String?) -> ActionableStringTuple {
|
||||
|
||||
var actionableTuple: ActionableStringTuple = (front: nil, action: nil, end: nil)
|
||||
|
||||
guard let text = text else { return actionableTuple }
|
||||
|
||||
if let leftTag = startTag, text.contains(leftTag) {
|
||||
|
||||
let firstHalf = text.components(separatedBy: leftTag)
|
||||
actionableTuple.front = firstHalf.first
|
||||
|
||||
if let rightTag = endTag, text.contains(rightTag) {
|
||||
let secondHalf = firstHalf[1].components(separatedBy: rightTag)
|
||||
actionableTuple.action = secondHalf[0]
|
||||
actionableTuple.end = secondHalf[1]
|
||||
if let text = text {
|
||||
var initialSegments = [String]()
|
||||
if let leftTag = startTag, text.contains(leftTag) {
|
||||
initialSegments = text.components(separatedBy: leftTag)
|
||||
frontText = initialSegments[0].trimmingCharacters(in: .whitespaces)
|
||||
|
||||
if let rightTag = endTag, text.contains(rightTag) {
|
||||
let secondPart = initialSegments[1].components(separatedBy: rightTag)
|
||||
actionText = secondPart[0].trimmingCharacters(in: .whitespaces)
|
||||
backText = secondPart[1].trimmingCharacters(in: .whitespaces)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return actionableTuple
|
||||
self.text = getTextFromStringComponents()
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
// Reset the text and action map
|
||||
@ -459,49 +343,34 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
|
||||
public func setAlternateNormalTextAttributes(_ attributes: [AnyHashable: Any]?) {
|
||||
|
||||
guard let _attributedText = attributedText, let _attributes = attributes as? [NSAttributedString.Key: Any] else { return }
|
||||
let attributedString = NSMutableAttributedString(attributedString: _attributedText)
|
||||
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())
|
||||
attributedString.addAttributes(attributes, range: frontRange)
|
||||
attributedString.addAttributes(attributes, range: backRange)
|
||||
}
|
||||
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)
|
||||
self.attributedText = attributedString
|
||||
}
|
||||
|
||||
@objc public func setActionTextString(_ actionText: String?) {
|
||||
|
||||
createLabel()
|
||||
self.actionText = actionText
|
||||
text = getTextFromStringComponents()
|
||||
setup()
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
/// Used to just reset the texts and actions if already initialized
|
||||
@objc public func reset(withActionMap actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
|
||||
|
||||
createLabel()
|
||||
frontText = actionMap?.optionalStringForKey(KeyTitlePrefix)
|
||||
actionText = actionMap?.optionalStringForKey(KeyTitle)
|
||||
backText = actionMap?.optionalStringForKey(KeyTitlePostfix)
|
||||
|
||||
actionBlock = {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
text = getTextFromStringComponents()
|
||||
setup()
|
||||
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
@ -539,20 +408,18 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
|
||||
@available(*, deprecated)
|
||||
public init(frontText: String?, actionText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) {
|
||||
super.init(frame: CGRect.zero)
|
||||
super.init(frame: .zero)
|
||||
|
||||
setFrontText(frontText, actionText: actionText, actionMap: actionMap, backText: backText, additionalData: additionalData, delegate: delegate, buttonDelegate: buttonDelegate)
|
||||
}
|
||||
|
||||
@available(*, deprecated)
|
||||
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)
|
||||
}
|
||||
|
||||
@available(*, deprecated)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -583,29 +450,18 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
|
||||
setText(fullText, startTag: startTag, endTag: endTag)
|
||||
|
||||
weak var weakDelegate: ActionObjectDelegate? = delegate
|
||||
|
||||
actionBlock = {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: weakDelegate as? CoreObjectActionLoadPresentDelegate)
|
||||
actionBlock = { [weak delegate] in
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated)
|
||||
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 = { [weak self, weak delegate, weak buttonDelegate] in
|
||||
|
||||
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 performAction {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: weakDelegate as? CoreObjectActionLoadPresentDelegate)
|
||||
if let wSelf = self, buttonDelegate?.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,12 +474,13 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
@available(*, deprecated)
|
||||
@objc public func setFrontText(_ frontText: String?, actionText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) {
|
||||
|
||||
createLabel()
|
||||
self.frontText = frontText
|
||||
setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate)
|
||||
self.actionText = actionText
|
||||
self.backText = backText
|
||||
text = getTextFromStringComponents()
|
||||
setup()
|
||||
setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate)
|
||||
setLabelAttributes()
|
||||
}
|
||||
|
||||
@available(*, deprecated)
|
||||
@ -644,10 +501,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
}
|
||||
|
||||
if let b2Font = MFStyler.fontB2(),
|
||||
let actions = actionMap,
|
||||
actions.keys.count > 0,
|
||||
let actionString = actions.optionalStringForKey(KeyTitle),
|
||||
!actionString.isEmpty {
|
||||
let actions = actionMap, actions.keys.count > 0,
|
||||
let actionString = actions.optionalStringForKey(KeyTitle), !actionString.isEmpty {
|
||||
|
||||
let actionStringOnLine = actionString + (addNewLine ? "\n" : " ")
|
||||
actionText = actionStringOnLine
|
||||
@ -664,7 +519,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
}
|
||||
|
||||
attributedText = mutableAttributedString
|
||||
// Added this line for underlining
|
||||
setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)])
|
||||
}
|
||||
|
||||
@ -712,23 +566,22 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
@available(*, deprecated)
|
||||
@objc public func reset(withActionMap actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?) {
|
||||
|
||||
createLabel()
|
||||
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()
|
||||
|
||||
actionBlock = { [weak delegate] in
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate)
|
||||
}
|
||||
setLabelAttributes()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Atomization
|
||||
extension LabelWithInternalButton: MVMCoreUIMoleculeViewProtocol {
|
||||
//------------------------------------------------------
|
||||
// MARK: - Atomization
|
||||
//------------------------------------------------------
|
||||
|
||||
// Default values for view.
|
||||
@objc open func setAsMolecule() {
|
||||
|
||||
|
||||
183
MVMCoreUI/Atoms/Views/LeftRightLabelView.swift
Normal file
183
MVMCoreUI/Atoms/Views/LeftRightLabelView.swift
Normal file
@ -0,0 +1,183 @@
|
||||
//
|
||||
// LeftRightLabelView.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Christiano, Kevin on 5/20/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@objcMembers open class LeftRightLabelView: ViewConstrainingView {
|
||||
//------------------------------------------------------
|
||||
// MARK: - Outlets
|
||||
//------------------------------------------------------
|
||||
|
||||
let leftTextLabel = Label.commonLabelB1(true)
|
||||
let rightTextLabel = Label.commonLabelB1(true)
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//------------------------------------------------------
|
||||
|
||||
var rightTextLabelLeading: NSLayoutConstraint?
|
||||
var leftTextLabelTrailing: NSLayoutConstraint?
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Initialization
|
||||
//------------------------------------------------------
|
||||
|
||||
public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
public convenience init(json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.init()
|
||||
setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
override open func setupView() {
|
||||
super.setupView()
|
||||
|
||||
guard subviews.isEmpty else { return }
|
||||
|
||||
addSubview(leftTextLabel)
|
||||
addSubview(rightTextLabel)
|
||||
|
||||
leftTextLabel.textAlignment = .left
|
||||
rightTextLabel.textAlignment = .right
|
||||
|
||||
constrainBothLabels()
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - View Lifecycle
|
||||
//------------------------------------------------------
|
||||
|
||||
override open func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
|
||||
leftTextLabel.updateView(size)
|
||||
rightTextLabel.updateView(size)
|
||||
|
||||
// Resolves text layout issues found between both dynamically sized labels, number is not exact but performs as required.
|
||||
if leftTextLabel.hasText && rightTextLabel.hasText {
|
||||
let padding = MFStyler.defaultHorizontalPadding(forSize: size) * 2
|
||||
let maximumTextWidth = (size - (padding + 16)) * 0.4
|
||||
// Subtracting 10 resolves issues of SE and iPad
|
||||
rightTextLabel.preferredMaxLayoutWidth = round(maximumTextWidth) - 10
|
||||
} else {
|
||||
rightTextLabel.preferredMaxLayoutWidth = 0
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Setup
|
||||
//------------------------------------------------------
|
||||
|
||||
private func constrainBothLabels() {
|
||||
|
||||
leftTextLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
leftTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
|
||||
let leftTextBottom = leftTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
leftTextBottom.priority = UILayoutPriority(249)
|
||||
leftTextBottom.isActive = true
|
||||
|
||||
bottomAnchor.constraint(greaterThanOrEqualTo: leftTextLabel.bottomAnchor).isActive = true
|
||||
|
||||
rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor, constant: 16)
|
||||
rightTextLabelLeading?.isActive = true
|
||||
|
||||
rightTextLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
let rightLayout = layoutMarginsGuide.trailingAnchor.constraint(equalTo: rightTextLabel.trailingAnchor)
|
||||
rightLayout.priority = UILayoutPriority(rawValue: 995)
|
||||
rightLayout.isActive = true
|
||||
|
||||
let rightTextBottom = rightTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
rightTextBottom.priority = UILayoutPriority(rawValue: 249)
|
||||
rightTextBottom.isActive = true
|
||||
|
||||
bottomAnchor.constraint(greaterThanOrEqualTo: rightTextLabel.bottomAnchor).isActive = true
|
||||
|
||||
let leftTextWidth = leftTextLabel.widthAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.6)
|
||||
leftTextWidth.priority = UILayoutPriority(rawValue: 995)
|
||||
leftTextWidth.isActive = true
|
||||
|
||||
let rightTextWidth = rightTextLabel.widthAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.4)
|
||||
rightTextWidth.priority = UILayoutPriority(rawValue: 906)
|
||||
rightTextWidth.isActive = true
|
||||
|
||||
leftTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 901), for: .horizontal)
|
||||
rightTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 902), for: .horizontal)
|
||||
|
||||
leftTextLabel.setContentHuggingPriority(.required, for: .vertical)
|
||||
rightTextLabel.setContentHuggingPriority(.required, for: .vertical)
|
||||
|
||||
leftTextLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
rightTextLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
rightTextLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 902), for: .horizontal)
|
||||
}
|
||||
|
||||
private func constrainLeftLabel() {
|
||||
|
||||
deactivateMiddleConstraint()
|
||||
leftTextLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor)
|
||||
leftTextLabelTrailing?.isActive = true
|
||||
}
|
||||
|
||||
private func constrainRightLabel() {
|
||||
|
||||
deactivateMiddleConstraint()
|
||||
rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
|
||||
rightTextLabelLeading?.isActive = true
|
||||
}
|
||||
|
||||
override open func reset() {
|
||||
super.reset()
|
||||
|
||||
deactivateMiddleConstraint()
|
||||
constrainBothLabels()
|
||||
leftTextLabel.text = ""
|
||||
rightTextLabel.text = ""
|
||||
backgroundColor = nil
|
||||
}
|
||||
|
||||
private func deactivateMiddleConstraint() {
|
||||
|
||||
leftTextLabelTrailing?.isActive = false
|
||||
rightTextLabelLeading?.isActive = false
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Atomization
|
||||
//------------------------------------------------------
|
||||
|
||||
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData)
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
leftTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData)
|
||||
rightTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData)
|
||||
|
||||
if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String {
|
||||
backgroundColor = UIColor.mfGet(forHex: backgroundColorHex)
|
||||
}
|
||||
|
||||
if !leftTextLabel.hasText {
|
||||
constrainRightLabel()
|
||||
} else if !rightTextLabel.hasText {
|
||||
constrainLeftLabel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -277,6 +277,11 @@
|
||||
[super setupView];
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.directionalLayoutMargins = NSDirectionalEdgeInsetsZero;
|
||||
} else {
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateView:(CGFloat)size {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class ProgressBarView: ViewConstrainingView {
|
||||
@objcMembers public class ProgressBarWithLabel: ViewConstrainingView {
|
||||
|
||||
var progress = ProgressBar()
|
||||
var topleftlabel = Label()
|
||||
@ -22,7 +22,8 @@ import UIKit
|
||||
|
||||
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
progress.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
let progressbarjson = json?.optionalDictionaryForKey("progressbar")
|
||||
progress.setWithJSON(progressbarjson, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
let topleftlabeljson = json?.optionalDictionaryForKey("label")
|
||||
let toprightlabeljson = json?.optionalDictionaryForKey("toprightlabel")
|
||||
@ -44,8 +44,8 @@ import UIKit
|
||||
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
|
||||
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
|
||||
}
|
||||
let primaryButtonMap = json?.optionalDictionaryForKey("primaryButton")
|
||||
let secondaryButtonMap = json?.optionalDictionaryForKey("secondaryButton")
|
||||
let primaryButtonMap = json?.optionalDictionaryForKey(KeyPrimaryButton)
|
||||
let secondaryButtonMap = json?.optionalDictionaryForKey(KeySecondaryButton)
|
||||
set(primaryButtonJSON: primaryButtonMap, secondaryButtonJSON: secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
@"caretButton": CaretButton.class,
|
||||
@"textField" : MFTextField.class,
|
||||
@"checkbox" : MVMCoreUICheckBox.class,
|
||||
@"progressBarView" : ProgressBarView.class,
|
||||
@"progressBarWithLabel" : ProgressBarWithLabel.class,
|
||||
@"progressBar": ProgressBar.class,
|
||||
@"textField": MFTextField.class,
|
||||
@"checkbox": MVMCoreUICheckBox.class,
|
||||
@ -44,6 +44,7 @@
|
||||
@"switchLineItem": SwitchLineItem.class,
|
||||
@"switch": Switch.class,
|
||||
@"image": MFLoadImageView.class
|
||||
@"leftRightLabelView": LeftRightLabelView.class
|
||||
} mutableCopy];
|
||||
});
|
||||
return mapping;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user