diff --git a/MVMCoreUI/Atomic/Atoms/Views/ProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/ProgressBar.swift index edb8c1de..a9fde5c1 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ProgressBar.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ProgressBar.swift @@ -8,20 +8,30 @@ import Foundation + @objcMembers open class ProgressBar: UIProgressView, MVMCoreViewProtocol, MoleculeViewProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + var progressBarModel: ProgressBarModel? var thickness: CGFloat = 8.0 { willSet(newValue) { heightAnchor.constraint(equalToConstant: newValue).isActive = true + if progressBarModel?.roundedCorners ?? false { - layer.cornerRadius = newValue/2.0 + layer.cornerRadius = newValue / 2.0 } else { progressViewStyle = .bar } } } + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public override init(frame: CGRect) { super.init(frame: frame) setupView() @@ -37,41 +47,49 @@ import Foundation setupView() } - // MARK: - MVMCoreViewProtocol + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + public func setupView() { + clipsToBounds = true translatesAutoresizingMaskIntoConstraints = false thickness = 8 progress = 0 - progressTintColor = UIColor.mfCerulean() - trackTintColor = UIColor.mfLightSilver() + progressTintColor = .mfCerulean() + trackTintColor = .mvmCoolGray3 } - public func updateView(_ size: CGFloat) { - } + public func updateView(_ size: CGFloat) { } + + //-------------------------------------------------- + // MARK: - MVMCoreViewProtocol + //-------------------------------------------------- - //MARK: - MoleculeViewProtocol public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let progressBarModel = model as? ProgressBarModel else { return } + self.progressBarModel = progressBarModel thickness = progressBarModel.thickness ?? 8 - progress = Float((progressBarModel.percent)/100.0) + progress = Float((progressBarModel.percent) / 100.0) progressTintColor = progressBarModel.color.uiColor + if let backgroundColor = progressBarModel.backgroundColor { trackTintColor = backgroundColor.uiColor } } public func reset() { + thickness = 8 progress = 0 - progressTintColor = UIColor.mfCerulean() - trackTintColor = UIColor.mfLightSilver() + progressTintColor = .mfCerulean() + trackTintColor = .mvmCoolGray3 } public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 8 } } - - diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift index 072be6bb..1e5875f1 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThin.swift @@ -7,24 +7,28 @@ // import Foundation + + @objcMembers open class ListProgressBarThin: TableViewCell { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- + public let progressBar = ProgressBar() - public let leftHeadline = Label.commonLabelB1(true) - public let leftBody = Label.commonLabelB2(true) + public let leftHeadline = Label.createLabelBoldBodySmall(true) + public let leftBody = Label.createLabelBoldBodySmall(true) public let rightBar = Line() - public let rightLabel = Label.commonLabelB2(true) + public let rightLabel = Label.createLabelBoldBodySmall(true) private let barStackItem: StackItem private let rightLabelStackItem: StackItem public var labelStack: Stack public var horizontalStack: Stack public var stack: Stack - + //------------------------------------------------------ // MARK: - Initializers //------------------------------------------------------ + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { //vertical stack with leftHeadline, leftBody labelStack = Stack.createStack(with: [leftHeadline, leftBody], axis: .vertical, spacing: 2) @@ -34,9 +38,9 @@ import Foundation rightLabelStackItem = StackItem(andContain: rightLabel) let horizontalStackItems = [StackItem(andContain: labelStack), barStackItem, rightLabelStackItem] let horizontalStackModel = StackModel(molecules: [StackItemModel(horizontalAlignment: .leading), StackItemModel(horizontalAlignment: .fill), StackItemModel(spacing: 5, horizontalAlignment: .fill)], - axis: .horizontal) + axis: .horizontal) horizontalStack = Stack(with: horizontalStackModel, stackItems: horizontalStackItems) - + //stack with all components stack = Stack.createStack(with: [horizontalStack, progressBar], axis: .vertical, spacing: PaddingDefaultVerticalSpacing3) super.init(style: style, reuseIdentifier: reuseIdentifier) @@ -49,19 +53,23 @@ import Foundation open override func alignAccessoryToHero() -> CGPoint? { // Ensures that the right items are centered with the arrow. let heroCenter = super.alignAccessoryToHero() + if let heroCenter = heroCenter { let convertedPoint = horizontalStack.convert(heroCenter, from: self) barStackItem.containerHelper.alignCenterVerticalConstraint?.constant = convertedPoint.y - horizontalStack.bounds.midY rightLabelStackItem.containerHelper.alignCenterVerticalConstraint?.constant = convertedPoint.y - horizontalStack.bounds.midY } + return heroCenter } //------------------------------------------------------- - // MARK: - View Lifecycle + // MARK: - Lifecycle //------------------------------------------------------- + open override func setupView() { super.setupView() + rightBar.widthAnchor.constraint(equalToConstant: 20).isActive = true rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) @@ -70,19 +78,24 @@ import Foundation stack.restack() horizontalStack.restack() labelStack.restack() + isAccessibilityElement = true + updateAccessibilityLabel() } //------------------------------------------------------ // MARK: - Molecule //------------------------------------------------------ - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListProgressBarThinModel else { return } - labelStack.updateContainedMolecules(with: [model.leftHeadline, - model.leftBody], delegateObject, additionalData) + + labelStack.updateContainedMolecules(with: [model.leftHeadline, model.leftBody], delegateObject, additionalData) progressBar.set(with: model.progressBar, delegateObject, additionalData) rightBar.set(with: model.rightBar, delegateObject, additionalData) rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + updateAccessibilityLabel() } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { @@ -96,4 +109,31 @@ import Foundation rightLabel.styleB2(true) rightBar.setStyle(.medium) } + + //------------------------------------------------------ + // MARK: - Accessibility + //------------------------------------------------------ + + func updateAccessibilityLabel() { + + var message = "" + + if let leftHeadlineText = leftHeadline.text { + message += leftHeadlineText + ", " + } + + if let leftBodyText = leftBody.text { + message += leftBodyText + ", " + } + + if let progressLabel = progressBar.accessibilityLabel, let progressValue = progressBar.accessibilityValue { + message += progressLabel + ", " + progressValue + ", " + } + + if let rightLabelText = rightLabel.text { + message += rightLabelText + ", " + } + + accessibilityLabel = message + } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift index acd8e013..b74c64b3 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/ListProgressBarThinModel.swift @@ -7,7 +7,13 @@ // import Foundation + + public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier = "listPrgBarThin" public var progressBar: ProgressBarModel public var leftHeadline: LabelModel @@ -15,6 +21,10 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol { public var rightBar: LineModel public var rightLabel: LabelModel + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(progressBar: ProgressBarModel, leftHeadline: LabelModel, leftBody: LabelModel? = nil, rightBar: LineModel, rightLabel: LabelModel) { self.progressBar = progressBar self.leftHeadline = leftHeadline @@ -24,15 +34,26 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol { super.init() } + //-------------------------------------------------- + // MARK: - Method + //-------------------------------------------------- + override public func setDefaults() { super.setDefaults() + rightBar.type = .medium + if rightBar.backgroundColor == nil { rightBar.backgroundColor = Color(uiColor: .gray) } + leftHeadline.hero = 0 } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case progressBar @@ -42,6 +63,10 @@ public class ListProgressBarThinModel: ListItemModel, MoleculeModelProtocol { case rightLabel } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + public required init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) progressBar = try typeContainer.decode(ProgressBarModel.self, forKey:.progressBar) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift index 2291c080..e1e9d758 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinks.swift @@ -43,17 +43,17 @@ import Foundation super.setupView() addMolecule(stack) stack.restack() + updateAccessibilityLabel() } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListOneColumnFullWidthTextAllTextAndLinksModel else { return } - stack.updateContainedMolecules(with: [model.eyebrow, - model.headline, - model.subHeadline, - model.body, - model.link], + + stack.updateContainedMolecules(with: [model.eyebrow, model.headline, model.subHeadline, model.body, model.link], delegateObject, additionalData) + updateAccessibilityLabel() } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { @@ -67,4 +67,60 @@ import Foundation subHeadline.styleBoldBodySmall(true) body.styleRegularBodySmall(true) } + + func updateAccessibilityLabel() { + + var message = "" + + if let eyebrowLabel = eyebrow.text { + message += eyebrowLabel + ", " + } + + if let headlineLabel = headline.text { + message += headlineLabel + ", " + } + + if let headlineLabel = headline.text { + message += headlineLabel + ", " + } + + if let bodyLabel = body.text { + message += bodyLabel + } + + let linkShowing = link.titleLabel?.text?.count ?? 0 > 0 + isAccessibilityElement = !linkShowing + link.isAccessibilityElement = linkShowing + + if !linkShowing { + // Make whole cell focusable if no link. + accessibilityLabel = message + } else { + // Allow only radio button and link to be focused on. + var elements: [UIView] = [] + + + if let eyeBrowText = eyebrow.text, !eyeBrowText.isEmpty { + elements.append(eyebrow) + } + + if let headlineText = headline.text, !headlineText.isEmpty { + elements.append(headline) + } + + if let subHeadlineText = subHeadline.text, !subHeadlineText.isEmpty { + elements.append(subHeadline) + } + + if let bodyText = body.text, !bodyText.isEmpty { + elements.append(body) + } + + if linkShowing { + elements.append(link) + } + + accessibilityElements = elements + } + } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift index 47e482a0..34e1d28b 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextAllTextAndLinksModel.swift @@ -8,7 +8,12 @@ import Foundation + public class ListOneColumnFullWidthTextAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "list1CTxt" public var eyebrow: LabelModel? public var headline : LabelModel? @@ -16,6 +21,10 @@ public class ListOneColumnFullWidthTextAllTextAndLinksModel: ListItemModel, Mole public var body: LabelModel? public var link : LinkModel? + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(eyebrow: LabelModel? = nil, headline: LabelModel? = nil, subHeadline: LabelModel? = nil, body: LabelModel? = nil, link: LinkModel? = nil) { self.eyebrow = eyebrow self.headline = headline @@ -25,6 +34,10 @@ public class ListOneColumnFullWidthTextAllTextAndLinksModel: ListItemModel, Mole super.init() } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case eyebrow @@ -34,6 +47,10 @@ public class ListOneColumnFullWidthTextAllTextAndLinksModel: ListItemModel, Mole case link } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift index 99bc1a16..bad9caca 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyText.swift @@ -8,28 +8,55 @@ import Foundation + @objcMembers open class ListOneColumnFullWidthTextBodyText: TableViewCell { - //----------------------------------------------------- // MARK: - Outlets //----------------------------------------------------- - public var headlineBody = HeadlineBody(frame: .zero) + + public var headlineBody = HeadlineBody() //----------------------------------------------------- - // MARK: - View Lifecycle + // MARK: - Lifecycle //----------------------------------------------------- + override open func setupView() { super.setupView() + addMolecule(headlineBody) + isAccessibilityElement = true + updateAccessibilityLabel() } - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?){ + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?){ super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListOneColumnFullWidthTextBodyTextModel else { return } + headlineBody.set(with: model.headlineBody, delegateObject, additionalData) + updateAccessibilityLabel() } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 90 } + + //----------------------------------------------------- + // MARK: - Accessibility + //----------------------------------------------------- + + func updateAccessibilityLabel() { + + var message = "" + + if let headlineLabel = headlineBody.headlineLabel.text { + message += headlineLabel + ", " + } + + if let messageLabel = headlineBody.messageLabel.text { + message += messageLabel + } + + accessibilityLabel = message + } } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift index f25870aa..288b0591 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift @@ -8,26 +8,46 @@ import Foundation + public class ListOneColumnFullWidthTextBodyTextModel: ListItemModel, MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "list1CFWBdy" public var headlineBody: HeadlineBodyModel + //-------------------------------------------------- + // MARK: - Initializier + //-------------------------------------------------- + public init(headlineBody: HeadlineBodyModel) { self.headlineBody = headlineBody super.init() } - // Defaults to set + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + override public func setDefaults() { super.setDefaults() headlineBody.style = .item } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case headlineBody } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) headlineBody = try typeContainer.decode(HeadlineBodyModel.self, forKey: .headlineBody)