Merge branch 'release/20_0_0' into 'develop'

release/20_0_0 hotfix merge

Co-authored-by: Hedden, Kyle Matthew <kyle.hedden@verizonwireless.com>
Co-authored-by: Matt Bruce <matt.bruce@verizon.com>
Co-authored-by: Subramaniam, Ramya <ramya.subramaniam@one.verizon.com>
Co-authored-by: Bruce, Matt R <matt.bruce@one.verizon.com>

See merge request https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui/-/merge_requests/1128
This commit is contained in:
Pfeil, Scott Robert 2024-06-26 14:42:50 +00:00
commit eb78d507d6
7 changed files with 40 additions and 41 deletions

View File

@ -364,8 +364,8 @@ extension Label {
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
guard let abstractContainer = label.abstractTextContainer() else { return CGRect() } guard let abstractContainer = label.abstractTextContainer() else { return CGRect() }
let textContainer = abstractContainer.0 let textContainer = abstractContainer.textContainer
let layoutManager = abstractContainer.1 let layoutManager = abstractContainer.layoutManager
var glyphRange = NSRange() var glyphRange = NSRange()
@ -374,36 +374,6 @@ extension Label {
return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer) 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 // MARK: - Atomization

View File

@ -307,6 +307,7 @@ open class Carousel: View {
pagingView?.removeFromSuperview() pagingView?.removeFromSuperview()
bottomPin?.isActive = false bottomPin?.isActive = false
pagingView = nil
guard var pagingView = view else { guard var pagingView = view else {
bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor)

View File

@ -83,6 +83,9 @@ import Foundation
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let defaultPageType = Self.defaultPageType() { if let defaultPageType = Self.defaultPageType() {
pageType = try typeContainer.decodeIfPresent(String.self, forKey: .pageType) ?? defaultPageType pageType = try typeContainer.decodeIfPresent(String.self, forKey: .pageType) ?? defaultPageType
if pageType.isEmpty {
pageType = defaultPageType
}
} else { } else {
pageType = try typeContainer.decode(String.self, forKey: .pageType) pageType = try typeContainer.decode(String.self, forKey: .pageType)
} }

View File

@ -52,18 +52,20 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController, Rotor
bottomView.updateView(width) bottomView.updateView(width)
showFooter(width) showFooter(width)
} }
tableView.reloadData() tableView.visibleCells.forEach { cell in
(cell as? MVMCoreViewProtocol)?.updateView(width)
}
} }
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) { open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
let isFirstRender = self.isFirstRender
super.updateUI(for: molecules) super.updateUI(for: molecules)
guard molecules == nil else { return } guard molecules == nil else { return }
createViewForTableHeader() createViewForTableHeader()
createViewForTableFooter() 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() { override open func viewDidLoad() {

View File

@ -20,7 +20,7 @@ public class ReplaceableMoleculeBehaviorModel: PageBehaviorModelProtocol {
} }
} }
public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, CoreLogging { public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, PageVisibilityBehavior, CoreLogging {
public var loggingPrefix: String { public var loggingPrefix: String {
"\(self) \(ObjectIdentifier(self).hashValue) \(moleculeIds.prefix(3)) \(moleculeIds.count > 3 ? "+ \(moleculeIds.count - 3) more" : ""):\n" "\(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) String(describing: Self.self)
} }
var isPageShowing = false
var previouslyReplacedIds = Set<String>()
var moleculeIds: [String] var moleculeIds: [String]
public var modulesToListenFor: [String] public var modulesToListenFor: [String]
private var observingForResponses: NSObjectProtocol? private var observingForResponses: NSObjectProtocol?
@ -75,7 +77,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
return rootMolecule return rootMolecule
} }
debugLog("top replacing \(rootMolecule) with \(updatedMolecule)") debugLog("top replacing \(rootMolecule) with \(updatedMolecule)")
logUpdated(molecule: updatedMolecule) logUpdated(moleculeId: updatedMolecule.id)
changeList.append(updatedMolecule) changeList.append(updatedMolecule)
return updatedMolecule return updatedMolecule
} }
@ -92,7 +94,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
return return
} }
debugLog("deep replacing \(replacedMolecule) with \(newMolecule)") debugLog("deep replacing \(replacedMolecule) with \(newMolecule)")
logUpdated(molecule: newMolecule) logUpdated(moleculeId: newMolecule.id)
changeList.append(newMolecule) changeList.append(newMolecule)
} }
} catch { } catch {
@ -111,15 +113,29 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
return hasReplacement ? updatedRootMolecules : nil return hasReplacement ? updatedRootMolecules : nil
} }
private func logUpdated(molecule: MoleculeModelProtocol) { private func logUpdated(moleculeId: String) {
guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(molecule.id), guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(moleculeId),
let viewController = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { 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 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) 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 { deinit {
debugLog("deinit") debugLog("deinit")
} }

View File

@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN
// The bundle for this framework // The bundle for this framework
+ (nullable NSBundle *)bundleForMVMCoreUI; + (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. // Returns the hardcoded string from the string file.
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key; + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key;

View File

@ -22,6 +22,10 @@
return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCoreUI"]; return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCoreUI"];
} }
+ (nullable NSBundle *)bundleForFonts {
return [NSBundle bundleWithIdentifier:@"com.vzw.vds"];
}
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key { + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key {
// Redirect key with relevant module. // Redirect key with relevant module.
return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreUIUtility bundleForMVMCoreUI]]; return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreUIUtility bundleForMVMCoreUI]];