diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index fa71def6..605f2e8a 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -364,8 +364,8 @@ extension Label { public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { guard let abstractContainer = label.abstractTextContainer() else { return CGRect() } - let textContainer = abstractContainer.0 - let layoutManager = abstractContainer.1 + let textContainer = abstractContainer.textContainer + let layoutManager = abstractContainer.layoutManager var glyphRange = NSRange() @@ -374,36 +374,6 @@ extension Label { return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer) } - - /** - Provides a text container and layout manager of how the text would appear on screen. - They are used in tandem to derive low-level TextKit results of the label. - */ - public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? { - - // Must configure the attributed string to translate what would appear on screen to accurately analyze. - guard let attributedText = attributedText else { return nil } - - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = textAlignment - - let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) - stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) - - let textStorage = NSTextStorage(attributedString: stagedAttributedString) - let layoutManager = NSLayoutManager() - let textContainer = NSTextContainer(size: .zero) - - layoutManager.addTextContainer(textContainer) - textStorage.addLayoutManager(layoutManager) - - textContainer.lineFragmentPadding = 0.0 - textContainer.lineBreakMode = lineBreakMode - textContainer.maximumNumberOfLines = numberOfLines - textContainer.size = bounds.size - - return (textContainer, layoutManager, textStorage) - } } // MARK: - Atomization diff --git a/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift b/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift index 585944bf..3ef117da 100644 --- a/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift +++ b/MVMCoreUI/Atomic/Organisms/Carousel/Carousel.swift @@ -307,6 +307,7 @@ open class Carousel: View { pagingView?.removeFromSuperview() bottomPin?.isActive = false + pagingView = nil guard var pagingView = view else { bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) diff --git a/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift b/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift index da57579d..ea4e0d88 100644 --- a/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift @@ -83,6 +83,9 @@ import Foundation let typeContainer = try decoder.container(keyedBy: CodingKeys.self) if let defaultPageType = Self.defaultPageType() { pageType = try typeContainer.decodeIfPresent(String.self, forKey: .pageType) ?? defaultPageType + if pageType.isEmpty { + pageType = defaultPageType + } } else { pageType = try typeContainer.decode(String.self, forKey: .pageType) } diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index d7c60545..4bd889bd 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -52,18 +52,20 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController, Rotor bottomView.updateView(width) showFooter(width) } - tableView.reloadData() + tableView.visibleCells.forEach { cell in + (cell as? MVMCoreViewProtocol)?.updateView(width) + } } open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) { - let isFirstRender = self.isFirstRender super.updateUI(for: molecules) guard molecules == nil else { return } createViewForTableHeader() createViewForTableFooter() - // Reloading the table is handled in updateViews. + // Reloading the table is handled in updateViews, however, update views is on a separate rendering task than the current thread. The table render needs to be bound and settled to the new model before others put in additional update requests. + tableView.reloadData() } override open func viewDidLoad() { diff --git a/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift b/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift index 9cbf85ed..c35a5d19 100644 --- a/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift +++ b/MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift @@ -20,7 +20,7 @@ public class ReplaceableMoleculeBehaviorModel: PageBehaviorModelProtocol { } } -public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, CoreLogging { +public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, PageVisibilityBehavior, CoreLogging { public var loggingPrefix: String { "\(self) \(ObjectIdentifier(self).hashValue) \(moleculeIds.prefix(3)) \(moleculeIds.count > 3 ? "+ \(moleculeIds.count - 3) more" : ""):\n" @@ -30,6 +30,8 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co String(describing: Self.self) } + var isPageShowing = false + var previouslyReplacedIds = Set() var moleculeIds: [String] public var modulesToListenFor: [String] private var observingForResponses: NSObjectProtocol? @@ -75,7 +77,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co return rootMolecule } debugLog("top replacing \(rootMolecule) with \(updatedMolecule)") - logUpdated(molecule: updatedMolecule) + logUpdated(moleculeId: updatedMolecule.id) changeList.append(updatedMolecule) return updatedMolecule } @@ -92,7 +94,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co return } debugLog("deep replacing \(replacedMolecule) with \(newMolecule)") - logUpdated(molecule: newMolecule) + logUpdated(moleculeId: newMolecule.id) changeList.append(newMolecule) } } catch { @@ -111,15 +113,29 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co return hasReplacement ? updatedRootMolecules : nil } - private func logUpdated(molecule: MoleculeModelProtocol) { - guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(molecule.id), + private func logUpdated(moleculeId: String) { + guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(moleculeId), let viewController = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { - debugLog("Missing the originating module \(molecule.id) creating this molecule!") + debugLog("Missing the originating module \(moleculeId) creating this molecule!") return } + previouslyReplacedIds.insert(moleculeId) + guard isPageShowing else { return } // Page has not been made visible yet. (Pulled and replaced from cache on load or background update.) Hold reporting until onPageShown. MVMCoreUILoggingHandler.shared()?.defaultLogPageUpdate(forController: viewController, from: module) } + public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) { + isPageShowing = true + debugLog("Page shown. Send molecule analytics for: \(previouslyReplacedIds)") + previouslyReplacedIds.forEach { id in + logUpdated(moleculeId: id) + } + } + + public func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?) { + isPageShowing = false + } + deinit { debugLog("deinit") } diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.h b/MVMCoreUI/Utility/MVMCoreUIUtility.h index c6959723..12c1aa34 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.h +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.h @@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN // The bundle for this framework + (nullable NSBundle *)bundleForMVMCoreUI; +/// The bundle for the VDS frameowrk. Handy for accessing VDS resources such as fonts. ++ (nullable NSBundle *)bundleForFonts; + // Returns the hardcoded string from the string file. + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key; diff --git a/MVMCoreUI/Utility/MVMCoreUIUtility.m b/MVMCoreUI/Utility/MVMCoreUIUtility.m index ad7366df..e725114d 100644 --- a/MVMCoreUI/Utility/MVMCoreUIUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUIUtility.m @@ -22,6 +22,10 @@ return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCoreUI"]; } ++ (nullable NSBundle *)bundleForFonts { + return [NSBundle bundleWithIdentifier:@"com.vzw.vds"]; +} + + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key { // Redirect key with relevant module. return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreUIUtility bundleForMVMCoreUI]];