refactored label for first cut, pretty big changes though....
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
29f43b7861
commit
ddb15e72f3
@ -8,15 +8,18 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import MVMCore
|
import MVMCore
|
||||||
|
import VDS
|
||||||
|
|
||||||
public typealias ActionBlock = () -> ()
|
public typealias ActionBlock = () -> ()
|
||||||
|
|
||||||
|
|
||||||
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ViewMaskingProtocol {
|
@objcMembers open class Label: VDS.Label, VDSMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ViewMaskingProtocol {
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
|
open var viewModel: LabelModel!
|
||||||
|
open var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
open var additionalData: [AnyHashable : Any]?
|
||||||
|
|
||||||
public var makeWholeViewClickable = false
|
public var makeWholeViewClickable = false
|
||||||
|
|
||||||
@ -30,52 +33,15 @@ public typealias ActionBlock = () -> ()
|
|||||||
/// A specific text index to use as a unique marker.
|
/// A specific text index to use as a unique marker.
|
||||||
public var hero: Int?
|
public var hero: Int?
|
||||||
|
|
||||||
// Used for scaling the font in updateView.
|
|
||||||
private var originalAttributedString: NSAttributedString?
|
|
||||||
|
|
||||||
public var hasText: Bool {
|
|
||||||
guard let text = text, let attributedText = attributedText else { return false }
|
|
||||||
return !text.isEmpty || !attributedText.string.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
public var getRange: NSRange {
|
public var getRange: NSRange {
|
||||||
NSRange(location: 0, length: text?.count ?? 0)
|
NSRange(location: 0, length: text?.count ?? 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var shouldMaskWhileRecording: Bool = false
|
public var shouldMaskWhileRecording: Bool = false
|
||||||
|
|
||||||
public var model: MoleculeModelProtocol?
|
public var hasText: Bool {
|
||||||
//------------------------------------------------------
|
guard let text = text, let attributedText = attributedText else { return false }
|
||||||
// MARK: - Multi-Action Text
|
return !text.isEmpty || !attributedText.string.isEmpty
|
||||||
//------------------------------------------------------
|
|
||||||
|
|
||||||
/// Data store of the tappable ranges of the text.
|
|
||||||
public var clauses: [ActionableClause] = [] {
|
|
||||||
didSet {
|
|
||||||
isUserInteractionEnabled = !clauses.isEmpty
|
|
||||||
if clauses.count > 1 {
|
|
||||||
clauses.sort { first, second in
|
|
||||||
return first.range.location < second.range.location
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used for tappable links in the text.
|
|
||||||
public struct ActionableClause {
|
|
||||||
public var range: NSRange
|
|
||||||
public var actionBlock: ActionBlock
|
|
||||||
public var accessibilityID: Int = 0
|
|
||||||
|
|
||||||
public func performAction() {
|
|
||||||
actionBlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(range: NSRange, actionBlock: @escaping ActionBlock, accessibilityID: Int = 0) {
|
|
||||||
self.range = range
|
|
||||||
self.actionBlock = actionBlock
|
|
||||||
self.accessibilityID = accessibilityID
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
@ -84,54 +50,32 @@ public typealias ActionBlock = () -> ()
|
|||||||
|
|
||||||
/// Sets the clauses array to empty.
|
/// Sets the clauses array to empty.
|
||||||
@objc public func setEmptyClauses() {
|
@objc public func setEmptyClauses() {
|
||||||
clauses = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
|
|
||||||
@objc public func setupView() {
|
@objc public required init() {
|
||||||
backgroundColor = .clear
|
super.init()
|
||||||
numberOfLines = 0
|
|
||||||
lineBreakMode = .byWordWrapping
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
clauses = []
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
accessibilityTraits = .staticText
|
|
||||||
|
|
||||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped))
|
|
||||||
tapGesture.numberOfTapsRequired = 1
|
|
||||||
addGestureRecognizer(tapGesture)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public init() {
|
|
||||||
super.init(frame: .zero)
|
|
||||||
setupView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc required public init?(coder: NSCoder) {
|
@objc required public init?(coder: NSCoder) {
|
||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
setupView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc override public init(frame: CGRect) {
|
@objc override public init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
setupView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(fontStyle: Styler.Font, _ scale: Bool = true) {
|
public init(fontStyle: Styler.Font, _ scale: Bool = true) {
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
setupView()
|
guard let style = fontStyle.vdsTextStyle() else { return }
|
||||||
|
textStyle = style
|
||||||
font = fontStyle.getFont(false)
|
|
||||||
textColor = fontStyle.color()
|
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc convenience public init(standardFontSize size: CGFloat) {
|
@objc convenience public init(standardFontSize size: CGFloat) {
|
||||||
self.init()
|
self.init()
|
||||||
standardFontSize = size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience to init Label with a link comprised of range, actionMap and delegateObject
|
/// Convenience to init Label with a link comprised of range, actionMap and delegateObject
|
||||||
@ -145,7 +89,6 @@ public typealias ActionBlock = () -> ()
|
|||||||
|
|
||||||
required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
setupView()
|
|
||||||
styleB2(true)
|
styleB2(true)
|
||||||
set(with: model, delegateObject, additionalData)
|
set(with: model, delegateObject, additionalData)
|
||||||
}
|
}
|
||||||
@ -236,12 +179,6 @@ public typealias ActionBlock = () -> ()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LabelAlignment: String {
|
|
||||||
case center
|
|
||||||
case right
|
|
||||||
case left
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public func resetAttributeStyle() {
|
@objc public func resetAttributeStyle() {
|
||||||
/*
|
/*
|
||||||
* This is to address a reuse issue with iOS 13 and up.
|
* This is to address a reuse issue with iOS 13 and up.
|
||||||
@ -250,294 +187,104 @@ public typealias ActionBlock = () -> ()
|
|||||||
* appropriately called.
|
* appropriately called.
|
||||||
* Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml
|
* Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml
|
||||||
*/
|
*/
|
||||||
if let attributedText = attributedText, let text = text, !text.isEmpty {
|
if let text = text, !text.isEmpty {
|
||||||
let attributedString = NSMutableAttributedString(string: text)
|
|
||||||
let range = NSRange(location: 0, length: text.count)
|
//create the primary string
|
||||||
for attribute in attributedText.attributes(at: 0, effectiveRange: nil) {
|
let mutableText = NSMutableAttributedString.mutableText(for: text,
|
||||||
if attribute.key == .underlineStyle {
|
textStyle: textStyle,
|
||||||
attributedString.addAttribute(.underlineStyle, value: 0, range: range)
|
useScaledFont: useScaledFont,
|
||||||
}
|
textColor: textColorConfiguration.getColor(self),
|
||||||
if attribute.key == .strikethroughStyle {
|
alignment: textAlignment,
|
||||||
attributedString.addAttribute(.strikethroughStyle, value: 0, range: range)
|
lineBreakMode: lineBreakMode)
|
||||||
}
|
|
||||||
|
if let attributes = attributes {
|
||||||
|
mutableText.apply(attributes: attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.attributedText = attributedString
|
self.attributedText = mutableText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject? = nil, _ additionalData: [AnyHashable: Any]? = nil) {
|
public func viewModelDidUpdate() {
|
||||||
|
shouldMaskWhileRecording = viewModel.shouldMaskRecordedView ?? false
|
||||||
|
text = viewModel.text
|
||||||
|
hero = viewModel.hero
|
||||||
|
Label.setLabel(self, withHTML: viewModel.html)
|
||||||
|
textAlignment = viewModel.textAlignment ?? .left
|
||||||
|
surface = viewModel.surface
|
||||||
|
|
||||||
clauses = []
|
makeWholeViewClickable = viewModel.makeWholeViewClickable ?? false
|
||||||
text = nil
|
if let backgroundColor = viewModel.backgroundColor {
|
||||||
attributedText = nil
|
|
||||||
originalAttributedString = nil
|
|
||||||
shouldMaskWhileRecording = model.shouldMaskRecordedView ?? false
|
|
||||||
|
|
||||||
guard let labelModel = model as? LabelModel else { return }
|
|
||||||
|
|
||||||
text = labelModel.text
|
|
||||||
if let accessibilityTraits = labelModel.accessibilityTraits {
|
|
||||||
self.accessibilityTraits = accessibilityTraits
|
|
||||||
}
|
|
||||||
|
|
||||||
resetAttributeStyle()
|
|
||||||
|
|
||||||
hero = labelModel.hero
|
|
||||||
Label.setLabel(self, withHTML: labelModel.html)
|
|
||||||
isAccessibilityElement = hasText
|
|
||||||
|
|
||||||
switch labelModel.textAlignment {
|
|
||||||
case .center:
|
|
||||||
textAlignment = .center
|
|
||||||
|
|
||||||
case .right:
|
|
||||||
textAlignment = .right
|
|
||||||
|
|
||||||
default:
|
|
||||||
textAlignment = .left
|
|
||||||
}
|
|
||||||
|
|
||||||
makeWholeViewClickable = labelModel.makeWholeViewClickable ?? false
|
|
||||||
if let backgroundColor = labelModel.backgroundColor {
|
|
||||||
self.backgroundColor = backgroundColor.uiColor
|
self.backgroundColor = backgroundColor.uiColor
|
||||||
}
|
}
|
||||||
|
|
||||||
if let accessibilityText = labelModel.accessibilityText {
|
if let style = viewModel.fontStyle?.vdsTextStyle() {
|
||||||
accessibilityLabel = accessibilityText
|
font = style.font
|
||||||
}
|
textStyle = style
|
||||||
|
} else if let fontName = viewModel.fontName {
|
||||||
if let fontStyle = labelModel.fontStyle {
|
// there is a TextStyle.defaultStyle
|
||||||
fontStyle.styleLabel(self, genericScaling: false)
|
let fontSize = viewModel.fontSize
|
||||||
standardFontSize = font.pointSize
|
if let fontSize {
|
||||||
} else {
|
|
||||||
let fontSize = labelModel.fontSize
|
|
||||||
if let fontSize = fontSize {
|
|
||||||
standardFontSize = fontSize
|
standardFontSize = fontSize
|
||||||
}
|
}
|
||||||
if let fontName = labelModel.fontName {
|
if let customStyle = style(for: fontName, pointSize: fontSize ?? standardFontSize), customStyle != textStyle {
|
||||||
font = MFFonts.mfFont(withName: fontName, size: fontSize ?? standardFontSize)
|
font = customStyle.font
|
||||||
} else if let fontSize = fontSize {
|
textStyle = customStyle
|
||||||
font = font.updateSize(fontSize)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let color = labelModel.textColor {
|
if let color = viewModel.textColor {
|
||||||
textColor = color.uiColor
|
textColorConfiguration = SurfaceColorConfiguration(color.uiColor, color.uiColor).eraseToAnyColorable()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let lines = labelModel.numberOfLines {
|
if let lines = viewModel.numberOfLines {
|
||||||
numberOfLines = lines
|
numberOfLines = lines
|
||||||
}
|
}
|
||||||
|
|
||||||
if let attributes = labelModel.attributes, let labelText = text {
|
if let attributeModels = viewModel.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) {
|
||||||
let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.updateSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor])
|
attributes = attributeModels
|
||||||
|
|
||||||
for attribute in attributes {
|
|
||||||
guard let range = validateAttribute(range: NSRange(location: attribute.location, length: attribute.length) , in: attributedString, type: attribute.type)
|
|
||||||
else { continue }
|
|
||||||
|
|
||||||
switch attribute {
|
|
||||||
case let underlineAtt as LabelAttributeUnderlineModel:
|
|
||||||
attributedString.addAttribute(.underlineStyle, value: underlineAtt.underlineValue.rawValue, range: range)
|
|
||||||
if let underlineColor = underlineAtt.color?.uiColor {
|
|
||||||
attributedString.addAttribute(.underlineColor, value: underlineColor, range: range)
|
|
||||||
}
|
|
||||||
|
|
||||||
case _ as LabelAttributeStrikeThroughModel:
|
|
||||||
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
|
|
||||||
attributedString.addAttribute(.baselineOffset, value: 0, range: range)
|
|
||||||
|
|
||||||
case let colorAtt as LabelAttributeColorModel:
|
|
||||||
if let colorHex = colorAtt.textColor {
|
|
||||||
attributedString.removeAttribute(.foregroundColor, range: range)
|
|
||||||
attributedString.addAttribute(.foregroundColor, value: colorHex.uiColor, range: range)
|
|
||||||
}
|
|
||||||
|
|
||||||
case let imageAtt as LabelAttributeImageModel:
|
|
||||||
var fontSize = font.pointSize
|
|
||||||
if let attributeSize = imageAtt.size {
|
|
||||||
fontSize = attributeSize
|
|
||||||
}
|
|
||||||
let imageName = imageAtt.name ?? "externalLink"
|
|
||||||
let imageAttachment: NSTextAttachment
|
|
||||||
|
|
||||||
if let url = imageAtt.URL {
|
|
||||||
imageAttachment = Label.getTextAttachmentFrom(url: url, dimension: fontSize, label: self)
|
|
||||||
} else {
|
|
||||||
imageAttachment = Label.getTextAttachmentImage(name: imageName, dimension: fontSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm that the intended image location is within range.
|
|
||||||
if 0...labelText.count ~= imageAtt.location {
|
|
||||||
let mutableString = NSMutableAttributedString()
|
|
||||||
mutableString.append(NSAttributedString(attachment: imageAttachment))
|
|
||||||
attributedString.insert(mutableString, at: imageAtt.location)
|
|
||||||
}
|
|
||||||
|
|
||||||
case let fontAtt as LabelAttributeFontModel:
|
|
||||||
if let fontStyle = fontAtt.style {
|
|
||||||
attributedString.removeAttribute(.font, range: range)
|
|
||||||
attributedString.removeAttribute(.foregroundColor, range: range)
|
|
||||||
attributedString.addAttribute(.font, value: fontStyle.getFont(), range: range)
|
|
||||||
attributedString.addAttribute(.foregroundColor, value: fontStyle.color(), range: range)
|
|
||||||
} else {
|
|
||||||
let fontSize = fontAtt.size
|
|
||||||
var font: UIFont?
|
|
||||||
|
|
||||||
if let fontName = fontAtt.name {
|
|
||||||
font = MFFonts.mfFont(withName: fontName, size: fontSize ?? self.font.pointSize)
|
|
||||||
} else if let fontSize = fontSize {
|
|
||||||
font = self.font.updateSize(fontSize)
|
|
||||||
}
|
|
||||||
if let font = font {
|
|
||||||
attributedString.removeAttribute(.font, range: range)
|
|
||||||
attributedString.addAttribute(.font, value: font, range: range)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let actionAtt as LabelAttributeActionModel:
|
|
||||||
addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) {
|
|
||||||
MVMCoreUIActionHandler.performActionUnstructured(with: actionAtt.action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject)
|
|
||||||
}
|
|
||||||
addActionAttributes(range: range, string: attributedString)
|
|
||||||
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attributedText = attributedString
|
|
||||||
originalAttributedString = attributedText
|
|
||||||
}
|
}
|
||||||
self.model = labelModel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See if the font that is currently set matches a VDS Font and if so grab the matching TextStyle or create custom TextStyle that
|
||||||
|
/// that the Label will use moving forward.
|
||||||
|
private func checkforFontChange() {
|
||||||
|
guard let customStyle = style(for: font.fontName, pointSize: font.pointSize), customStyle != textStyle
|
||||||
|
else { return }
|
||||||
|
textStyle = customStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
private func style(for fontName: String, pointSize: CGFloat) -> TextStyle? {
|
||||||
|
guard let vdsFont = Font.from(fontName: fontName),
|
||||||
|
let customStyle = TextStyle.style(from: vdsFont, pointSize: pointSize)
|
||||||
|
else { return nil }
|
||||||
|
return customStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func updateView() {
|
||||||
|
checkforFontChange()
|
||||||
|
super.updateView()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func updateAccessibility() {
|
||||||
|
super.updateAccessibility()
|
||||||
|
if let accessibilityTraits = viewModel?.accessibilityTraits {
|
||||||
|
self.accessibilityTraits = accessibilityTraits
|
||||||
|
}
|
||||||
|
|
||||||
|
if let accessibilityText = viewModel?.accessibilityText {
|
||||||
|
accessibilityLabel = accessibilityText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateView(_ size: CGFloat) { }
|
||||||
|
|
||||||
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||||
guard let label = label else { return }
|
guard let label = label as? Label,
|
||||||
|
let json = json as? [String: Any],
|
||||||
// Some properties can only be set on Label.
|
let labelModel = try? LabelModel.decode(jsonDict: json) else { return }
|
||||||
// Label fonts should not be scaled because it will be scaled in updateView.
|
label.set(with: labelModel, delegate as? MVMCoreUIDelegateObject, additionalData)
|
||||||
let mvmLabel = label as? Label
|
|
||||||
|
|
||||||
label.text = json?.optionalStringForKey(KeyText)
|
|
||||||
|
|
||||||
setLabel(label, withHTML: json?.optionalStringForKey("html"))
|
|
||||||
|
|
||||||
if let alignment = json?.optionalStringForKey("textAlignment") {
|
|
||||||
switch alignment {
|
|
||||||
case "center":
|
|
||||||
label.textAlignment = .center
|
|
||||||
case "right":
|
|
||||||
label.textAlignment = .right
|
|
||||||
default:
|
|
||||||
label.textAlignment = .left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mvmLabel?.makeWholeViewClickable = json?.boolForKey("makeWholeViewClickable") ?? false
|
|
||||||
|
|
||||||
if let backgroundColorHex = json?.optionalStringForKey(KeyBackgroundColor), !backgroundColorHex.isEmpty {
|
|
||||||
label.backgroundColor = UIColor.mfGet(forHex: backgroundColorHex)
|
|
||||||
}
|
|
||||||
|
|
||||||
label.accessibilityLabel = json?.optionalStringForKey("accessibilityText")
|
|
||||||
|
|
||||||
if let fontStyle = json?.optionalStringForKey("fontStyle") {
|
|
||||||
MFStyler.style(label: label, styleString: fontStyle, genericScaling: mvmLabel == nil)
|
|
||||||
mvmLabel?.standardFontSize = label.font.pointSize
|
|
||||||
} else {
|
|
||||||
let fontSize = json?["fontSize"] as? CGFloat
|
|
||||||
if let fontSize = fontSize {
|
|
||||||
mvmLabel?.standardFontSize = fontSize
|
|
||||||
}
|
|
||||||
|
|
||||||
if let fontName = json?.optionalStringForKey("fontName") {
|
|
||||||
label.font = MFFonts.mfFont(withName: fontName, size: fontSize ?? mvmLabel?.standardFontSize ?? label.font.pointSize)
|
|
||||||
} else if let fontSize = fontSize {
|
|
||||||
label.font = label.font.updateSize(fontSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let textColorHex = json?.optionalStringForKey(KeyTextColor), !textColorHex.isEmpty {
|
|
||||||
label.textColor = UIColor.mfGet(forHex: textColorHex)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let attributes = json?.optionalArrayForKey("attributes"), let labelText = label.text {
|
|
||||||
let attributedString = NSMutableAttributedString(string: labelText,
|
|
||||||
attributes: [NSAttributedString.Key.font: mvmLabel?.font.updateSize(mvmLabel!.standardFontSize) ?? 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,
|
|
||||||
let range = validateAttribute(range: NSRange(location: location, length: length), in: attributedString, type: attributeType)
|
|
||||||
else { continue }
|
|
||||||
|
|
||||||
switch attributeType {
|
|
||||||
case "underline":
|
|
||||||
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
|
|
||||||
|
|
||||||
case "strikethrough":
|
|
||||||
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
|
|
||||||
attributedString.addAttribute(.baselineOffset, value: 0, range: range)
|
|
||||||
|
|
||||||
case "color":
|
|
||||||
if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty {
|
|
||||||
attributedString.removeAttribute(.foregroundColor, range: range)
|
|
||||||
attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range)
|
|
||||||
}
|
|
||||||
case "image":
|
|
||||||
let fontSize = attribute["size"] as? CGFloat ?? label.font.pointSize
|
|
||||||
let imageName = attribute["name"] as? String ?? "externalLink"
|
|
||||||
let imageURL = attribute["URL"] as? String
|
|
||||||
let imageAttachment: NSTextAttachment
|
|
||||||
|
|
||||||
if let url = imageURL, let label = label as? Label {
|
|
||||||
imageAttachment = Label.getTextAttachmentFrom(url: url, dimension: fontSize, label: label)
|
|
||||||
} else {
|
|
||||||
imageAttachment = Label.getTextAttachmentImage(name: imageName, dimension: fontSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mutableString = NSMutableAttributedString()
|
|
||||||
mutableString.append(NSAttributedString(attachment: imageAttachment))
|
|
||||||
attributedString.insert(mutableString, at: location)
|
|
||||||
|
|
||||||
case "font":
|
|
||||||
if let fontStyle = attribute.optionalStringForKey("style") {
|
|
||||||
let styles = MFStyler.getAttributedString(for: "0", styleString: fontStyle, genericScaling: mvmLabel == nil)
|
|
||||||
attributedString.removeAttribute(.font, range: range)
|
|
||||||
attributedString.removeAttribute(.foregroundColor, range: range)
|
|
||||||
attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range)
|
|
||||||
} else {
|
|
||||||
let fontSize = attribute["size"] as? CGFloat
|
|
||||||
var font: UIFont?
|
|
||||||
|
|
||||||
if let fontName = attribute.optionalStringForKey("name") {
|
|
||||||
font = MFFonts.mfFont(withName: fontName, size: fontSize ?? mvmLabel?.standardFontSize ?? label.font.pointSize)
|
|
||||||
} else if let fontSize = fontSize {
|
|
||||||
font = label.font.updateSize(fontSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let font = font {
|
|
||||||
attributedString.removeAttribute(.font, range: range)
|
|
||||||
attributedString.addAttribute(.font, value: font, range: range)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "action":
|
|
||||||
guard let actionLabel = label as? Label else { continue }
|
|
||||||
|
|
||||||
actionLabel.addActionAttributes(range: range, string: attributedString)
|
|
||||||
if let actionBlock = actionLabel.createActionBlockFor(actionMap: attribute, additionalData: additionalData, delegateObject: delegate) {
|
|
||||||
actionLabel.appendActionableClause(range: range, actionBlock: actionBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label.attributedText = attributedString
|
|
||||||
mvmLabel?.originalAttributedString = attributedString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
@ -545,206 +292,53 @@ public typealias ActionBlock = () -> ()
|
|||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
|
|
||||||
public func setFontStyle(_ fontStyle: Styler.Font, _ scale: Bool = true) {
|
public func setFontStyle(_ fontStyle: Styler.Font, _ scale: Bool = true) {
|
||||||
fontStyle.styleLabel(self, genericScaling: false)
|
style(for: fontStyle)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
// MARK: - 2.0 Styling Methods
|
// MARK: - 2.0 Styling Methods
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
|
private func style(for legacy: Styler.Font) {
|
||||||
|
guard let style = legacy.vdsTextStyle() else { return }
|
||||||
|
textStyle = style
|
||||||
|
}
|
||||||
|
|
||||||
@objc public func styleH1(_ scale: Bool) {
|
@objc public func styleH1(_ scale: Bool) {
|
||||||
MFStyler.styleLabelH1(self, genericScaling: false)
|
style(for: .H1)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func styleH2(_ scale: Bool) {
|
@objc public func styleH2(_ scale: Bool) {
|
||||||
MFStyler.styleLabelH2(self, genericScaling: false)
|
style(for: .H2)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func styleH3(_ scale: Bool) {
|
@objc public func styleH3(_ scale: Bool) {
|
||||||
MFStyler.styleLabelH3(self, genericScaling: false)
|
style(for: .H3)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func styleH32(_ scale: Bool) {
|
@objc public func styleH32(_ scale: Bool) {
|
||||||
MFStyler.styleLabelH32(self, genericScaling: false)
|
style(for: .H32)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func styleB1(_ scale: Bool) {
|
@objc public func styleB1(_ scale: Bool) {
|
||||||
MFStyler.styleLabelB1(self, genericScaling: false)
|
style(for: .B1)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func styleB2(_ scale: Bool) {
|
@objc public func styleB2(_ scale: Bool) {
|
||||||
MFStyler.styleLabelB2(self, genericScaling: false)
|
style(for: .B2)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func styleB3(_ scale: Bool) {
|
@objc public func styleB3(_ scale: Bool) {
|
||||||
MFStyler.styleLabelB3(self, genericScaling: false)
|
style(for: .B3)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func styleB20(_ scale: Bool) {
|
@objc public func styleB20(_ scale: Bool) {
|
||||||
MFStyler.styleLabelB20(self, genericScaling: false)
|
style(for: .B20)
|
||||||
setScale(scale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Will remove the values contained in attributedText.
|
}
|
||||||
func clearAttributes() {
|
|
||||||
guard let labelText = text, !labelText.isEmpty else { return }
|
|
||||||
guard let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: labelText.count))
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
let attributedString = NSMutableAttributedString(string: labelText)
|
// Mark: - Old Helpers
|
||||||
|
extension Label {
|
||||||
for attribute in attributes {
|
|
||||||
attributedString.removeAttribute(attribute.key, range: NSRange(location: 0, length: labelText.count))
|
|
||||||
}
|
|
||||||
|
|
||||||
attributedText = attributedString
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@objc public func updateView(_ size: CGFloat) {
|
|
||||||
scaleSize = size as NSNumber
|
|
||||||
|
|
||||||
if let originalAttributedString = originalAttributedString {
|
|
||||||
let attributedString = NSMutableAttributedString(attributedString: originalAttributedString)
|
|
||||||
attributedString.removeAttribute(.font, range: NSRange(location: 0, length: attributedString.length))
|
|
||||||
|
|
||||||
// Loop the original attributed string, resize the fonts.
|
|
||||||
originalAttributedString.enumerateAttribute(.font, in: NSRange(location: 0, length: originalAttributedString.length), options: []) { value, range, stop in
|
|
||||||
|
|
||||||
if let fontObj = value as? UIFont, let stylerSize = MFStyler.sizeObjectGeneric(forCurrentDevice: fontObj.pointSize)?.getValueBased(onSize: size) {
|
|
||||||
attributedString.addAttribute(.font, value: fontObj.updateSize(stylerSize) as Any, range: range)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop the original attributed string, resize the image attachments.
|
|
||||||
originalAttributedString.enumerateAttribute(.attachment, in: NSRange(location: 0, length: originalAttributedString.length), options: []) { value, range, stop in
|
|
||||||
if let attachment = value as? NSTextAttachment,
|
|
||||||
let stylerSize = MFStyler.sizeObjectGeneric(forCurrentDevice: attachment.bounds.width)?.getValueBased(onSize: size) {
|
|
||||||
|
|
||||||
let dimension = round(stylerSize)
|
|
||||||
attachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attributedText = attributedString
|
|
||||||
} else if !MVMCoreGetterUtility.fequal(a: Float(standardFontSize), b: 0.0), let sizeObject = sizeObject ?? MFStyler.sizeObjectGeneric(forCurrentDevice: standardFontSize) {
|
|
||||||
font = font.updateSize(sizeObject.getValueBased(onSize: size))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public func setFont(_ font: UIFont, scale: Bool) {
|
|
||||||
self.font = font
|
|
||||||
setScale(scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public func setScale(_ scale: Bool) {
|
|
||||||
if scale {
|
|
||||||
standardFontSize = font.pointSize
|
|
||||||
if let floatScale = scaleSize?.floatValue {
|
|
||||||
updateView(CGFloat(floatScale))
|
|
||||||
} else {
|
|
||||||
updateView(MVMCoreUIUtility.getWidth())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
standardFontSize = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Appends an external link image to the end of the attributed string.
|
|
||||||
Will provide one whitespace to the left of the icon; adds 2 chars to the end of the string.
|
|
||||||
*/
|
|
||||||
@objc public func appendExternalLinkIcon() {
|
|
||||||
|
|
||||||
guard let attributedText = attributedText else { return }
|
|
||||||
|
|
||||||
let mutableString = NSMutableAttributedString(attributedString: attributedText)
|
|
||||||
|
|
||||||
mutableString.append(NSAttributedString(string: " "))
|
|
||||||
mutableString.append(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)))
|
|
||||||
self.attributedText = mutableString
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Insert external link icon anywhere within text of Label.
|
|
||||||
|
|
||||||
- Note: Each icon insertion adds 1 additional characters to the overall text length.
|
|
||||||
Therefore, you **MUST** insert icons and links in the order they would appear.
|
|
||||||
- parameter index: Location within the associated text to insert an external Link Icon
|
|
||||||
*/
|
|
||||||
public func insertExternalLinkIcon(at index: Int) {
|
|
||||||
|
|
||||||
guard let attributedText = attributedText, index <= attributedText.string.count && index >= 0 else { return }
|
|
||||||
|
|
||||||
let mutableString = NSMutableAttributedString(attributedString: attributedText)
|
|
||||||
mutableString.insert(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)), at: index)
|
|
||||||
|
|
||||||
self.attributedText = mutableString
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Retrieves an NSTextAttachment for NSAttributedString that is prepped to be inserted with the text.
|
|
||||||
|
|
||||||
- parameter name: The Asset name of the image. DEFAULT: "externalLink"
|
|
||||||
- parameter dimension: length of the height and width of the image. Will be 80% the passed magnitude.
|
|
||||||
*/
|
|
||||||
static func getTextAttachmentImage(name: String = "externalLink", dimension: CGFloat) -> NSTextAttachment {
|
|
||||||
|
|
||||||
let imageAttachment = NSTextAttachment()
|
|
||||||
imageAttachment.image = MVMCoreCache.shared()?.getImageFromRegisteredBundles(name)
|
|
||||||
imageAttachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension)
|
|
||||||
|
|
||||||
return imageAttachment
|
|
||||||
}
|
|
||||||
|
|
||||||
static func getTextAttachmentFrom(url: String, dimension: CGFloat, label: Label) -> NSTextAttachment {
|
|
||||||
|
|
||||||
let imageAttachment = NSTextAttachment()
|
|
||||||
imageAttachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension)
|
|
||||||
|
|
||||||
DispatchQueue.global(qos: .default).async {
|
|
||||||
MVMCoreCache.shared()?.getImage(url, useWidth: false, widthForS7: 0, useHeight: false, heightForS7: 0, localFallbackImageName: nil) { image, data, _ in
|
|
||||||
|
|
||||||
DispatchQueue.main.sync {
|
|
||||||
imageAttachment.image = image
|
|
||||||
label.setNeedsDisplay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return imageAttachment
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call to detect in the attributedText contains an NSTextAttachment.
|
|
||||||
func containsTextAttachment() -> Bool {
|
|
||||||
|
|
||||||
guard let attributedText = attributedText else { return false }
|
|
||||||
|
|
||||||
var containsAttachment = false
|
|
||||||
|
|
||||||
attributedText.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText.length), options: []) { value, range, stop in
|
|
||||||
if value is NSTextAttachment {
|
|
||||||
containsAttachment = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return containsAttachment
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendActionableClause(range: NSRange, actionBlock: @escaping ActionBlock) {
|
|
||||||
|
|
||||||
accessibilityTraits = .button
|
|
||||||
let accessibleAction = customAccessibilityAction(range: range)
|
|
||||||
clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1))
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
|
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
|
||||||
|
|
||||||
@ -789,25 +383,11 @@ public typealias ActionBlock = () -> ()
|
|||||||
|
|
||||||
return (textContainer, layoutManager, textStorage)
|
return (textContainer, layoutManager, textStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Atomization
|
// MARK: - Atomization
|
||||||
extension Label {
|
extension Label {
|
||||||
|
|
||||||
public func reset() {
|
|
||||||
text = nil
|
|
||||||
attributedText = nil
|
|
||||||
hero = nil
|
|
||||||
textAlignment = .left
|
|
||||||
originalAttributedString = nil
|
|
||||||
styleB2(true)
|
|
||||||
accessibilityCustomActions = []
|
|
||||||
clauses = []
|
|
||||||
accessibilityTraits = .staticText
|
|
||||||
numberOfLines = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
public func needsToBeConstrained() -> Bool { true }
|
public func needsToBeConstrained() -> Bool { true }
|
||||||
|
|
||||||
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
|
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
|
||||||
@ -818,18 +398,15 @@ extension Label {
|
|||||||
// MARK: - Multi-Link Functionality
|
// MARK: - Multi-Link Functionality
|
||||||
extension Label {
|
extension Label {
|
||||||
|
|
||||||
/// Applied to existing text. Removes underlines of tappable links and assoated actionable clauses.
|
/// Underlines the tappable region and stores the tap logic for interation.
|
||||||
@objc public func clearActionableClauses() {
|
private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) {
|
||||||
guard let attributedText = attributedText else { return }
|
var textLink = ActionLabelAttribute(location: range.location, length: range.length)
|
||||||
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
|
textLink.subscriber = textLink
|
||||||
|
.action
|
||||||
clauses.forEach { clause in
|
.sink { _ in
|
||||||
mutableAttributedString.removeAttribute(NSAttributedString.Key.underlineStyle, range: clause.range)
|
actionBlock()
|
||||||
}
|
}
|
||||||
|
attributes?.append(textLink)
|
||||||
self.attributedText = mutableAttributedString
|
|
||||||
accessibilityElements = []
|
|
||||||
clauses = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func createActionBlockFor(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? {
|
public func createActionBlockFor(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? {
|
||||||
@ -842,24 +419,6 @@ extension Label {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addActionAttributes(range: NSRange, string: NSMutableAttributedString?) {
|
|
||||||
|
|
||||||
guard let string = string,
|
|
||||||
let range = validateAttribute(range: range, in: string)
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
string.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func setActionAttributes(range: NSRange) {
|
|
||||||
|
|
||||||
guard let attributedText = attributedText else { return }
|
|
||||||
|
|
||||||
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
|
|
||||||
addActionAttributes(range: range, string: mutableAttributedString)
|
|
||||||
self.attributedText = mutableAttributedString
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Provides an actionable range of text.
|
Provides an actionable range of text.
|
||||||
|
|
||||||
@ -898,113 +457,6 @@ extension Label {
|
|||||||
|
|
||||||
setTextLinkState(range: getRange, actionBlock: actionBlock)
|
setTextLinkState(range: getRange, actionBlock: actionBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Underlines the tappable region and stores the tap logic for interation.
|
|
||||||
private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) {
|
|
||||||
|
|
||||||
setActionAttributes(range: range)
|
|
||||||
appendActionableClause(range: range, actionBlock: actionBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
|
|
||||||
|
|
||||||
for clause in clauses {
|
|
||||||
// This determines if we tapped on the desired range of text.
|
|
||||||
if gesture.didTapAttributedTextInLabel(self, inRange: clause.range) {
|
|
||||||
clause.performAction()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
extension UITapGestureRecognizer {
|
|
||||||
|
|
||||||
func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool {
|
|
||||||
|
|
||||||
// There would only ever be one clause to act on.
|
|
||||||
if label.makeWholeViewClickable {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let abstractContainer = label.abstractTextContainer() else { return false }
|
|
||||||
let textContainer = abstractContainer.0
|
|
||||||
let layoutManager = abstractContainer.1
|
|
||||||
|
|
||||||
let tapLocation = location(in: label)
|
|
||||||
let indexOfGlyph = layoutManager.glyphIndex(for: tapLocation, in: textContainer)
|
|
||||||
let intrinsicWidth = label.intrinsicContentSize.width
|
|
||||||
|
|
||||||
// Assert that tapped occured within acceptable bounds based on alignment.
|
|
||||||
switch label.textAlignment {
|
|
||||||
case .right:
|
|
||||||
if tapLocation.x < label.bounds.width - intrinsicWidth {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case .center:
|
|
||||||
let halfBounds = label.bounds.width / 2
|
|
||||||
let halfIntrinsicWidth = intrinsicWidth / 2
|
|
||||||
|
|
||||||
if tapLocation.x > halfBounds + halfIntrinsicWidth {
|
|
||||||
return false
|
|
||||||
} else if tapLocation.x < halfBounds - halfIntrinsicWidth {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default: // Left align
|
|
||||||
if tapLocation.x > intrinsicWidth {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Affirms that the tap occured in the desired rect of provided by the target range.
|
|
||||||
return layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer).contains(tapLocation) && NSLocationInRange(indexOfGlyph, targetRange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Accessibility
|
|
||||||
extension Label {
|
|
||||||
|
|
||||||
func customAccessibilityAction(range: NSRange) -> UIAccessibilityCustomAction? {
|
|
||||||
|
|
||||||
guard let text = text else { return nil }
|
|
||||||
|
|
||||||
if accessibilityHint == nil {
|
|
||||||
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint")
|
|
||||||
}
|
|
||||||
|
|
||||||
let actionText = NSString(string: text).substring(with: range)
|
|
||||||
let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:)))
|
|
||||||
accessibilityCustomActions?.append(accessibleAction)
|
|
||||||
|
|
||||||
return accessibleAction
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
|
|
||||||
|
|
||||||
for clause in clauses {
|
|
||||||
if action.hash == clause.accessibilityID {
|
|
||||||
clause.performAction()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func accessibilityActivate() -> Bool {
|
|
||||||
|
|
||||||
guard let accessibleActions = accessibilityCustomActions else { return false }
|
|
||||||
|
|
||||||
for clause in clauses {
|
|
||||||
for action in accessibleActions {
|
|
||||||
if action.hash == clause.accessibilityID {
|
|
||||||
clause.performAction()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
@ -1025,3 +477,4 @@ func validateAttribute(range: NSRange, in string: NSAttributedString, type: Stri
|
|||||||
|
|
||||||
return range
|
return range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user