Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git into feature/atomic-vds-checkbox
This commit is contained in:
commit
c7d2dcd655
@ -153,6 +153,9 @@
|
|||||||
444FB7C32821B76B00DFE692 /* TitleLockupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444FB7C22821B76B00DFE692 /* TitleLockupModel.swift */; };
|
444FB7C32821B76B00DFE692 /* TitleLockupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444FB7C22821B76B00DFE692 /* TitleLockupModel.swift */; };
|
||||||
4457904E27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */; };
|
4457904E27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */; };
|
||||||
4B002ACA2BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */; };
|
4B002ACA2BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */; };
|
||||||
|
4B3408A22C3873B0003BFABF /* CircularProgressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */; };
|
||||||
|
4B3408A42C3873E8003BFABF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */; };
|
||||||
|
4B53AF7B2C45BBBA00274685 /* GraphSizeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B53AF7A2C45BBBA00274685 /* GraphSizeProtocol.swift */; };
|
||||||
522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; };
|
522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; };
|
||||||
522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */; };
|
522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */; };
|
||||||
52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */; };
|
52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */; };
|
||||||
@ -771,6 +774,9 @@
|
|||||||
444FB7C22821B76B00DFE692 /* TitleLockupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLockupModel.swift; sourceTree = "<group>"; };
|
444FB7C22821B76B00DFE692 /* TitleLockupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLockupModel.swift; sourceTree = "<group>"; };
|
||||||
4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageRenderingMode+Extension.swift"; sourceTree = "<group>"; };
|
4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageRenderingMode+Extension.swift"; sourceTree = "<group>"; };
|
||||||
4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateDropdownEntryFieldModel+Extension.swift"; sourceTree = "<group>"; };
|
4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateDropdownEntryFieldModel+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBarModel.swift; sourceTree = "<group>"; };
|
||||||
|
4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = "<group>"; };
|
||||||
|
4B53AF7A2C45BBBA00274685 /* GraphSizeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphSizeProtocol.swift; sourceTree = "<group>"; };
|
||||||
522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = "<group>"; };
|
522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = "<group>"; };
|
||||||
522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinksModel.swift; sourceTree = "<group>"; };
|
522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinksModel.swift; sourceTree = "<group>"; };
|
||||||
52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinks.swift; sourceTree = "<group>"; };
|
52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinks.swift; sourceTree = "<group>"; };
|
||||||
@ -2311,8 +2317,11 @@
|
|||||||
94C2D9822386F3E30006CF46 /* Label */,
|
94C2D9822386F3E30006CF46 /* Label */,
|
||||||
31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */,
|
31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */,
|
||||||
0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */,
|
0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */,
|
||||||
|
4B53AF7A2C45BBBA00274685 /* GraphSizeProtocol.swift */,
|
||||||
D28A838223CCBD3F00DFE4FC /* WheelModel.swift */,
|
D28A838223CCBD3F00DFE4FC /* WheelModel.swift */,
|
||||||
943784F3236B77BB006A1E82 /* Wheel.swift */,
|
943784F3236B77BB006A1E82 /* Wheel.swift */,
|
||||||
|
4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */,
|
||||||
|
4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */,
|
||||||
943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */,
|
943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */,
|
||||||
0AE98BB623FF18E9004C5109 /* ArrowModel.swift */,
|
0AE98BB623FF18E9004C5109 /* ArrowModel.swift */,
|
||||||
0AE98BB423FF18D2004C5109 /* Arrow.swift */,
|
0AE98BB423FF18D2004C5109 /* Arrow.swift */,
|
||||||
@ -3008,6 +3017,7 @@
|
|||||||
D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */,
|
D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */,
|
||||||
D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */,
|
D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */,
|
||||||
D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */,
|
D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */,
|
||||||
|
4B3408A42C3873E8003BFABF /* CircularProgressBar.swift in Sources */,
|
||||||
0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */,
|
0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */,
|
||||||
EA17584E2BC9895A00A5C0D9 /* ButtonIcon.swift in Sources */,
|
EA17584E2BC9895A00A5C0D9 /* ButtonIcon.swift in Sources */,
|
||||||
D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */,
|
D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */,
|
||||||
@ -3028,6 +3038,7 @@
|
|||||||
AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */,
|
AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */,
|
||||||
AA37CBD52519072F0027344C /* Stars.swift in Sources */,
|
AA37CBD52519072F0027344C /* Stars.swift in Sources */,
|
||||||
942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */,
|
942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */,
|
||||||
|
4B3408A22C3873B0003BFABF /* CircularProgressBarModel.swift in Sources */,
|
||||||
8D8067D32444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift in Sources */,
|
8D8067D32444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift in Sources */,
|
||||||
8D4687E4242E2DF300802879 /* ListFourColumnDataUsageListItem.swift in Sources */,
|
8D4687E4242E2DF300802879 /* ListFourColumnDataUsageListItem.swift in Sources */,
|
||||||
D2874024249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift in Sources */,
|
D2874024249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift in Sources */,
|
||||||
@ -3104,6 +3115,7 @@
|
|||||||
D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */,
|
D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */,
|
||||||
D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */,
|
D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */,
|
||||||
C7F8012123E8303200396FBD /* ListRVWheel.swift in Sources */,
|
C7F8012123E8303200396FBD /* ListRVWheel.swift in Sources */,
|
||||||
|
4B53AF7B2C45BBBA00274685 /* GraphSizeProtocol.swift in Sources */,
|
||||||
BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */,
|
BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */,
|
||||||
D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */,
|
D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */,
|
||||||
EA7D81622B2B6E7F00D29F9E /* IconModel.swift in Sources */,
|
EA7D81622B2B6E7F00D29F9E /* IconModel.swift in Sources */,
|
||||||
|
|||||||
@ -53,4 +53,15 @@ open class BadgeModel: MoleculeModelProtocol {
|
|||||||
try container.encode(numberOfLines, forKey: .numberOfLines)
|
try container.encode(numberOfLines, forKey: .numberOfLines)
|
||||||
try container.encodeIfPresent(maxWidth, forKey: .maxWidth)
|
try container.encodeIfPresent(maxWidth, forKey: .maxWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
|
||||||
|
guard let model = model as? BadgeModel else { return false }
|
||||||
|
return self.backgroundColor == model.backgroundColor
|
||||||
|
&& self.fillColor == model.fillColor
|
||||||
|
&& self.numberOfLines == model.numberOfLines
|
||||||
|
&& self.text == model.text
|
||||||
|
&& self.surface == model.surface
|
||||||
|
&& self.accessibilityText == model.accessibilityText
|
||||||
|
&& self.maxWidth == model.maxWidth
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
128
MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift
Normal file
128
MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// CircularProgressBar.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Xi Zhang on 7/5/24.
|
||||||
|
// Copyright © 2024 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
@objcMembers open class CircularProgressBar: View, MVMCoreUIViewConstrainingProtocol {
|
||||||
|
|
||||||
|
var heightConstraint: NSLayoutConstraint?
|
||||||
|
var graphModel: CircularProgressBarModel? {
|
||||||
|
return model as? CircularProgressBarModel
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewWidth: CGFloat {
|
||||||
|
graphModel?.diameter ?? CGFloat(64)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var progressLayer = CAShapeLayer()
|
||||||
|
private var tracklayer = CAShapeLayer()
|
||||||
|
private var labelLayer = CATextLayer()
|
||||||
|
|
||||||
|
var progressColor: UIColor = UIColor.red
|
||||||
|
var trackColor: UIColor = UIColor.lightGray
|
||||||
|
|
||||||
|
// A path with which CAShapeLayer will be drawn on the screen
|
||||||
|
private var viewCGPath: CGPath? {
|
||||||
|
|
||||||
|
let width = viewWidth
|
||||||
|
let height = width
|
||||||
|
|
||||||
|
return UIBezierPath(arcCenter: CGPoint(x: width / 2.0, y: height / 2.0),
|
||||||
|
radius: (width - 1.5)/2,
|
||||||
|
startAngle: CGFloat(-0.5 * Double.pi),
|
||||||
|
endAngle: CGFloat(1.5 * Double.pi), clockwise: true).cgPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: setup
|
||||||
|
override open func setupView() {
|
||||||
|
super.setupView()
|
||||||
|
heightConstraint = heightAnchor.constraint(equalToConstant: 0)
|
||||||
|
heightConstraint?.isActive = true
|
||||||
|
widthAnchor.constraint(equalTo: heightAnchor).isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
|
|
||||||
|
super.set(with: model, delegateObject, additionalData)
|
||||||
|
guard let model = model as? CircularProgressBarModel else { return }
|
||||||
|
|
||||||
|
// set background color
|
||||||
|
backgroundColor = model.backgroundColor?.uiColor ?? UIColor.clear
|
||||||
|
|
||||||
|
configureProgressViewToBeCircular()
|
||||||
|
|
||||||
|
// set progress color
|
||||||
|
progressColor = model.color?.uiColor ?? .red
|
||||||
|
progressLayer.strokeColor = progressColor.cgColor
|
||||||
|
|
||||||
|
// set track color
|
||||||
|
trackColor = model.trackColor?.uiColor ?? .lightGray
|
||||||
|
tracklayer.strokeColor = trackColor.cgColor
|
||||||
|
|
||||||
|
// show circular progress view with animation.
|
||||||
|
showProgressWithAnimation(duration: graphModel?.duration ?? 0, value: Float(graphModel?.percent ?? 0) / 100)
|
||||||
|
|
||||||
|
// show progress percentage label.
|
||||||
|
if let drawText = model.drawText, drawText {
|
||||||
|
showProgressPercentage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func configureProgressViewToBeCircular() {
|
||||||
|
let lineWidth = graphModel?.lineWidth ?? 4.0
|
||||||
|
|
||||||
|
self.drawShape(using: tracklayer, lineWidth: lineWidth)
|
||||||
|
self.drawShape(using: progressLayer, lineWidth: lineWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func drawShape(using shape: CAShapeLayer, lineWidth: CGFloat) {
|
||||||
|
shape.path = self.viewCGPath
|
||||||
|
shape.fillColor = UIColor.clear.cgColor
|
||||||
|
shape.lineWidth = lineWidth
|
||||||
|
self.layer.addSublayer(shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
// value range is [0,1]
|
||||||
|
private func showProgressWithAnimation(duration: TimeInterval, value: Float) {
|
||||||
|
let animation = CABasicAnimation(keyPath: "strokeEnd")
|
||||||
|
animation.duration = duration
|
||||||
|
|
||||||
|
animation.fromValue = 0 //start animation at point 0
|
||||||
|
animation.toValue = value //end animation at point specified
|
||||||
|
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||||
|
progressLayer.strokeEnd = CGFloat(value)
|
||||||
|
progressLayer.add(animation, forKey: "animateCircle")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showProgressPercentage() {
|
||||||
|
|
||||||
|
let percent = graphModel?.percent ?? 0
|
||||||
|
let percentLen = String(percent).count
|
||||||
|
|
||||||
|
// configure attributed string for progress percentage.
|
||||||
|
let attributedString = NSMutableAttributedString(string: String(percent) + "%")
|
||||||
|
// percent value
|
||||||
|
attributedString.setAttributes([NSAttributedString.Key.font: MFStyler.fontBoldTitleLarge()], range: NSMakeRange(0, percentLen))
|
||||||
|
// % symbol
|
||||||
|
attributedString.setAttributes([NSAttributedString.Key.font: MFStyler.fontBoldBodyLarge()], range: NSMakeRange(percentLen, 1))
|
||||||
|
|
||||||
|
// show progress percentage in a text layer
|
||||||
|
let width = viewWidth
|
||||||
|
let height = width
|
||||||
|
labelLayer.string = attributedString
|
||||||
|
labelLayer.frame = CGRectMake((width - CGFloat(percentLen * 20))/2, (height - 30)/2, 60, 30)
|
||||||
|
self.layer.addSublayer(labelLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: MVMCoreUIViewConstrainingProtocol
|
||||||
|
public func needsToBeConstrained() -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
119
MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift
Normal file
119
MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
//
|
||||||
|
// CircularProgressBarModel.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// https://oneconfluence.verizon.com/display/MFD/Circular+Progress+Tracker
|
||||||
|
//
|
||||||
|
// Created by Xi Zhang on 7/5/24.
|
||||||
|
// Copyright © 2024 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class CircularProgressBarModel: GraphSizeBase, MoleculeModelProtocol {
|
||||||
|
|
||||||
|
public static var identifier: String = "circularProgress"
|
||||||
|
public var id: String = UUID().uuidString
|
||||||
|
|
||||||
|
public var percent: Int = 0
|
||||||
|
public var diameter: CGFloat? = 64
|
||||||
|
public var lineWidth: CGFloat? = 4
|
||||||
|
public var duration : Double? = 0
|
||||||
|
public var color: Color? = Color(uiColor: UIColor.mfGet(forHex: "#007AB8"))
|
||||||
|
public var trackColor: Color? = Color(uiColor: .mvmCoolGray3)
|
||||||
|
public var drawText: Bool? = true
|
||||||
|
public var backgroundColor: Color? = Color(uiColor: UIColor.clear)
|
||||||
|
|
||||||
|
public override init() {
|
||||||
|
super.init()
|
||||||
|
updateSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case moleculeName
|
||||||
|
case percent
|
||||||
|
case size
|
||||||
|
case diameter
|
||||||
|
case lineWidth
|
||||||
|
case duration
|
||||||
|
case color
|
||||||
|
case trackColor
|
||||||
|
case drawText
|
||||||
|
case backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(from decoder: Decoder) throws {
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
||||||
|
|
||||||
|
percent = try typeContainer.decode(Int.self, forKey: .percent)
|
||||||
|
|
||||||
|
if let size = try typeContainer.decodeIfPresent(GraphSize.self, forKey: .size) {
|
||||||
|
self.size = size
|
||||||
|
}
|
||||||
|
updateSize()
|
||||||
|
|
||||||
|
if let diameter = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .diameter) {
|
||||||
|
self.diameter = diameter
|
||||||
|
}
|
||||||
|
|
||||||
|
if let lineWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .lineWidth) {
|
||||||
|
self.lineWidth = lineWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
if let duration = try typeContainer.decodeIfPresent(Double.self, forKey: .duration) {
|
||||||
|
self.duration = duration
|
||||||
|
}
|
||||||
|
|
||||||
|
if let drawText = try typeContainer.decodeIfPresent(Bool.self, forKey: .drawText) {
|
||||||
|
self.drawText = drawText
|
||||||
|
}
|
||||||
|
|
||||||
|
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) {
|
||||||
|
self.color = color
|
||||||
|
}
|
||||||
|
|
||||||
|
if let trackColor = try typeContainer.decodeIfPresent(Color.self, forKey: .trackColor) {
|
||||||
|
self.trackColor = trackColor
|
||||||
|
}
|
||||||
|
|
||||||
|
if let backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) {
|
||||||
|
self.backgroundColor = backgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
try container.encode(id, forKey: .id)
|
||||||
|
try container.encode(moleculeName, forKey: .moleculeName)
|
||||||
|
try container.encode(percent, forKey: .percent)
|
||||||
|
try container.encodeIfPresent(size, forKey: .size)
|
||||||
|
try container.encodeIfPresent(diameter, forKey: .diameter)
|
||||||
|
try container.encodeIfPresent(lineWidth, forKey: .lineWidth)
|
||||||
|
try container.encodeIfPresent(duration, forKey: .duration)
|
||||||
|
try container.encodeIfPresent(drawText, forKey: .drawText)
|
||||||
|
try container.encodeIfPresent(trackColor, forKey: .trackColor)
|
||||||
|
try container.encodeIfPresent(color, forKey: .color)
|
||||||
|
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func updateSize() {
|
||||||
|
switch size {
|
||||||
|
case .small:
|
||||||
|
diameter = MFSizeObject(standardSize: 64)?.getValueBasedOnApplicationWidth() ?? 64
|
||||||
|
lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4
|
||||||
|
break
|
||||||
|
case .medium:
|
||||||
|
diameter = MFSizeObject(standardSize: 84)?.getValueBasedOnApplicationWidth() ?? 84
|
||||||
|
lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4
|
||||||
|
break
|
||||||
|
case .large:
|
||||||
|
diameter = MFSizeObject(standardSize: 124)?.getValueBasedOnApplicationWidth() ?? 124
|
||||||
|
lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
MVMCoreUI/Atomic/Atoms/Views/GraphSizeProtocol.swift
Normal file
29
MVMCoreUI/Atomic/Atoms/Views/GraphSizeProtocol.swift
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// GraphSizeProtocol.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Xi Zhang on 7/15/24.
|
||||||
|
// Copyright © 2024 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum GraphSize: String, Codable {
|
||||||
|
case small, medium, large
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol GraphSizeProtocol {
|
||||||
|
var size: GraphSize { get set }
|
||||||
|
func updateSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GraphSizeBase: GraphSizeProtocol {
|
||||||
|
public var size: GraphSize = .small {
|
||||||
|
didSet {
|
||||||
|
updateSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateSize() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,15 +8,11 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public enum GraphSize: String, Codable {
|
|
||||||
case small, medium, large
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum GraphStyle: String, Codable {
|
public enum GraphStyle: String, Codable {
|
||||||
case unlimited, safetyMode
|
case unlimited, safetyMode
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WheelModel: MoleculeModelProtocol {
|
public class WheelModel: GraphSizeBase, MoleculeModelProtocol {
|
||||||
|
|
||||||
public static var identifier: String = "wheel"
|
public static var identifier: String = "wheel"
|
||||||
public var id: String = UUID().uuidString
|
public var id: String = UUID().uuidString
|
||||||
@ -27,11 +23,6 @@ public class WheelModel: MoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var size: GraphSize = .small {
|
|
||||||
didSet {
|
|
||||||
updateSize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public var diameter: CGFloat = 24
|
public var diameter: CGFloat = 24
|
||||||
public var lineWidth: CGFloat = 5
|
public var lineWidth: CGFloat = 5
|
||||||
public var clockwise: Bool = true
|
public var clockwise: Bool = true
|
||||||
@ -39,7 +30,8 @@ public class WheelModel: MoleculeModelProtocol {
|
|||||||
public var colors = [Color]()
|
public var colors = [Color]()
|
||||||
public var backgroundColor: Color?
|
public var backgroundColor: Color?
|
||||||
|
|
||||||
public init() {
|
public override init() {
|
||||||
|
super.init()
|
||||||
updateStyle()
|
updateStyle()
|
||||||
updateSize()
|
updateSize()
|
||||||
}
|
}
|
||||||
@ -58,6 +50,7 @@ public class WheelModel: MoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
required public init(from decoder: Decoder) throws {
|
required public init(from decoder: Decoder) throws {
|
||||||
|
super.init()
|
||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
||||||
|
|
||||||
@ -123,7 +116,7 @@ public class WheelModel: MoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSize() {
|
public override func updateSize() {
|
||||||
switch size {
|
switch size {
|
||||||
case .small:
|
case .small:
|
||||||
diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20
|
diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20
|
||||||
|
|||||||
@ -108,8 +108,7 @@ extension TabsListItemModel: AddMolecules {
|
|||||||
public func moleculesToAdd() -> AddMolecules.AddParameters? {
|
public func moleculesToAdd() -> AddMolecules.AddParameters? {
|
||||||
guard addedMolecules == nil else { return nil }
|
guard addedMolecules == nil else { return nil }
|
||||||
let index = tabs.selectedIndex
|
let index = tabs.selectedIndex
|
||||||
guard molecules.count >= index else { return nil }
|
guard let addedMolecules = molecules[safe: index] else { return nil }
|
||||||
let addedMolecules = molecules[index]
|
|
||||||
self.addedMolecules = addedMolecules
|
self.addedMolecules = addedMolecules
|
||||||
return (addedMolecules, .below)
|
return (addedMolecules, .below)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,9 @@ open class Carousel: View {
|
|||||||
/// The models for the molecules.
|
/// The models for the molecules.
|
||||||
public var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]?
|
public var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]?
|
||||||
|
|
||||||
|
/// A list of currently registered cells.
|
||||||
|
public var registeredMoleculeIds: [String]?
|
||||||
|
|
||||||
/// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%.
|
/// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%.
|
||||||
public var itemAlignment = UICollectionView.ScrollPosition.left
|
public var itemAlignment = UICollectionView.ScrollPosition.left
|
||||||
|
|
||||||
@ -174,9 +177,7 @@ open class Carousel: View {
|
|||||||
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] [\(ObjectIdentifier(self).hashValue)]\noriginal model: \(originalModel?.debugDescription ?? "none")\nnew model: \(model)")
|
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] [\(ObjectIdentifier(self).hashValue)]\noriginal model: \(originalModel?.debugDescription ?? "none")\nnew model: \(model)")
|
||||||
|
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
if let originalModel, carouselModel.isDeeplyVisuallyEquivalent(to: originalModel),
|
if hasSameCellRegistration(with: carouselModel, delegateObject: delegateObject) {
|
||||||
originalModel.visibleMolecules.isVisuallyEquivalent(to: molecules ?? []) // Since the carousel model's children are in place replaced and we do not have a deep copy of this model tree, add in this hack to check if the prior captured carousel items match the newly visible ones.
|
|
||||||
{
|
|
||||||
// Prevents a carousel reset while still updating the cell backing data through reconfigureItems.
|
// Prevents a carousel reset while still updating the cell backing data through reconfigureItems.
|
||||||
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] Model is visually equivalent. Skipping rebuild...")
|
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] Model is visually equivalent. Skipping rebuild...")
|
||||||
prepareMolecules(with: carouselModel)
|
prepareMolecules(with: carouselModel)
|
||||||
@ -209,6 +210,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
registerCells(with: carouselModel, delegateObject: delegateObject)
|
registerCells(with: carouselModel, delegateObject: delegateObject)
|
||||||
prepareMolecules(with: carouselModel)
|
prepareMolecules(with: carouselModel)
|
||||||
|
pageIndex = 0
|
||||||
FormValidator.setupValidation(for: carouselModel, delegate: delegateObject?.formHolderDelegate)
|
FormValidator.setupValidation(for: carouselModel, delegate: delegateObject?.formHolderDelegate)
|
||||||
|
|
||||||
setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject)
|
setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject)
|
||||||
@ -249,8 +251,6 @@ open class Carousel: View {
|
|||||||
} else {
|
} else {
|
||||||
loop = false
|
loop = false
|
||||||
}
|
}
|
||||||
|
|
||||||
pageIndex = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func reset() {
|
open override func reset() {
|
||||||
@ -284,12 +284,29 @@ open class Carousel: View {
|
|||||||
|
|
||||||
/// Registers the cells with the collection view
|
/// Registers the cells with the collection view
|
||||||
func registerCells(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) {
|
func registerCells(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
var registeredIds = [String]()
|
||||||
for molecule in carouselModel.molecules {
|
for molecule in carouselModel.visibleMolecules {
|
||||||
if let info = getMoleculeInfo(with: molecule, delegateObject: delegateObject) {
|
if let info = getMoleculeInfo(with: molecule, delegateObject: delegateObject) {
|
||||||
collectionView.register(info.class, forCellWithReuseIdentifier: info.identifier)
|
collectionView.register(info.class, forCellWithReuseIdentifier: info.identifier)
|
||||||
|
registeredIds.append(info.identifier)
|
||||||
|
} else {
|
||||||
|
registeredIds.append(molecule.moleculeName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
registeredMoleculeIds = registeredIds
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasSameCellRegistration(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) -> Bool {
|
||||||
|
guard let registeredMoleculeIds else { return false }
|
||||||
|
|
||||||
|
let incomingIds = carouselModel.visibleMolecules.map { molecule in
|
||||||
|
if let info = getMoleculeInfo(with: molecule, delegateObject: delegateObject) {
|
||||||
|
return info.identifier
|
||||||
|
} else {
|
||||||
|
return molecule.moleculeName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return incomingIds == registeredMoleculeIds
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -361,7 +378,7 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func trackSwipeActionAnalyticsforIndex(_ index : Int){
|
func trackSwipeActionAnalyticsforIndex(_ index : Int){
|
||||||
guard let itemModel = molecules?[index],
|
guard let itemModel = molecules?[safe:index],
|
||||||
let viewControllerObject = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { return }
|
let viewControllerObject = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { return }
|
||||||
MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: viewControllerObject, actionInformation: itemModel.toJSON(), additionalData: nil)
|
MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: viewControllerObject, actionInformation: itemModel.toJSON(), additionalData: nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,8 +52,8 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController, Rotor
|
|||||||
bottomView.updateView(width)
|
bottomView.updateView(width)
|
||||||
showFooter(width)
|
showFooter(width)
|
||||||
}
|
}
|
||||||
tableView.visibleCells.forEach { cell in
|
MVMCoreUIUtility.findParentViews(by: (UITableViewCell & MVMCoreViewProtocol).self, views: tableView.subviews).forEach { view in
|
||||||
(cell as? MVMCoreViewProtocol)?.updateView(width)
|
view.updateView(width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import MVMCore
|
import MVMCore
|
||||||
|
import Combine
|
||||||
|
|
||||||
@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, ActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol, PageProtocol, PageBehaviorHandlerProtocol {
|
@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, ActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol, PageProtocol, PageBehaviorHandlerProtocol {
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ import MVMCore
|
|||||||
public var behaviors: [PageBehaviorProtocol]?
|
public var behaviors: [PageBehaviorProtocol]?
|
||||||
|
|
||||||
public var needsUpdateUI = false
|
public var needsUpdateUI = false
|
||||||
private var observingForResponses: NSObjectProtocol?
|
private var observingForResponses: AnyCancellable?
|
||||||
private var initialLoadFinished = false
|
private var initialLoadFinished = false
|
||||||
public var isFirstRender = true
|
public var isFirstRender = true
|
||||||
public var previousScreenSize = CGSize.zero
|
public var previousScreenSize = CGSize.zero
|
||||||
@ -66,9 +67,28 @@ import MVMCore
|
|||||||
(pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0)
|
(pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0)
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
observingForResponses = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil, queue: pageUpdateQueue) { [weak self] notification in
|
observingForResponses = NotificationCenter.default.publisher(for: NSNotification.Name(rawValue: NotificationResponseLoaded))
|
||||||
self?.responseJSONUpdated(notification: notification)
|
.receive(on: self.pageUpdateQueue) // Background serial queue.
|
||||||
}
|
.compactMap { [weak self] notification in
|
||||||
|
self?.pullUpdates(from: notification) ?? nil
|
||||||
|
}
|
||||||
|
// Merge all page and module updates into one update event.
|
||||||
|
.scan((nil, nil, nil)) { accumulator, next in
|
||||||
|
// Always take the latest page and the latest modules with same key.
|
||||||
|
return (next.0 ?? accumulator.0, next.1 ?? accumulator.1, next.2?.mergingRight(accumulator.2 ?? [:]))
|
||||||
|
}
|
||||||
|
// Delay allowing the previous model update to settle before triggering a re-render.
|
||||||
|
.throttle(for: .seconds(0.25), scheduler: RunLoop.main, latest: true)
|
||||||
|
.sink { [weak self] (pageUpdates: [String : Any]?, pageModel: PageModelProtocol?, moduleUpdates: [String : Any]?) in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if let pageUpdates, pageModel != nil {
|
||||||
|
self.loadObject?.pageJSON = pageUpdates
|
||||||
|
}
|
||||||
|
let mergedModuleUpdates = (loadObject?.modulesJSON ?? [:]).mergingLeft(moduleUpdates ?? [:])
|
||||||
|
self.loadObject?.modulesJSON = mergedModuleUpdates
|
||||||
|
self.debugLog("Applying async update page model \(pageModel.debugDescription) and modules \(mergedModuleUpdates.keys) to page.")
|
||||||
|
self.handleNewData(pageModel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func stopObservingForResponseJSONUpdates() {
|
open func stopObservingForResponseJSONUpdates() {
|
||||||
@ -77,6 +97,31 @@ import MVMCore
|
|||||||
self.observingForResponses = nil
|
self.observingForResponses = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pullUpdates(from notification: Notification) -> (pageUpdates: [String : Any]?, pageModel: PageModelProtocol?, moduleUpdates: [String : Any]?)? {
|
||||||
|
// Get the page data.
|
||||||
|
let pageUpdates = extractInterestedPageType(from: notification.userInfo?.optionalDictionaryForKey(KeyPageMap) ?? [:])
|
||||||
|
// Convert the page data into a new model.
|
||||||
|
var pageModel: PageModelProtocol? = nil
|
||||||
|
if let pageUpdates {
|
||||||
|
do {
|
||||||
|
// TODO: Rewiring to parse from plain JSON rather than this protocol indirection.
|
||||||
|
pageModel = try (self as? any TemplateProtocol & PageBehaviorHandlerProtocol & MVMCoreViewControllerProtocol)?.parseTemplate(pageJSON: pageUpdates)
|
||||||
|
} catch {
|
||||||
|
if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: self.pageType))") {
|
||||||
|
MVMCoreLoggingHandler.shared()?.addError(toLog: coreError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get the module data.
|
||||||
|
let moduleUpdates = extractInterestedModules(from: notification.userInfo?.optionalDictionaryForKey(KeyModuleMap) ?? [:])
|
||||||
|
debugLog("Receiving page \(pageModel?.pageType ?? "none") & \(moduleUpdates?.keys.description ?? "none") modules from \((notification.userInfo?["MVMCoreLoadObject"] as? MVMCoreLoadObject)?.requestParameters?.url?.absoluteString ?? "")")
|
||||||
|
|
||||||
|
guard (pageUpdates != nil && pageModel != nil) || (moduleUpdates != nil && moduleUpdates!.count > 0) else { return nil }
|
||||||
|
|
||||||
|
// Bundle the transformations.
|
||||||
|
return (pageUpdates, pageModel, moduleUpdates)
|
||||||
|
}
|
||||||
|
|
||||||
open func pagesToListenFor() -> [String]? {
|
open func pagesToListenFor() -> [String]? {
|
||||||
guard let pageType = loadObject?.pageType else { return nil }
|
guard let pageType = loadObject?.pageType else { return nil }
|
||||||
return [pageType]
|
return [pageType]
|
||||||
@ -88,51 +133,22 @@ import MVMCore
|
|||||||
return requestModules + behaviorModules
|
return requestModules + behaviorModules
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc open func responseJSONUpdated(notification: Notification) {
|
private func extractInterestedPageType(from pageMap: [String: Any]) -> [String: Any]? {
|
||||||
// Checks for a page we are listening for.
|
guard let pageType = pagesToListenFor()?.first(where: { pageTypeListened -> Bool in
|
||||||
var hasDataUpdate = false
|
guard let page = pageMap.optionalDictionaryForKey(pageTypeListened),
|
||||||
var pageModel: PageModelProtocol? = nil
|
|
||||||
if let pagesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyPageMap),
|
|
||||||
let loadObject,
|
|
||||||
let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in
|
|
||||||
guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened),
|
|
||||||
let pageType = page.optionalStringForKey(KeyPageType),
|
let pageType = page.optionalStringForKey(KeyPageType),
|
||||||
pageType == pageTypeListened
|
pageType == pageTypeListened
|
||||||
else { return false }
|
else { return false }
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}) {
|
}) else { return nil }
|
||||||
hasDataUpdate = true
|
return pageMap.optionalDictionaryForKey(pageType)
|
||||||
loadObject.pageJSON = pagesLoaded.optionalDictionaryForKey(pageType)
|
}
|
||||||
|
|
||||||
// Separate page updates from the module updates to avoid unecessary resets to behaviors and full re-renders.
|
private func extractInterestedModules(from moduleMap: [String: Any]) -> [String: Any]? {
|
||||||
do {
|
guard let modulesListened = modulesToListenFor() else { return nil }
|
||||||
pageModel = try parsePageJSON(loadObject: loadObject)
|
return moduleMap.filter { (key: String, value: Any) in
|
||||||
} catch {
|
modulesListened.contains { $0 == key }
|
||||||
if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") {
|
|
||||||
MVMCoreLoggingHandler.shared()?.addError(toLog: coreError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for modules we are listening for.
|
|
||||||
if let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap),
|
|
||||||
let modulesListened = modulesToListenFor() {
|
|
||||||
for moduleName in modulesListened {
|
|
||||||
if let module = modulesLoaded.optionalDictionaryForKey(moduleName) {
|
|
||||||
hasDataUpdate = true
|
|
||||||
var currentModules = loadObject?.modulesJSON ?? [:]
|
|
||||||
currentModules.updateValue(module, forKey: moduleName)
|
|
||||||
loadObject?.modulesJSON = currentModules
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard hasDataUpdate else { return }
|
|
||||||
|
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
|
||||||
self.handleNewData(pageModel)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>) -> Bool {
|
open func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>) -> Bool {
|
||||||
|
|||||||
@ -67,6 +67,7 @@ open class CoreUIModelMapping: ModelMapping {
|
|||||||
ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self)
|
ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self)
|
||||||
ModelRegistry.register(handler: Line.self, for: LineModel.self)
|
ModelRegistry.register(handler: Line.self, for: LineModel.self)
|
||||||
ModelRegistry.register(handler: Wheel.self, for: WheelModel.self)
|
ModelRegistry.register(handler: Wheel.self, for: WheelModel.self)
|
||||||
|
ModelRegistry.register(handler: CircularProgressBar.self, for: CircularProgressBarModel.self)
|
||||||
ModelRegistry.register(handler: Toggle.self, for: ToggleModel.self)
|
ModelRegistry.register(handler: Toggle.self, for: ToggleModel.self)
|
||||||
ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self)
|
ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self)
|
||||||
ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self)
|
ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self)
|
||||||
|
|||||||
@ -60,6 +60,16 @@ public extension MVMCoreUIUtility {
|
|||||||
return findViews(by: type, views: queue, excludedViews: excludedViews) + matching
|
return findViews(by: type, views: queue, excludedViews: excludedViews) + matching
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func findParentViews<T>(by type: T.Type, views: [UIView]) -> [T] {
|
||||||
|
return views.reduce(into: [T]()) { matchingViews, view in
|
||||||
|
if let view = view as? T {
|
||||||
|
return matchingViews.append(view) // If this view is the type stop here and return, ignoring its children.
|
||||||
|
}
|
||||||
|
// Otherwise check downstream.
|
||||||
|
matchingViews += findParentViews(by: type, views: view.subviews)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
static func visibleNavigationBarStlye() -> NavigationItemStyle? {
|
static func visibleNavigationBarStlye() -> NavigationItemStyle? {
|
||||||
if let navController = NavigationController.navigationController(),
|
if let navController = NavigationController.navigationController(),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user