diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 37eff177..0b12b5a9 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -182,29 +182,7 @@ public typealias ActionBlock = () -> () } @objc public func resetAttributeStyle() { - /* - * This is to address a reuse issue with iOS 13 and up. - * Even if you set text & attributedText to nil, the moment you set text with a value, - * attributedText will hold a dirty value from a previously reused cell even if reset() is - * appropriately called. - * Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml - */ - if let text = text, !text.isEmpty { - - //create the primary string - let mutableText = NSMutableAttributedString.mutableText(for: text, - textStyle: textStyle, - useScaledFont: useScaledFont, - textColor: textColorConfiguration.getColor(self), - alignment: textAlignment, - lineBreakMode: lineBreakMode) - - if let attributes = attributes { - mutableText.apply(attributes: attributes) - } - - self.attributedText = mutableText - } + setNeedsUpdate() } public func viewModelDidUpdate() { @@ -221,7 +199,6 @@ public typealias ActionBlock = () -> () } if let style = viewModel.fontStyle?.vdsTextStyle() { - font = style.font textStyle = style } else if let fontName = viewModel.fontName { // there is a TextStyle.defaultStyle @@ -229,9 +206,8 @@ public typealias ActionBlock = () -> () if let fontSize { standardFontSize = fontSize } - if let customStyle = style(for: fontName, pointSize: fontSize ?? standardFontSize), customStyle != textStyle { - font = customStyle.font - textStyle = customStyle + if let newFont = UIFont(name: fontName, size: fontSize ?? standardFontSize) { + font = newFont } } @@ -248,26 +224,6 @@ public typealias ActionBlock = () -> () } } - - /// 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() diff --git a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift index f7e5917e..2d24c5a1 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift @@ -54,12 +54,3 @@ extension VDS.Font { Self.allCases.filter({$0.fontName == fontName }).first } } - -extension VDS.TextStyle { - internal static func style(from font: VDS.Font, pointSize: CGFloat) -> TextStyle? { - guard let first = allCases.filter({$0.fontFace == font && $0.pointSize == pointSize}).first else { - return TextStyle(rawValue: "Custom-TextStyle", fontFace: font, pointSize: pointSize) - } - return first - } -} diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift index 2d176986..654e48ac 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift @@ -41,16 +41,6 @@ import VDS } } } - - //-------------------------------------------------- - // MARK: - Public Properties Overrides - //-------------------------------------------------- - open override var selectedIndex: Int { - didSet { - guard let viewModel else { return } - viewModel.selectedIndex = selectedIndex - } - } //------------------------------------------------- // MARK: - Layout Views diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift index 4785cfb9..536bc16b 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift @@ -17,11 +17,11 @@ import VDS open var viewModel: TwoButtonViewModel! open var delegateObject: MVMCoreUIDelegateObject? open var additionalData: [AnyHashable : Any]? - + open var primaryButton = PillButton() open var secondaryButton = PillButton() private var buttonGroup = VDS.ButtonGroup() - + private var heightConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -34,6 +34,7 @@ import VDS buttonGroup.alignment = .center buttonGroup.rowQuantityPhone = 2 buttonGroup.rowQuantityTablet = 2 + heightConstraint = height(constant: VDS.Button.Size.large.height, priority: .required) } //-------------------------------------------------- @@ -70,6 +71,8 @@ import VDS if buttons.count != buttonGroup.buttons.count { buttonGroup.buttons = buttons } + + heightConstraint?.constant = primaryButton.size == .small || secondaryButton.size == .small ? VDS.Button.Size.small.height : VDS.Button.Size.large.height } //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift index c2cad75a..0ed1db11 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift @@ -38,6 +38,7 @@ public class TwoButtonViewModel: ParentMoleculeModelProtocol { case backgroundColor case primaryButton case secondaryButton + case fillContainer } //-------------------------------------------------- @@ -69,6 +70,7 @@ public class TwoButtonViewModel: ParentMoleculeModelProtocol { try decoder.setContext(value: Use.secondary, for: "style") { self.secondaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .secondaryButton) } + fillContainer = try typeContainer.decodeIfPresent(Bool.self, forKey: .fillContainer) ?? false } public func encode(to encoder: Encoder) throws { @@ -78,5 +80,6 @@ public class TwoButtonViewModel: ParentMoleculeModelProtocol { try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(primaryButton, forKey: .primaryButton) try container.encodeIfPresent(secondaryButton, forKey: .secondaryButton) + try container.encodeIfPresent(fillContainer, forKey: .fillContainer) } } diff --git a/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift b/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift index 421c2e95..c4f7d825 100644 --- a/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift @@ -55,6 +55,9 @@ open class Carousel: View { /// The view that we use for paging public var pagingView: (MoleculeViewProtocol & CarouselPageControlProtocol)? + /// The pagingView anchor to the bottom of the carousel. Disabled when the pagingView is hidden. + public var pagingBottomPin: NSLayoutConstraint? + /// If the carousel should loop after scrolling past the first and final cells. public var loop = false @@ -89,10 +92,10 @@ open class Carousel: View { // Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled. guard let model = model as? CarouselModel, !model.molecules.isEmpty, (model.paging == true || loop == true) else { return } - DispatchQueue.main.async { - self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false) - self.collectionView.layoutIfNeeded() - self.showPeaking(true) + DispatchQueue.main.async { [self] in + collectionView.scrollToItem(at: IndexPath(row: currentIndex, section: 0), at: itemAlignment, animated: false) + collectionView.layoutIfNeeded() + showPeaking(true) } } @@ -137,6 +140,16 @@ open class Carousel: View { (cell as? MVMCoreViewProtocol)?.updateView(size) } layoutCollection() + + // Check must be dispatched to main for the layout to complete in layoutCollection. + DispatchQueue.main.async { [self] in + let shouldHidePager = molecules?.count ?? 0 < 2 || collectionView.contentSize.width < bounds.width + if let pagingView = pagingView, shouldHidePager != pagingView.isHidden { + pagingView.isHidden = shouldHidePager + pagingBottomPin?.isActive = !shouldHidePager + delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + } + } } //-------------------------------------------------- @@ -276,7 +289,8 @@ open class Carousel: View { addSubview(pagingView) pagingView.centerXAnchor.constraint(equalTo: collectionView.centerXAnchor).isActive = true collectionView.bottomAnchor.constraint(equalTo: pagingView.centerYAnchor, constant: position).isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: pagingView.bottomAnchor).isActive = true + pagingBottomPin = bottomAnchor.constraint(greaterThanOrEqualTo: pagingView.bottomAnchor) + pagingBottomPin?.isActive = true bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) bottomPin?.priority = .defaultLow bottomPin?.isActive = true diff --git a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift index e1713b01..daf022a8 100644 --- a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift @@ -18,6 +18,8 @@ public var line: LineModel? public var scrollToRowIndex: Int? public var singleCellSelection: Bool = false + public var footerlessSpacerHeight: CGFloat? + public var footerlessSpacerColor: Color? public override var rootMolecules: [MoleculeModelProtocol] { if let molecules = molecules { @@ -60,6 +62,8 @@ case line case scrollToRowIndex case singleCellSelection + case footerlessSpacerHeight + case footerlessSpacerColor } //-------------------------------------------------- @@ -74,6 +78,8 @@ if let singleCellSelection = try typeContainer.decodeIfPresent(Bool.self, forKey: .singleCellSelection) { self.singleCellSelection = singleCellSelection } + footerlessSpacerColor = try typeContainer.decodeIfPresent(Color.self, forKey: .footerlessSpacerColor) + footerlessSpacerHeight = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .footerlessSpacerHeight) try super.init(from: decoder) try validateModelHasContent() } @@ -85,5 +91,7 @@ try container.encode(line, forKey: .line) try container.encode(singleCellSelection, forKey: .singleCellSelection) try container.encodeIfPresent(scrollToRowIndex, forKey: .scrollToRowIndex) + try container.encodeIfPresent(footerlessSpacerColor, forKey: .footerlessSpacerColor) + try container.encodeIfPresent(footerlessSpacerHeight, forKey: .footerlessSpacerHeight) } } diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index c00e5181..6f4d945e 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -69,16 +69,21 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return molecule } - override open func viewForBottom() -> UIView { - guard let footerModel = templateModel?.footer, - let molecule = generateMoleculeView(from: footerModel) - else { - let view = super.viewForBottom() - view.backgroundColor = templateModel?.backgroundColor?.uiColor ?? .clear - return view + open override func viewForBottom() -> UIView { + // If there is a footer molecule return the molecule. + if let footerModel = templateModel?.footer, + let molecule = generateMoleculeView(from: footerModel) { + return molecule } - - return molecule + // Otherwise setup a bottom spacer view. + let view: UIView + if let footerlessSpacerHeight = templateModel?.footerlessSpacerHeight { + view = MVMCoreUICommonViewsUtility.getView(with: footerlessSpacerHeight <= 0.5 ? 0.5 : footerlessSpacerHeight) + } else { + view = super.viewForBottom() + } + view.backgroundColor = templateModel?.footerlessSpacerColor?.uiColor ?? templateModel?.backgroundColor?.uiColor ?? .clear + return view } open override func handleNewData() {