// // Container.swift // MVMCoreUI // // Created by Scott Pfeil on 12/11/19. // Copyright © 2019 Verizon Wireless. All rights reserved. // import UIKit public protocol ContainerModelProtocol { var horizontalAlignment: UIStackView.Alignment? { get set } var verticalAlignment: UIStackView.Alignment? { get set } var useHorizontalMargins: Bool? { get set } var useVerticalMargins: Bool? { get set } } public class ContainerHelper: NSObject { var leftConstraint: NSLayoutConstraint? var topConstraint: NSLayoutConstraint? var bottomConstraint: NSLayoutConstraint? var rightConstraint: NSLayoutConstraint? var alignCenterHorizontalConstraint: NSLayoutConstraint? var alignCenterLeftConstraint: NSLayoutConstraint? var alignCenterRightConstraint: NSLayoutConstraint? var alignCenterVerticalConstraint: NSLayoutConstraint? var alignCenterTopConstraint: NSLayoutConstraint? var alignCenterBottomConstraint: NSLayoutConstraint? var leftLowConstraint: NSLayoutConstraint? var topLowConstraint: NSLayoutConstraint? var bottomLowConstraint: NSLayoutConstraint? var rightLowConstraint: NSLayoutConstraint? func constrainView(_ view: UIView) { guard let margins = view.superview?.layoutMarginsGuide else { return } leftConstraint = view.leftAnchor.constraint(equalTo: margins.leftAnchor) leftConstraint?.isActive = true topConstraint = view.topAnchor.constraint(equalTo: margins.topAnchor) topConstraint?.isActive = true rightConstraint = margins.rightAnchor.constraint(equalTo: view.rightAnchor) rightConstraint?.isActive = true bottomConstraint = margins.bottomAnchor.constraint(equalTo: view.bottomAnchor) bottomConstraint?.isActive = true alignCenterHorizontalConstraint = view.centerXAnchor.constraint(equalTo: margins.centerXAnchor) alignCenterLeftConstraint = view.leftAnchor.constraint(greaterThanOrEqualTo: margins.leftAnchor) alignCenterRightConstraint = margins.rightAnchor.constraint(greaterThanOrEqualTo: view.rightAnchor) alignCenterVerticalConstraint = view.centerYAnchor.constraint(equalTo: margins.centerYAnchor) alignCenterTopConstraint = view.topAnchor.constraint(greaterThanOrEqualTo: margins.topAnchor) alignCenterBottomConstraint = margins.bottomAnchor.constraint(greaterThanOrEqualTo: view.bottomAnchor) leftLowConstraint = view.leftAnchor.constraint(equalTo: margins.leftAnchor) leftLowConstraint?.priority = UILayoutPriority(rawValue: 200) leftLowConstraint?.isActive = true topLowConstraint = view.topAnchor.constraint(equalTo: margins.topAnchor) topLowConstraint?.priority = UILayoutPriority(rawValue: 200) topLowConstraint?.isActive = true rightLowConstraint = margins.rightAnchor.constraint(equalTo: view.rightAnchor) rightLowConstraint?.priority = UILayoutPriority(rawValue: 200) rightLowConstraint?.isActive = true bottomLowConstraint = margins.bottomAnchor.constraint(equalTo: view.bottomAnchor) bottomLowConstraint?.priority = UILayoutPriority(rawValue: 200) bottomLowConstraint?.isActive = true setAccessibility(view) } func setAccessibility(_ view: UIView) { guard let superView = view.superview else { return } superView.isAccessibilityElement = false if let elements = view.accessibilityElements { superView.accessibilityElements = elements } else { superView.accessibilityElements = [view] } } func alignHorizontal(_ alignment: UIStackView.Alignment) { switch alignment { case .center: alignCenterHorizontalConstraint?.isActive = true alignCenterLeftConstraint?.isActive = true alignCenterRightConstraint?.isActive = true leftConstraint?.isActive = false rightConstraint?.isActive = false case .leading: alignCenterHorizontalConstraint?.isActive = false alignCenterLeftConstraint?.isActive = false alignCenterRightConstraint?.isActive = true leftConstraint?.isActive = true rightConstraint?.isActive = false case .trailing: alignCenterHorizontalConstraint?.isActive = false alignCenterLeftConstraint?.isActive = true alignCenterRightConstraint?.isActive = false leftConstraint?.isActive = false rightConstraint?.isActive = true case .fill: alignCenterHorizontalConstraint?.isActive = false alignCenterLeftConstraint?.isActive = false alignCenterRightConstraint?.isActive = false leftConstraint?.isActive = true rightConstraint?.isActive = true default: break } } func alignVertical(_ alignment: UIStackView.Alignment) { switch alignment { case .center: alignCenterVerticalConstraint?.isActive = true alignCenterTopConstraint?.isActive = true alignCenterBottomConstraint?.isActive = true topConstraint?.isActive = false bottomConstraint?.isActive = false case .leading: alignCenterVerticalConstraint?.isActive = false alignCenterTopConstraint?.isActive = false alignCenterBottomConstraint?.isActive = true topConstraint?.isActive = true bottomConstraint?.isActive = false case .trailing: alignCenterVerticalConstraint?.isActive = false alignCenterTopConstraint?.isActive = true alignCenterBottomConstraint?.isActive = false topConstraint?.isActive = false bottomConstraint?.isActive = true case .fill: alignCenterVerticalConstraint?.isActive = false alignCenterTopConstraint?.isActive = false alignCenterBottomConstraint?.isActive = false topConstraint?.isActive = true bottomConstraint?.isActive = true default: break } } func set(with model: ContainerModelProtocol) { if let horizontalAlignment = model.horizontalAlignment { alignHorizontal(horizontalAlignment) } if let verticalAlignment = model.verticalAlignment { alignVertical(verticalAlignment) } } static func getAlignment(for string: String) -> UIStackView.Alignment? { switch string { case "leading": return .leading case "trailing": return .trailing case "center": return .center case "fill": return .fill default: return nil } } func set(with JSON: [AnyHashable: Any]?, for contained: UIView) { if let horizontalAlignmentString = JSON?.optionalStringForKey("horizontalAlignment"), let alignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) ?? (contained as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() { alignHorizontal(alignment) } else if let alignment = (contained as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() { alignHorizontal(alignment) } if let verticalAlignmentString = JSON?.optionalStringForKey("verticalAlignment"), let alignment = ContainerHelper.getAlignment(for: verticalAlignmentString) ?? (contained as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() { alignVertical(alignment) } else if let alignment = (contained as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() { alignVertical(alignment) } } } open class Container: View { var model: ContainerModelProtocol? var view: UIView? let containerHelper = ContainerHelper() var topMarginPadding: CGFloat = 0 var bottomMarginPadding: CGFloat = 0 } // MARK: - MVMCoreViewProtocol public extension Container { override func updateView(_ size: CGFloat) { super.updateView(size) (view as? MVMCoreViewProtocol)?.updateView(size) MFStyler.setMarginsFor(self, size: size, defaultHorizontal: model?.useHorizontalMargins ?? true, top: model?.useHorizontalMargins ?? true ? topMarginPadding : 0, bottom: model?.useHorizontalMargins ?? true ? bottomMarginPadding : 0) } /// Will be called only once. override func setupView() { super.setupView() backgroundColor = .clear } func addAndContain(_ view: UIView) { view.translatesAutoresizingMaskIntoConstraints = false addSubview(view) containerHelper.constrainView(view) self.view = view } convenience init(andContain view: UIView) { self.init() addAndContain(view) } } // MARK: - MVMCoreUIMoleculeViewProtocol public extension Container { override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) guard let view = view else { return } containerHelper.set(with: json, for: view) } override func reset() { super.reset() (view as? MVMCoreUIMoleculeViewProtocol)?.reset?() } func setAsMolecule() { (view as? MVMCoreUIMoleculeViewProtocol)?.setAsMolecule?() } }