Merge branch 'feature/revised_accessibility' into 'develop'
Changes made to resolve VO issues See merge request BPHV_MIPS/mvm_core_ui!390
This commit is contained in:
commit
992a6ade1b
@ -20,7 +20,7 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
public override var isSelected: Bool {
|
||||
@objc public override var isSelected: Bool {
|
||||
didSet {
|
||||
radioModel?.state = isSelected
|
||||
updateAccessibilityLabel()
|
||||
@ -127,7 +127,7 @@ import UIKit
|
||||
func updateAccessibilityLabel() {
|
||||
|
||||
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "radio_selected_state" : "radio_not_selected_state") {
|
||||
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "radio_desc_state") ?? "%@%@", "", state)
|
||||
accessibilityLabel = state
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,22 +19,22 @@ import Foundation
|
||||
private var selectedRadioButton: RadioButton?
|
||||
private var selectedRadioButtonModel: RadioButtonModel?
|
||||
public var baseValue: AnyHashable?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public func set(_ radioButtonModel: RadioButtonModel, _ radioButton: RadioButton) {
|
||||
self.fieldKey = radioButtonModel.fieldKey
|
||||
self.groupName = radioButtonModel.groupName
|
||||
|
||||
|
||||
if radioButtonModel.state {
|
||||
if self.baseValue == nil,
|
||||
let selected = radioButtonModel.baseValue as? Bool, selected {
|
||||
self.baseValue = radioButtonModel.fieldValue
|
||||
}
|
||||
selectedRadioButtonModel = radioButtonModel
|
||||
|
||||
|
||||
// Below code is needed for cell resuse scenario.
|
||||
radioButton.isSelected = true
|
||||
selectedRadioButton = radioButton
|
||||
@ -50,24 +50,24 @@ import Foundation
|
||||
public static func setupForRadioButtonGroup(_ radioButtonModel: RadioButtonModel, _ radioButton: RadioButton, delegateObject: MVMCoreUIDelegateObject?) {
|
||||
|
||||
guard let groupName = radioButtonModel.fieldKey,
|
||||
let formValidator = delegateObject?.formHolderDelegate?.formValidator else {
|
||||
return
|
||||
}
|
||||
|
||||
let formValidator = delegateObject?.formHolderDelegate?.formValidator
|
||||
else { return }
|
||||
|
||||
let radioButtonSelectionHelper = formValidator.radioButtonsModelByGroup[groupName] ?? RadioButtonSelectionHelper()
|
||||
radioButtonSelectionHelper.set(radioButtonModel, radioButton)
|
||||
formValidator.radioButtonsModelByGroup[groupName] = radioButtonSelectionHelper
|
||||
FormValidator.setupValidation(for: radioButtonSelectionHelper, delegate: delegateObject?.formHolderDelegate)
|
||||
}
|
||||
|
||||
|
||||
public func selected(_ radioButton: RadioButton) {
|
||||
|
||||
// Checks because the view could be reused
|
||||
if selectedRadioButton?.radioModel === selectedRadioButtonModel {
|
||||
selectedRadioButton?.isSelected = false
|
||||
} else {
|
||||
selectedRadioButtonModel?.state = false
|
||||
}
|
||||
|
||||
|
||||
selectedRadioButton = radioButton
|
||||
selectedRadioButton?.isSelected = true
|
||||
selectedRadioButtonModel = selectedRadioButton?.radioModel
|
||||
@ -76,6 +76,7 @@ import Foundation
|
||||
|
||||
// MARK: - FormValidationFormFieldProtocol
|
||||
extension RadioButtonSelectionHelper {
|
||||
|
||||
public func formFieldValue() -> AnyHashable? {
|
||||
return selectedRadioButtonModel?.fieldValue
|
||||
}
|
||||
|
||||
@ -14,11 +14,13 @@ import UIKit
|
||||
// MARK: - Outlets
|
||||
//-----------------------------------------------------
|
||||
|
||||
let radioButton = RadioButton(frame: .zero)
|
||||
let radioButton = RadioButton()
|
||||
let leftImage = LoadImageView(pinnedEdges: .all)
|
||||
let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink()
|
||||
var stack: Stack<StackModel>
|
||||
|
||||
private var observation: NSKeyValueObservation? = nil
|
||||
|
||||
//-----------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//-----------------------------------------------------
|
||||
@ -46,6 +48,16 @@ import UIKit
|
||||
stack.restack()
|
||||
eyebrowHeadlineBodyLink.body.textColor = .mvmOrangeAA
|
||||
eyebrowHeadlineBodyLink.headline.styleBoldBodySmall(true)
|
||||
radioButton.isAccessibilityElement = false
|
||||
isAccessibilityElement = true
|
||||
updateAccessibilityLabel()
|
||||
accessibilityTraits = .button
|
||||
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint")
|
||||
updateAccessibilityLabel()
|
||||
|
||||
observation = observe(\.radioButton.isSelected, options: [.new]) { [weak self] _, _ in
|
||||
self?.updateAccessibilityLabel()
|
||||
}
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
@ -64,6 +76,7 @@ import UIKit
|
||||
radioButton.set(with: model.radioButton, delegateObject, additionalData)
|
||||
leftImage.set(with: model.image, delegateObject, additionalData)
|
||||
eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData)
|
||||
updateAccessibilityLabel()
|
||||
}
|
||||
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
@ -71,6 +84,34 @@ import UIKit
|
||||
}
|
||||
|
||||
public override func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
radioButton.tapAction()
|
||||
radioButton.tapAction()
|
||||
}
|
||||
|
||||
func updateAccessibilityLabel() {
|
||||
|
||||
var message = MVMCoreUIUtility.hardcodedString(withKey: "radio_button") ?? ""
|
||||
|
||||
radioButton.updateAccessibilityLabel()
|
||||
if let radioButtonLabel = radioButton.accessibilityLabel {
|
||||
message += radioButtonLabel + ", "
|
||||
}
|
||||
|
||||
if let leftImageLabel = leftImage.accessibilityLabel {
|
||||
message += leftImageLabel + ", "
|
||||
}
|
||||
|
||||
if let eyebrowLabel = eyebrowHeadlineBodyLink.eyebrow.text {
|
||||
message += eyebrowLabel + ", "
|
||||
}
|
||||
|
||||
if let headlineLabel = eyebrowHeadlineBodyLink.headline.text {
|
||||
message += headlineLabel + ", "
|
||||
}
|
||||
|
||||
if let bodyLabel = eyebrowHeadlineBodyLink.body.text {
|
||||
message += bodyLabel
|
||||
}
|
||||
|
||||
accessibilityLabel = message
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,11 +9,19 @@
|
||||
import Foundation
|
||||
|
||||
public class ListLeftVariableRadioButtonAndPaymentMethodModel: ListItemModel, MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "listLVRBImg"
|
||||
public var radioButton: RadioButtonModel
|
||||
public var image: ImageViewModel
|
||||
public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init(radioButton: RadioButtonModel, image: ImageViewModel, eyebrowHeadlineBodyLink:EyebrowHeadlineBodyLinkModel) {
|
||||
self.radioButton = radioButton
|
||||
self.image = image
|
||||
@ -21,6 +29,10 @@ public class ListLeftVariableRadioButtonAndPaymentMethodModel: ListItemModel, Mo
|
||||
super.init()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
public override func setDefaults() {
|
||||
super.setDefaults()
|
||||
if image.width == nil, image.height == nil {
|
||||
@ -29,6 +41,10 @@ public class ListLeftVariableRadioButtonAndPaymentMethodModel: ListItemModel, Mo
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case radioButton
|
||||
@ -36,6 +52,10 @@ public class ListLeftVariableRadioButtonAndPaymentMethodModel: ListItemModel, Mo
|
||||
case eyebrowHeadlineBodyLink
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
radioButton = try typeContainer.decode(RadioButtonModel.self, forKey: .radioButton)
|
||||
|
||||
@ -14,10 +14,12 @@ open class ListLeftVariableRadioButtonBodyText: TableViewCell {
|
||||
// MARK: - Outlets
|
||||
//-----------------------------------------------------
|
||||
|
||||
let radioButton = RadioButton(frame: .zero)
|
||||
let radioButton = RadioButton()
|
||||
let headlineBody = HeadlineBody()
|
||||
var stack: Stack<StackModel>
|
||||
|
||||
private var observation: NSKeyValueObservation? = nil
|
||||
|
||||
//-----------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//-----------------------------------------------------
|
||||
@ -42,6 +44,15 @@ open class ListLeftVariableRadioButtonBodyText: TableViewCell {
|
||||
|
||||
addMolecule(stack)
|
||||
stack.restack()
|
||||
isAccessibilityElement = true
|
||||
radioButton.isAccessibilityElement = false
|
||||
accessibilityTraits = .button
|
||||
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "radio_action_hint")
|
||||
updateAccessibilityLabel()
|
||||
|
||||
observation = observe(\.radioButton.isSelected, options: [.new]) { [weak self] _, _ in
|
||||
self?.updateAccessibilityLabel()
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
@ -55,6 +66,7 @@ open class ListLeftVariableRadioButtonBodyText: TableViewCell {
|
||||
|
||||
radioButton.set(with: model.radioButton, delegateObject, additionalData)
|
||||
headlineBody.set(with: model.headlineBody, delegateObject, additionalData)
|
||||
updateAccessibilityLabel()
|
||||
}
|
||||
|
||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
@ -64,4 +76,24 @@ open class ListLeftVariableRadioButtonBodyText: TableViewCell {
|
||||
public override func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
radioButton.tapAction()
|
||||
}
|
||||
|
||||
func updateAccessibilityLabel() {
|
||||
|
||||
var message = MVMCoreUIUtility.hardcodedString(withKey: "radio_button") ?? ""
|
||||
|
||||
radioButton.updateAccessibilityLabel()
|
||||
if let radioButtonLabel = radioButton.accessibilityLabel {
|
||||
message += radioButtonLabel + ", "
|
||||
}
|
||||
|
||||
if let headlineLabel = headlineBody.headlineLabel.text {
|
||||
message += headlineLabel + ", "
|
||||
}
|
||||
|
||||
if let messageLabel = headlineBody.messageLabel.text {
|
||||
message += messageLabel
|
||||
}
|
||||
|
||||
accessibilityLabel = message
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,21 +9,39 @@
|
||||
import Foundation
|
||||
import MVMCore
|
||||
|
||||
|
||||
@objcMembers public class MoleculeListItemModel: ListItemModel, MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public class var identifier: String {
|
||||
return "listItem"
|
||||
}
|
||||
|
||||
public var molecule: MoleculeModelProtocol
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case molecule
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
//--------------------------------------------------
|
||||
|
||||
public init(with moleculeModel: MoleculeModelProtocol) {
|
||||
molecule = moleculeModel
|
||||
super.init()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
@ -38,4 +56,3 @@ import MVMCore
|
||||
try container.encodeModel(molecule, forKey: .molecule)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,8 +10,9 @@ import UIKit
|
||||
|
||||
|
||||
@objcMembers open class MoleculeTableViewCell: TableViewCell {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - MoleculeViewProtocol
|
||||
//--------------------------------------------------
|
||||
|
||||
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import UIKit
|
||||
|
||||
// This class is only temporarily necessary. Eventually we will have initWithModel instad of just init for moleculeviews, which will remove this need.
|
||||
open class StringAndMoleculeStack: MoleculeStackView {
|
||||
override open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
|
||||
override open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
guard let model = model as? StackModelProtocol,
|
||||
let molcules = model.molecules as? [MoleculeStackItemModel] else { return }
|
||||
for stackItemModel in molcules {
|
||||
|
||||
@ -9,6 +9,9 @@
|
||||
import UIKit
|
||||
|
||||
open class ModalMoleculeListTemplate: MoleculeListTemplate {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public var closeButton: Button?
|
||||
|
||||
@ -17,5 +20,8 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate {
|
||||
closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { _ in
|
||||
MVMCoreNavigationHandler.shared()?.removeCurrentViewController()
|
||||
})
|
||||
|
||||
accessibilityElements = [closeButton as Any, tableView as Any]
|
||||
UIAccessibility.post(notification: .layoutChanged, argument: closeButton)
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,12 +21,10 @@ public protocol MoleculeListCellProtocol: UITableViewCell {
|
||||
|
||||
// Default implementation does nothing
|
||||
extension MoleculeListCellProtocol {
|
||||
public func setLines(with model: LineModel?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) {
|
||||
}
|
||||
|
||||
public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
}
|
||||
|
||||
func willDisplay() {
|
||||
}
|
||||
public func setLines(with model: LineModel?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) { }
|
||||
|
||||
public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { }
|
||||
|
||||
func willDisplay() { }
|
||||
}
|
||||
|
||||
@ -209,8 +209,11 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
||||
func getMoleculeInfo(with listItem: (ListItemModelProtocol & MoleculeModelProtocol)?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol & MoleculeModelProtocol)? {
|
||||
|
||||
guard let listItem = listItem,
|
||||
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) else { return nil }
|
||||
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem)
|
||||
else { return nil }
|
||||
|
||||
let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName
|
||||
|
||||
return (moleculeName, moleculeClass, listItem)
|
||||
}
|
||||
|
||||
|
||||
@ -205,6 +205,8 @@ import UIKit
|
||||
let caret = CaretView(lineWidth: 1)
|
||||
caret.translatesAutoresizingMaskIntoConstraints = true
|
||||
caret.isAccessibilityElement = true
|
||||
caret.accessibilityTraits = .button
|
||||
caret.accessibilityLabel = "Caret,"
|
||||
caret.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccTabHint")
|
||||
caret.size = .small(.vertical)
|
||||
if let size = caret.size?.dimensions() {
|
||||
|
||||
@ -40,6 +40,7 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController {
|
||||
createViewForTableHeader()
|
||||
createViewForTableFooter()
|
||||
tableView?.reloadData()
|
||||
accessibilityElements = [tableView as Any]
|
||||
}
|
||||
|
||||
override open func viewDidLoad() {
|
||||
|
||||
@ -9,7 +9,12 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
open class ContainerHelper: NSObject {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
|
||||
var leftConstraint: NSLayoutConstraint?
|
||||
var topConstraint: NSLayoutConstraint?
|
||||
var bottomConstraint: NSLayoutConstraint?
|
||||
@ -28,17 +33,26 @@ open class ContainerHelper: NSObject {
|
||||
var bottomLowConstraint: NSLayoutConstraint?
|
||||
var rightLowConstraint: NSLayoutConstraint?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
open func constrainView(_ view: UIView) {
|
||||
guard let margins = view.superview?.layoutMarginsGuide else { return }
|
||||
|
||||
leftConstraint?.isActive = false
|
||||
leftConstraint = view.leftAnchor.constraint(equalTo: margins.leftAnchor)
|
||||
leftConstraint?.isActive = true
|
||||
|
||||
topConstraint?.isActive = false
|
||||
topConstraint = view.topAnchor.constraint(equalTo: margins.topAnchor)
|
||||
topConstraint?.isActive = true
|
||||
|
||||
rightConstraint?.isActive = false
|
||||
rightConstraint = margins.rightAnchor.constraint(equalTo: view.rightAnchor)
|
||||
rightConstraint?.isActive = true
|
||||
|
||||
bottomConstraint?.isActive = false
|
||||
bottomConstraint = margins.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||
bottomConstraint?.isActive = true
|
||||
|
||||
@ -50,23 +64,25 @@ open class ContainerHelper: NSObject {
|
||||
alignCenterTopConstraint = view.topAnchor.constraint(greaterThanOrEqualTo: margins.topAnchor)
|
||||
alignCenterBottomConstraint = margins.bottomAnchor.constraint(greaterThanOrEqualTo: view.bottomAnchor)
|
||||
|
||||
leftLowConstraint?.isActive = false
|
||||
leftLowConstraint = view.leftAnchor.constraint(equalTo: margins.leftAnchor)
|
||||
leftLowConstraint?.priority = UILayoutPriority(rawValue: 200)
|
||||
leftLowConstraint?.isActive = true
|
||||
|
||||
topLowConstraint?.isActive = false
|
||||
topLowConstraint = view.topAnchor.constraint(equalTo: margins.topAnchor)
|
||||
topLowConstraint?.priority = UILayoutPriority(rawValue: 200)
|
||||
topLowConstraint?.isActive = true
|
||||
|
||||
rightLowConstraint?.isActive = false
|
||||
rightLowConstraint = margins.rightAnchor.constraint(equalTo: view.rightAnchor)
|
||||
rightLowConstraint?.priority = UILayoutPriority(rawValue: 200)
|
||||
rightLowConstraint?.isActive = true
|
||||
|
||||
bottomLowConstraint?.isActive = false
|
||||
bottomLowConstraint = margins.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||
bottomLowConstraint?.priority = UILayoutPriority(rawValue: 200)
|
||||
bottomLowConstraint?.isActive = true
|
||||
|
||||
setAccessibility(view)
|
||||
}
|
||||
|
||||
open func setAccessibility(_ view: UIView) {
|
||||
|
||||
@ -53,6 +53,7 @@
|
||||
|
||||
|
||||
// MARK: Radio Button
|
||||
"radio_button" = "Radio Button,";
|
||||
"radio_action_hint" = "Double tap to select";
|
||||
"radio_selected_state" = "Selected";
|
||||
"radio_not_selected_state" = "Not Selected";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user