Merge branch 'develop' into feature/list_storelocator

This commit is contained in:
Lekshmi S 2020-10-26 10:26:25 +05:30
commit 849381ae6f
48 changed files with 1116 additions and 256 deletions

View File

@ -199,6 +199,8 @@
94CA227D24058534002D6750 /* VerizonNHGeDS-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 94CA227924058533002D6750 /* VerizonNHGeDS-Regular.otf */; };
94CA227E24058534002D6750 /* VerizonNHGeDS-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 94CA227A24058533002D6750 /* VerizonNHGeDS-Bold.otf */; };
94F6516D2437954100631BF9 /* Tabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F6516C2437954100631BF9 /* Tabs.swift */; };
AA07EA912510A442009A2AE3 /* StarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA07EA902510A442009A2AE3 /* StarModel.swift */; };
AA07EA932510A451009A2AE3 /* Star.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA07EA922510A451009A2AE3 /* Star.swift */; };
AA0A257824766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0A257724766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift */; };
AA0A257A24766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0A257924766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift */; };
AA104AC724472DB0004D2810 /* HeadersH1Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA104AC624472DB0004D2810 /* HeadersH1Button.swift */; };
@ -217,6 +219,8 @@
AA2AD118244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA2AD117244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift */; };
AA3561AC24C9684400452EB1 /* ListRightVariableRightCaretAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3561AB24C9684400452EB1 /* ListRightVariableRightCaretAllTextAndLinksModel.swift */; };
AA3561AE24C96B9000452EB1 /* ListRightVariableRightCaretAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3561AD24C96B9000452EB1 /* ListRightVariableRightCaretAllTextAndLinks.swift */; };
AA37CBD3251907200027344C /* StarsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA37CBD2251907200027344C /* StarsModel.swift */; };
AA37CBD52519072F0027344C /* Stars.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA37CBD42519072F0027344C /* Stars.swift */; };
AA45AA0B24BF0263007A6EA7 /* LockUpsPlanNamesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA45AA0A24BF0263007A6EA7 /* LockUpsPlanNamesModel.swift */; };
AA45AA0D24BF0276007A6EA7 /* LockUpsPlanNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA45AA0C24BF0276007A6EA7 /* LockUpsPlanNames.swift */; };
AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */; };
@ -494,6 +498,8 @@
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A99E23E07F8A000B42E6 /* PillButton.swift */; };
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */; };
D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */; };
D2EAC0A02541EFFB00AA9495 /* LineDecorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EAC09F2541EFFB00AA9495 /* LineDecorationView.swift */; };
D2EAC0A42541F00F00AA9495 /* CarouselCollectionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EAC0A32541F00F00AA9495 /* CarouselCollectionLayout.swift */; };
D2EC7BD52527B7A600F540AF /* MoleculeSectionHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */; };
D2EC7BD92527B7CF00F540AF /* MoleculeSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */; };
D2EC7BDD2527B83700F540AF /* SectionHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */; };
@ -707,6 +713,8 @@
94CA227A24058533002D6750 /* VerizonNHGeDS-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeDS-Bold.otf"; sourceTree = "<group>"; };
94CA227B24058533002D6750 /* VerizonNHGeTX-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeTX-Regular.otf"; sourceTree = "<group>"; };
94F6516C2437954100631BF9 /* Tabs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tabs.swift; sourceTree = "<group>"; };
AA07EA902510A442009A2AE3 /* StarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarModel.swift; sourceTree = "<group>"; };
AA07EA922510A451009A2AE3 /* Star.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Star.swift; sourceTree = "<group>"; };
AA0A257724766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretBodyTextModel.swift; sourceTree = "<group>"; };
AA0A257924766CA200862F64 /* ListLeftVariableIconWithRightCaretBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretBodyText.swift; sourceTree = "<group>"; };
AA104AC624472DB0004D2810 /* HeadersH1Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH1Button.swift; sourceTree = "<group>"; };
@ -725,6 +733,8 @@
AA2AD117244EE48C00BBFFE3 /* ListDeviceComplexLinkMediumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexLinkMediumModel.swift; sourceTree = "<group>"; };
AA3561AB24C9684400452EB1 /* ListRightVariableRightCaretAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableRightCaretAllTextAndLinksModel.swift; sourceTree = "<group>"; };
AA3561AD24C96B9000452EB1 /* ListRightVariableRightCaretAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableRightCaretAllTextAndLinks.swift; sourceTree = "<group>"; };
AA37CBD2251907200027344C /* StarsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarsModel.swift; sourceTree = "<group>"; };
AA37CBD42519072F0027344C /* Stars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stars.swift; sourceTree = "<group>"; };
AA45AA0A24BF0263007A6EA7 /* LockUpsPlanNamesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockUpsPlanNamesModel.swift; sourceTree = "<group>"; };
AA45AA0C24BF0276007A6EA7 /* LockUpsPlanNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockUpsPlanNames.swift; sourceTree = "<group>"; };
AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDividerModel.swift; sourceTree = "<group>"; };
@ -1003,6 +1013,8 @@
D2E2A99E23E07F8A000B42E6 /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = "<group>"; };
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModelProtocol.swift; sourceTree = "<group>"; };
D2E2A9A223E096B1000B42E6 /* DisableableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableableModelProtocol.swift; sourceTree = "<group>"; };
D2EAC09F2541EFFB00AA9495 /* LineDecorationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineDecorationView.swift; sourceTree = "<group>"; };
D2EAC0A32541F00F00AA9495 /* CarouselCollectionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselCollectionLayout.swift; sourceTree = "<group>"; };
D2EC7BD42527B7A600F540AF /* MoleculeSectionHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeaderModel.swift; sourceTree = "<group>"; };
D2EC7BD82527B7CF00F540AF /* MoleculeSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeSectionHeader.swift; sourceTree = "<group>"; };
D2EC7BDC2527B83700F540AF /* SectionHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderFooterView.swift; sourceTree = "<group>"; };
@ -1418,12 +1430,11 @@
D22479902316A9CB003FCCF9 /* Organisms */ = {
isa = PBXGroup;
children = (
D2EAC09E2541EFE200AA9495 /* Carousel */,
D260105A23D0BB7100764D80 /* StackModelProtocol.swift */,
D260106423D0CEA700764D80 /* StackModel.swift */,
D260105C23D0BCD400764D80 /* Stack.swift */,
D21B7F5E2437C5BC00051ABF /* MoleculeStackView.swift */,
012A88AE238C626E00FE3DA1 /* CarouselModel.swift */,
D2A6390022CBB1820052ED1F /* Carousel.swift */,
);
path = Organisms;
sourceTree = "<group>";
@ -1927,6 +1938,10 @@
D20492A524329CE200A5EED6 /* LoadImageView.swift */,
0A51F3E02475CB73002E08B6 /* LoadingSpinnerModel.swift */,
0A51F3E12475CB73002E08B6 /* LoadingSpinner.swift */,
AA37CBD2251907200027344C /* StarsModel.swift */,
AA37CBD42519072F0027344C /* Stars.swift */,
AA07EA902510A442009A2AE3 /* StarModel.swift */,
AA07EA922510A451009A2AE3 /* Star.swift */,
);
path = Views;
sourceTree = "<group>";
@ -2144,6 +2159,17 @@
path = TopNotification;
sourceTree = "<group>";
};
D2EAC09E2541EFE200AA9495 /* Carousel */ = {
isa = PBXGroup;
children = (
012A88AE238C626E00FE3DA1 /* CarouselModel.swift */,
D2A6390022CBB1820052ED1F /* Carousel.swift */,
D2EAC09F2541EFFB00AA9495 /* LineDecorationView.swift */,
D2EAC0A32541F00F00AA9495 /* CarouselCollectionLayout.swift */,
);
path = Carousel;
sourceTree = "<group>";
};
D2EC7BD22527A1E400F540AF /* HeadersAndFooters */ = {
isa = PBXGroup;
children = (
@ -2329,6 +2355,7 @@
0A6682B5243769C700AD3CA1 /* TextView.swift in Sources */,
D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */,
D21B7F602437C5BC00051ABF /* MoleculeStackView.swift in Sources */,
D2EAC0A02541EFFB00AA9495 /* LineDecorationView.swift in Sources */,
0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */,
AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */,
AA7F32AD246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift in Sources */,
@ -2387,6 +2414,7 @@
AA1EC59724373985003D6F50 /* ListThreeColumnSpeedTestDividerModel.swift in Sources */,
BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */,
D2CAC7D3251105A700C75681 /* MVMCoreUITopAlertExpandableView+Extension.swift in Sources */,
AA07EA932510A451009A2AE3 /* Star.swift in Sources */,
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */,
D28A837B23C928DA00DFE4FC /* MoleculeListCellProtocol.swift in Sources */,
D28BA74D248589C800B75CB8 /* TabPageModelProtocol.swift in Sources */,
@ -2412,6 +2440,7 @@
D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */,
D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */,
D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */,
AA37CBD3251907200027344C /* StarsModel.swift in Sources */,
8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */,
94C0150C2421564A005811A9 /* ActionCollapseNotificationModel.swift in Sources */,
D2CAC7CB251104E100C75681 /* NotificationXButtonModel.swift in Sources */,
@ -2512,6 +2541,7 @@
01EB368F23609801006832FA /* LabelModel.swift in Sources */,
0A6682AC243531C300AD3CA1 /* Padding.swift in Sources */,
AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */,
AA37CBD52519072F0027344C /* Stars.swift in Sources */,
942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */,
8D8067D32444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift in Sources */,
8D4687E4242E2DF300802879 /* ListFourColumnDataUsageListItem.swift in Sources */,
@ -2574,6 +2604,7 @@
279B1569242BBC2F00921D6C /* ActionModelAdapter.swift in Sources */,
BB6C6AC0242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift in Sources */,
8DEFA95E243DAC2F000D27E5 /* ListThreeColumnDataUsageDivider.swift in Sources */,
D2EAC0A42541F00F00AA9495 /* CarouselCollectionLayout.swift in Sources */,
AAE7270E24AC8B9300A3ED0E /* HeadersH2CaretLink.swift in Sources */,
0AD93A9F24C0AA5100E56A97 /* ImageView.swift in Sources */,
D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */,
@ -2661,6 +2692,7 @@
BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */,
943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */,
011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */,
AA07EA912510A442009A2AE3 /* StarModel.swift in Sources */,
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,
011D95892404249B000E3791 /* FormHolderModelProtocol.swift in Sources */,
BB54C5202434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift in Sources */,

View File

@ -51,6 +51,7 @@ import UIKit
guard let model = model as? LinkModel else { return }
setTitle(model.title, for: .normal)
accessibilityLabel = model.title
setTitleColor((model.inverted ? model.enabledColor_inverted : model.enabledColor).uiColor, for: .normal)
setTitleColor((model.inverted ? model.disabledColor_inverted : model.disabledColor).uiColor, for: .disabled)
isEnabled = model.enabled

View File

@ -6,9 +6,12 @@
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers public class ImageViewModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "image"
public var backgroundColor: Color?
public var moleculeName: String = ImageViewModel.identifier
@ -23,6 +26,10 @@ import Foundation
public var cornerRadius: CGFloat?
public var clipsImage: Bool?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(image: String, imageFormat: String? = nil, width: CGFloat? = nil, height: CGFloat? = nil) {
self.image = image
self.imageFormat = imageFormat
@ -30,6 +37,10 @@ import Foundation
self.height = height
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor

View File

@ -6,18 +6,21 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers open class LoadImageView: View {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
public let loadingSpinner = MFLoadingSpinner(frame: .zero)
public let imageView = MFTransparentGIFView(frame: .zero)
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var addSizeConstraintsForAspectRatio = true
public var shouldNotifyDelegateOnUpdate = true
var centerX: NSLayoutConstraint?
var centerY: NSLayoutConstraint?
var widthConstraint: NSLayoutConstraint?
var heightConstraint: NSLayoutConstraint?
var loadingSpinnerHeightConstraint: NSLayoutConstraint?
// Allows for a view to hardcode which height to use if there is none in the json.
var imageWidth: CGFloat?
@ -35,6 +38,25 @@ import UIKit
private var containerHelper = ContainerHelper()
open override var accessibilityLabel: String? {
get { imageView.accessibilityLabel }
set { imageView.accessibilityLabel = newValue }
}
//--------------------------------------------------
// MARK: - Constraint Properties
//--------------------------------------------------
var centerX: NSLayoutConstraint?
var centerY: NSLayoutConstraint?
var widthConstraint: NSLayoutConstraint?
var heightConstraint: NSLayoutConstraint?
var loadingSpinnerHeightConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public init(pinnedEdges edge: UIRectEdge) {
edges = edge
super.init(frame: .zero)
@ -60,51 +82,24 @@ import UIKit
set(with: model, delegateObject, additionalData)
}
public func pinEdges(_ edge: UIRectEdge) {
edges = edge
if edge == UIRectEdge.all {
imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.horizontal)
imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical)
} else {
imageView.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: NSLayoutConstraint.Axis.horizontal)
imageView.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: NSLayoutConstraint.Axis.vertical)
}
if edge.contains(UIRectEdge.top) && edge.contains(UIRectEdge.bottom) {
containerHelper.alignVertical(.fill)
} else if edge.contains(UIRectEdge.top) {
containerHelper.alignVertical(.leading)
} else if edge.contains(UIRectEdge.bottom) {
containerHelper.alignVertical(.trailing)
} else {
containerHelper.alignVertical(.center)
}
if edge.contains(UIRectEdge.left) && edge.contains(UIRectEdge.right) {
containerHelper.alignHorizontal(.fill)
} else if edge.contains(UIRectEdge.left) {
containerHelper.alignHorizontal(.leading)
} else if edge.contains(UIRectEdge.right) {
containerHelper.alignHorizontal(.trailing)
} else {
containerHelper.alignHorizontal(.center)
}
}
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol
//--------------------------------------------------
override open func setupView() {
super.setupView()
guard subviews.count == 0 else {
return
}
guard subviews.count == 0 else { return }
clipsToBounds = true
setContentHuggingPriority(UILayoutPriority(rawValue: 950), for: NSLayoutConstraint.Axis.horizontal)
setContentHuggingPriority(UILayoutPriority(rawValue: 950), for: NSLayoutConstraint.Axis.vertical)
setContentHuggingPriority(UILayoutPriority(rawValue: 950), for: .horizontal)
setContentHuggingPriority(UILayoutPriority(rawValue: 950), for: .vertical)
// Setup image.
imageView.translatesAutoresizingMaskIntoConstraints = false;
addSubview(imageView)
containerHelper.constrainView(imageView)
// Setup edges constraints
pinEdges(edges)
@ -125,8 +120,12 @@ import UIKit
}
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
open func defaultCompletionBlock() -> MVMCoreGetImageBlock {
return {image,gifData,_ in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
return { image, gifData,_ in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
if let image = image {
self?.imageView.image = image
self?.layoutIfNeeded()
@ -139,9 +138,8 @@ import UIKit
open func shouldLoadImage(withName imageName: String?, width: CGFloat) -> Bool {
// We should load a new image if there is no current image, the image names are different, the width is different, or we are using a fallback image.
guard let currentWidth = self.currentImageWidth else {
return true
}
guard let currentWidth = self.currentImageWidth else { return true }
return (imageView.image == nil && imageView.animatedImage == nil) || imageName != currentImageName || !MVMCoreGetterUtility.cgfequal(width, currentWidth) || isFallbackImage
}
@ -150,22 +148,61 @@ import UIKit
if ((imageView.image == nil && imageView.animatedImage == nil) || imageName != currentImageName || isFallbackImage) {
return true
}
// load new image if the width is different
if let oldWidth = self.currentImageWidth, let newWidth = width, !MVMCoreGetterUtility.cgfequal(oldWidth, newWidth) {
return true
} else if (self.currentImageWidth == nil) != (width == nil) {
return true
}
// load new image if the height is different
if let oldHeight = self.currentImageHeight, let newHeight = height, !MVMCoreGetterUtility.cgfequal(oldHeight, newHeight) {
return true
} else if (self.currentImageHeight == nil) || (height == nil) {
return true
}
return false
}
// MARK: - Constraints
//--------------------------------------------------
// MARK: - Constraint Methods
//--------------------------------------------------
public func pinEdges(_ edge: UIRectEdge) {
edges = edge
if edge == .all {
imageView.setContentHuggingPriority(.defaultLow, for: .horizontal)
imageView.setContentHuggingPriority(.defaultLow, for: .vertical)
} else {
imageView.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal)
imageView.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .vertical)
}
if edge.contains(.top) && edge.contains(.bottom) {
containerHelper.alignVertical(.fill)
} else if edge.contains(.top) {
containerHelper.alignVertical(.leading)
} else if edge.contains(.bottom) {
containerHelper.alignVertical(.trailing)
} else {
containerHelper.alignVertical(.center)
}
if edge.contains(.left) && edge.contains(.right) {
containerHelper.alignHorizontal(.fill)
} else if edge.contains(.left) {
containerHelper.alignHorizontal(.leading)
} else if edge.contains(.right) {
containerHelper.alignHorizontal(.trailing)
} else {
containerHelper.alignHorizontal(.center)
}
}
func setHeight(_ height: CGFloat) {
if let heightConstraint = heightConstraint, MVMCoreGetterUtility.cgfequal(heightConstraint.multiplier, 1) {
@ -187,9 +224,8 @@ import UIKit
}
func layoutWillChange(width: CGFloat?, height: CGFloat?, size: CGSize?) -> Bool {
guard addSizeConstraintsForAspectRatio else {
return false
}
guard addSizeConstraintsForAspectRatio else { return false }
let widthWillChange = !MVMCoreGetterUtility.cgfequal(widthConstraint?.constant ?? 0, width ?? 0)
let heightWillChange = !MVMCoreGetterUtility.cgfequal(heightConstraint?.constant ?? 0, height ?? 0)
let sizeWillChange = (width == nil || height == nil) && !(size?.equalTo(imageView.image?.size ?? CGSize.zero) ?? false)
@ -199,11 +235,11 @@ import UIKit
// Constrains the image view to be the size provided. Used to size it to the image to fix aspect fit defect.
func addConstraints(width: NSNumber?, height: NSNumber?, size: CGSize?) {
widthConstraint?.isActive = false
heightConstraint?.isActive = false
guard addSizeConstraintsForAspectRatio else {
return
}
guard addSizeConstraintsForAspectRatio else { return }
if let width = width, let height = height {
setHeight(height.cgfloat())
@ -221,31 +257,40 @@ import UIKit
}
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.delegateObject = delegateObject
super.set(with: model, delegateObject, additionalData)
guard let imageModel = model as? ImageViewModel else { return }
if let accessibilityString = imageModel.accessibilityText {
imageView.accessibilityLabel = accessibilityString
imageView.accessibilityTraits = .staticText
imageView.isAccessibilityElement = true
}
let width = imageModel.width ?? imageWidth
let height = imageModel.height ?? imageHeight
// For smoother transitions, set constraints that we know immediately.
if let width = width, addSizeConstraintsForAspectRatio {
setWidth(width)
}
if let height = height, addSizeConstraintsForAspectRatio {
setHeight(height)
}
if shouldLoadImage(withName: imageModel.image, width: width, height: height) {
imageView.image = nil
imageView.animatedImage = nil
loadImage(withName: imageModel.image, format: imageModel.imageFormat, width: width as NSNumber?, height: height as NSNumber?, customFallbackImage: imageModel.fallbackImage, localBundle: imageModel.localBundle)
}
if let contentMode = imageModel.contentMode {
imageView.contentMode = contentMode
}
@ -260,7 +305,10 @@ import UIKit
}
}
// MARK: - load functions
//--------------------------------------------------
// MARK: - Load Methods
//--------------------------------------------------
public func loadImage(withName imageName: String?, format: String? = nil, width: NSNumber? = nil, height: NSNumber? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) {
let completionBlock = completionHandler ?? defaultCompletionBlock()
@ -275,7 +323,7 @@ import UIKit
let finishedLoadingBlock: MVMCoreGetImageBlock = {[weak self] (image, data, isFallbackImage) in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
guard let self = self,
let loadingImageName = self.currentImageName, loadingImageName == imageName else { return }
let loadingImageName = self.currentImageName, loadingImageName == imageName else { return }
self.isFallbackImage = isFallbackImage
self.loadingSpinner.pause()
let layoutWillChange = self.shouldNotifyDelegateOnUpdate ? self.layoutWillChange(width: self.currentImageWidth, height: self.currentImageHeight, size: image?.size) : false
@ -298,12 +346,12 @@ import UIKit
}
public func loadCroppedImage(withName imageName:
String?, width: NSNumber?, height: NSNumber?, cropRect: CGRect, flipImage: Bool, customFallbackImage: String?) {
String?, width: NSNumber?, height: NSNumber?, cropRect: CGRect, flipImage: Bool, customFallbackImage: String?) {
MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in
self.currentImageName = imageName
self.loadingSpinner.resumeSpinnerAfterDelay()
self.loadingSpinnerHeightConstraint?.constant = self.spinnerHeight
MVMCoreCache.shared()?.getCroppedImage(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, finalRect: cropRect, flipImage: flipImage, localFallbackImageName: customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback"), completionHandler: { [weak self] (image, data, isFallBackImage) in
MVMCoreDispatchUtility.performBlock(onMainThread: {
guard let image = image, let loadingImageName = self?.currentImageName, loadingImageName == imageName else {
@ -327,7 +375,7 @@ import UIKit
public func loadImage(withName imageName: String?) {
loadImage(withName: imageName, format: nil, width: nil, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock())
}
public func loadImage(withName imageName: String?, width: NSNumber?) {
loadImage(withName: imageName, format: nil, width: width, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock())
}

View File

@ -0,0 +1,160 @@
//
// Star.swift
// MVMCoreUI
//
// Created by Lekshmi S on 15/09/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
@objcMembers open class Star: View {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
private var starLayer: CAShapeLayer?
private var progressLayer: CAShapeLayer?
private let maskLayer = CAShapeLayer()
public var starModel: StarModel? {
return model as? StarModel
}
@Percent public var percent = 0 {
didSet {
updateAccessibilityLabel()
setNeedsDisplay()
}
}
public var size: CGFloat = 30 {
didSet {
widthConstraint?.constant = size
setNeedsDisplay()
}
}
public var fillColor = UIColor.mvmBlack {
didSet {
setNeedsDisplay()
}
}
public var borderColor: CGColor = UIColor.mvmBlack.cgColor {
didSet {
setNeedsDisplay()
}
}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
public var widthConstraint: NSLayoutConstraint?
public var heightConstraint: NSLayoutConstraint?
//------------------------------------------------------
// MARK: - State Handling
//------------------------------------------------------
open override func draw(_ rect: CGRect) {
//Draw progress
progressLayer?.removeFromSuperlayer()
let progress = drawProgress()
layer.addSublayer(progress)
progressLayer = progress
//Draw the star
starLayer?.removeFromSuperlayer()
let star = drawStar()
layer.addSublayer(star)
starLayer = star
//Mask the star
maskLayer.removeFromSuperlayer()
maskLayer.path = star.path
layer.mask = maskLayer
}
func drawProgress() -> CAShapeLayer {
let shapeLayer = CAShapeLayer()
let width = bounds.size.width * percent / 100.0
shapeLayer.path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: width, height: bounds.height)).cgPath
shapeLayer.fillColor = fillColor.cgColor
return shapeLayer
}
func drawStar() -> CAShapeLayer {
let shapeLayer = CAShapeLayer()
shapeLayer.frame = bounds
let starPath = UIBezierPath()
let center = shapeLayer.position
let theta = .pi / CGFloat(5.0)
let outerRadius = center.x * 1.039
let excessRadius = outerRadius - center.x
let innerRadius = CGFloat(outerRadius * 0.382)
let leftEdgePointX = (center.x + cos(4.0 * theta) * outerRadius) + excessRadius
let horizontalOffset = leftEdgePointX / 2.0
let offsetCenter = CGPoint(x: center.x - horizontalOffset, y: center.y)
for i in 0 ..< 10 {
let radius = i % 2 == 0 ? outerRadius : innerRadius
let pointX = offsetCenter.x + cos(CGFloat(i) * theta) * radius
let pointY = offsetCenter.y + sin(CGFloat(i) * theta) * radius
let point = CGPoint(x: pointX, y: pointY)
if i == 0 {
starPath.move(to: point)
} else {
starPath.addLine(to: point)
}
}
starPath.close()
// Rotate the path so the star points up as expected
var pathTransform = CGAffineTransform.identity
pathTransform = pathTransform.translatedBy(x: center.x, y: center.y)
pathTransform = pathTransform.rotated(by: CGFloat(-.pi / 2.0))
pathTransform = pathTransform.translatedBy(x: -center.x, y: -center.y)
starPath.apply(pathTransform)
shapeLayer.path = starPath.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.opacity = 1.0
shapeLayer.lineWidth = 1
shapeLayer.strokeColor = borderColor
return shapeLayer
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
open override func reset() {
super.reset()
borderColor = UIColor.mvmBlack.cgColor
fillColor = .mvmBlack
percent = 0
}
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? StarModel else { return }
percent = model.percent
if let fillColor = model.fillColor?.uiColor {
self.fillColor = fillColor
}
if let borderColor = model.borderColor?.cgColor {
self.borderColor = borderColor
}
size = model.size
updateAccessibilityLabel()
}
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol
//--------------------------------------------------
open override func setupView() {
super.setupView()
backgroundColor = .clear
widthConstraint = widthAnchor.constraint(equalToConstant: 30)
widthConstraint?.isActive = true
heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1)
heightConstraint?.isActive = true
isAccessibilityElement = true
updateAccessibilityLabel()
}
// MARK: - Accessibility
func updateAccessibilityLabel() {
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "star")
accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "star_percent") ?? "", Int(percent))
}
}

View File

@ -0,0 +1,59 @@
//
// StarModel.swift
// MVMCoreUI
//
// Created by Lekshmi S on 15/09/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
open class StarModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "star"
public var backgroundColor: Color?
@Percent public var percent: CGFloat = 0
public var borderColor: Color?
public var fillColor: Color?
public var size: CGFloat = 30.0
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case percent
case borderColor
case fillColor
case size
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let percent = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .percent) {
self.percent = percent
}
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor)
fillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor)
if let size = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .size) {
self.size = size
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(percent, forKey: .percent)
try container.encodeIfPresent(borderColor, forKey: .borderColor)
try container.encodeIfPresent(fillColor, forKey: .fillColor)
try container.encode(size, forKey: .size)
}
}

View File

@ -0,0 +1,108 @@
//
// Stars.swift
// MVMCoreUI
//
// Created by Lekshmi S on 21/09/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
open class Stars: View {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
private var stack = UIStackView()
public var starsModel: StarsModel? {
return model as? StarsModel
}
private var delegateObject: MVMCoreUIDelegateObject?
private let itemSpacing: CGFloat = 3.0
private var heightConstraint: NSLayoutConstraint?
private var starsFilledValue: Float = 0
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol
//--------------------------------------------------
open override func setupView() {
super.setupView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .horizontal
stack.spacing = itemSpacing
addSubview(stack)
NSLayoutConstraint.constraintPinSubview(toSuperview: stack)
heightConstraint = heightAnchor.constraint(equalToConstant: 30)
heightConstraint?.isActive = true
isAccessibilityElement = true
updateAccessibilityLabel()
}
@objc override open func updateView(_ size: CGFloat) {
super.updateView(size)
stack.updateView(size)
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
self.delegateObject = delegateObject
createStars()
updateStars()
updateAccessibilityLabel()
}
public override func reset() {
stack.subviews.forEach({$0.removeFromSuperview()})
super.reset()
}
//------------------------------------------------------
// MARK: - Methods
//------------------------------------------------------
func createStars() {
guard let starsModel = starsModel else { return }
for starModel in starsModel.stars {
let star = Star(model: starModel, delegateObject, nil)
star.isAccessibilityElement = false
stack.addArrangedSubview(star)
}
heightConstraint?.constant = starsModel.size
}
func updateStars() {
guard let starsModel = starsModel else { return }
let percentRequiredToFillStarFully = CGFloat(100/(stack.arrangedSubviews.count))
let numberOfFilledStars = Int(starsModel.percent/percentRequiredToFillStarFully)
starsFilledValue = Float(numberOfFilledStars)
for case let (index, star as Star) in stack.arrangedSubviews.enumerated() {
//Star model colors should take priority over stars.
if let borderColor = starsModel.borderColor?.cgColor, star.starModel?.borderColor == nil {
star.borderColor = borderColor
}
if let fillColor = starsModel.fillColor?.uiColor, star.starModel?.fillColor == nil {
star.fillColor = fillColor
}
if let backgroundColor = starsModel.starBackgroundColor?.uiColor, star.starModel?.backgroundColor == nil {
star.backgroundColor = backgroundColor
}
//Fill the stars based on percentage. Ex: if there were 4 stars, 75 percent is 3 full stars
if index < numberOfFilledStars {
star.percent = 100
} else if index == numberOfFilledStars {
let remainingProgress = (starsModel.percent).truncatingRemainder(dividingBy: percentRequiredToFillStarFully)
let fillPercent = remainingProgress/percentRequiredToFillStarFully
starsFilledValue += Float(fillPercent)
star.percent = fillPercent * 100
} else {
star.percent = 0
}
star.size = starsModel.size
}
}
// MARK: - Accessibility
func updateAccessibilityLabel() {
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "stars_filled") ?? "", starsFilledValue, stack.arrangedSubviews.count)
}
}

View File

@ -0,0 +1,66 @@
//
// StarsModel.swift
// MVMCoreUI
//
// Created by Lekshmi S on 21/09/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
@objcMembers public class StarsModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "stars"
public var backgroundColor: Color?
public var starBackgroundColor: Color?
public var stars: [StarModel]
@Percent public var percent: CGFloat = 0
public var borderColor: Color?
public var fillColor: Color?
public var size: CGFloat = 30.0
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case starBackgroundColor
case stars
case percent
case borderColor
case fillColor
case size
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
stars = try typeContainer.decode([StarModel].self, forKey: .stars)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
starBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .starBackgroundColor)
if let percent = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .percent) {
self.percent = percent
}
borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor)
fillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor)
if let size = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .size) {
self.size = size
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(stars, forKey: .stars)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(starBackgroundColor, forKey: .starBackgroundColor)
try container.encodeIfPresent(borderColor, forKey: .borderColor)
try container.encodeIfPresent(fillColor, forKey: .fillColor)
try container.encode(percent, forKey: .percent)
try container.encode(size, forKey: .size)
}
}

View File

@ -88,6 +88,8 @@ import Foundation
MoleculeObjectMapping.shared()?.register(viewClass: Tags.self, viewModelClass: TagsModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: Tag.self, viewModelClass: TagModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: Heart.self, viewModelClass: HeartModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: Stars.self, viewModelClass: StarsModel.self)
MoleculeObjectMapping.shared()?.register(viewClass: Star.self, viewModelClass: StarModel.self)
// MARK:- Other Atoms

View File

@ -6,8 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers open class ListDeviceComplexButtonSmall: TableViewCell {
//--------------------------------------------------
@ -19,7 +17,7 @@ import Foundation
public let headline = Label(fontStyle: .BoldTitleMedium)
public let body = Label(fontStyle: .RegularBodySmall)
public let body2 = Label(fontStyle: .RegularBodySmall)
public let button = PillButton(frame: .zero)
public let button = PillButton()
public let rightImageView = LoadImageView()
public var stack: Stack<StackModel>
@ -99,6 +97,7 @@ import Foundation
//--------------------------------------------------
func getAccessibilityMessage() -> String? {
var message = ""
if let eyebrowText = eyebrow.text, !eyebrowText.isEmpty {
message += eyebrowText + ", "
@ -119,10 +118,12 @@ import Foundation
if let rightImageViewText = rightImageView.imageView.accessibilityLabel, !rightImageViewText.isEmpty {
message += rightImageViewText
}
return message.count > 0 ? message : nil
}
func updateAccessibilityLabel() {
if let accessoryView = accessoryView {
// Both caret and button. Read all content on caret.
isAccessibilityElement = false

View File

@ -8,14 +8,22 @@
import UIKit
public class MoleculeHeaderView: MoleculeContainer {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
var line = Line()
var headerModel: MoleculeHeaderModel? {
get { return model as? MoleculeHeaderModel }
}
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol
//--------------------------------------------------
open override func updateView(_ size: CGFloat) {
super.updateView(size)
line.updateView(size)
@ -30,16 +38,16 @@ public class MoleculeHeaderView: MoleculeContainer {
NSLayoutConstraint.pinViewRight(toSuperview: line, useMargins: true, constant: 0).isActive = true
}
// MARK: - MoleculeViewProtocol
open override func reset() {
super.reset()
line.setStyle(.heavy)
}
// MARK: - MoleculeViewProtocol
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let headerModel = headerModel else { return }
if let lineModel = headerModel.line {
line.set(with: lineModel, delegateObject, additionalData)
}

View File

@ -8,6 +8,7 @@
import UIKit
@objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol {
//--------------------------------------------------
// MARK: - Properties
@ -44,6 +45,7 @@ import UIKit
super.setupView()
stack.translatesAutoresizingMaskIntoConstraints = false
isAccessibilityElement = false
addSubview(stack)
stack.addArrangedSubview(secondaryButton)
stack.addArrangedSubview(primaryButton)
@ -68,6 +70,8 @@ import UIKit
if secondaryButton.superview != nil {
equalWidthConstraint?.isActive = true
}
primaryButton.isAccessibilityElement = true
}
public func showSecondaryButton() {
@ -80,6 +84,8 @@ import UIKit
if primaryButton.superview != nil {
equalWidthConstraint?.isActive = true
}
secondaryButton.isAccessibilityElement = true
}
public func hidePrimaryButton() {
@ -89,6 +95,7 @@ import UIKit
primaryButton.isHidden = true
}
primaryButton.isAccessibilityElement = false
equalWidthConstraint?.isActive = false
}
@ -99,6 +106,7 @@ import UIKit
secondaryButton.isHidden = true
}
secondaryButton.isAccessibilityElement = false
equalWidthConstraint?.isActive = false
}

View File

@ -30,7 +30,6 @@ open class MoleculeCollectionViewCell: CollectionViewCell {
open override func reset() {
super.reset()
molecule?.reset()
backgroundColor = .mvmWhite
}
open override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {

View File

@ -6,9 +6,12 @@
// Copyright © 2019 Suresh, Kamlesh. All rights reserved.
//
import Foundation
@objcMembers public class MoleculeStackItemModel: MoleculeContainerModel, StackItemModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public override class var identifier: String {
return "stackItem"
}
@ -16,6 +19,10 @@ import Foundation
public var percent: Int?
public var gone: Bool = false
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case spacing
case percent
@ -23,10 +30,18 @@ import Foundation
case moleculeName
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public override init(with moleculeModel: MoleculeModelProtocol) {
super.init(with: moleculeModel)
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing)
@ -36,7 +51,7 @@ import Foundation
}
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)

View File

@ -6,7 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
open class StackItem: Container {
var stackItemModel: StackItemModel? {

View File

@ -8,6 +8,7 @@
import UIKit
open class MoleculeContainer: Container {
/// Can be overriden to change how the molecule is added to the hierarchy.
@ -29,26 +30,32 @@ open class MoleculeContainer: Container {
}
public override static func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
guard let containerModel = model as? MoleculeContainerModelProtocol,
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule),
let moleculeName = moleculeClass.nameForReuse(with: containerModel.molecule, delegateObject) else {
return "\(model.moleculeName)<>"
}
let moleculeName = moleculeClass.nameForReuse(with: containerModel.molecule, delegateObject)
else { return "\(model.moleculeName)<>" }
return "\(model.moleculeName)<\(moleculeName)>"
}
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
guard let containerModel = model as? MoleculeContainerModelProtocol else { return 0 }
guard let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule),
let moleculeHeight = moleculeClass.estimatedHeight(with: containerModel.molecule, delegateObject) else {
return (containerModel.topPadding ?? 0) + (containerModel.bottomPadding ?? 0)
}
let moleculeHeight = moleculeClass.estimatedHeight(with: containerModel.molecule, delegateObject)
else { return (containerModel.topPadding ?? 0) + (containerModel.bottomPadding ?? 0) }
return moleculeHeight + (containerModel.topPadding ?? 0) + (containerModel.bottomPadding ?? 0)
}
public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
guard let containerModel = model as? MoleculeContainerModelProtocol,
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule) else { return nil }
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(containerModel.molecule)
else { return nil }
return moleculeClass.requiredModules(with: containerModel.molecule, delegateObject, error: error)
}
}

View File

@ -6,82 +6,135 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers public class BGImageHeadlineBodyButton: Container {
let headlineBody = HeadlineBody(frame: .zero)
let button = PillButton(frame: .zero)
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
let headlineBody = HeadlineBody()
let button = PillButton()
let backgroundImageView = LoadImageView(pinnedEdges: .all)
let maxWidth: CGFloat = 350.0
static let heightConstant: CGFloat = 320.0
//--------------------------------------------------
// MARK: - Property
//--------------------------------------------------
let maxWidth: CGFloat = 350
static let heightConstant: CGFloat = 320
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
var heightConstraint: NSLayoutConstraint?
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineBody.updateView(size)
button.updateView(size)
backgroundImageView.updateView(size)
backgroundImageView.pinEdges(.all)
}
//--------------------------------------------------
// MARK: - Setup
//--------------------------------------------------
open override func setupView() {
super.setupView()
heightConstraint = heightAnchor.constraint(equalToConstant: Self.heightConstant)
heightConstraint?.isActive = true
backgroundImageView.addSizeConstraintsForAspectRatio = true
let container = MVMCoreUICommonViewsUtility.commonView()
addAndContain(container)
container.addSubview(headlineBody)
container.addSubview(button)
//Headline view
headlineBody.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true
headlineBody.topAnchor.constraint(equalTo: container.topAnchor, constant: 0).isActive = true
headlineBody.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
headlineBody.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
let headLineBodyWidth = headlineBody.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.67)
headLineBodyWidth.priority = UILayoutPriority(rawValue: 999)
headLineBodyWidth.isActive = true
headlineBody.widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true
//Caret view
button.translatesAutoresizingMaskIntoConstraints = false
button.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true
container.bottomAnchor.constraint(greaterThanOrEqualTo: button.bottomAnchor, constant: 0).isActive = true
button.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
container.bottomAnchor.constraint(greaterThanOrEqualTo: button.bottomAnchor).isActive = true
button.topAnchor.constraint(equalTo: headlineBody.bottomAnchor, constant: PaddingDefault).isActive = true
//Background image view
backgroundImageView.translatesAutoresizingMaskIntoConstraints = false
backgroundImageView.imageView.contentMode = .scaleAspectFill
backgroundImageView.pinEdges(.all)
addSubview(backgroundImageView)
NSLayoutConstraint.constraintPinSubview(toSuperview: backgroundImageView)
sendSubviewToBack(backgroundImageView)
}
isAccessibilityElement = true
accessibilityHint = button.accessibilityHint
accessibilityTraits = button.accessibilityTraits
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineBody.updateView(size)
button.updateView(size)
backgroundImageView.updateView(size)
backgroundImageView.pinEdges(.all)
}
open override func reset() {
super.reset()
headlineBody.reset()
backgroundImageView.reset()
}
// MARK:- MoleculeViewProtocol
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 320
}
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? BGImageHeadlineBodyButtonModel else { return }
headlineBody.set(with: model.headlineBody, delegateObject, additionalData)
button.setOptional(with: model.button, delegateObject, additionalData)
button.isHidden = model.button == nil
backgroundImageView.set(with: model.image, delegateObject, additionalData)
backgroundImageView.pinEdges(.all)
updateAccessibilityLabel()
}
//----------------------------------------------------
// MARK: - Accessibility
//----------------------------------------------------
func updateAccessibilityLabel() {
var message = ""
if let headline = headlineBody.headlineLabel.text, !headline.isEmpty {
message += headline + ", "
}
if let body = headlineBody.messageLabel.text, !body.isEmpty {
message += body + ", "
}
if let backgroundImageText = backgroundImageView.accessibilityLabel, !backgroundImageText.isEmpty {
message += backgroundImageText + ", "
}
if let buttonLabel = button.accessibilityLabel, !buttonLabel.isEmpty {
message += buttonLabel
}
accessibilityLabel = message
}
open override func accessibilityActivate() -> Bool {
return button.accessibilityActivate()
}
}

View File

@ -6,42 +6,62 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "bgImageHeadlineBodyButton"
public var backgroundColor: Color?
public var button: ButtonModel?
public var headlineBody: HeadlineBodyModel
public var image: ImageViewModel
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
init(headlineBody: HeadlineBodyModel, image: ImageViewModel) {
self.headlineBody = headlineBody
self.image = image
super.init()
}
/// Defaults to set
//--------------------------------------------------
// MARK: - Defaults
//--------------------------------------------------
public override func setDefaults() {
if useHorizontalMargins == nil {
useHorizontalMargins = true
}
if useVerticalMargins == nil {
useVerticalMargins = true
}
if topPadding == nil {
topPadding = PaddingDefault
}
if bottomPadding == nil {
bottomPadding = PaddingDefault
}
if image.height == nil {
image.height = BGImageHeadlineBodyButton.heightConstant
}
button?.size = .tiny
button?.style = .secondary
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
@ -49,7 +69,11 @@ public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtoc
case image
case button
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
@ -58,7 +82,7 @@ public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtoc
button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button)
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
@ -69,4 +93,3 @@ public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtoc
try container.encodeIfPresent(button, forKey: .button)
}
}

View File

@ -6,7 +6,6 @@
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers open class EyebrowHeadlineBodyLink: View {
//--------------------------------------------------
@ -76,8 +75,9 @@ import UIKit
/// Returns the labels text in one message.
func getAccessibilityMessage() -> String? {
var message = ""
if let eyebrowLabel = eyebrow.text {
message += eyebrowLabel + ", "
}
@ -89,24 +89,31 @@ import UIKit
if let bodyLabel = body.text {
message += bodyLabel
}
return message.count > 0 ? message : nil
}
/// Returns an array of the appropriate accessibility elements.
func getAccessibilityElements() -> [Any]? {
var elements: [UIView] = []
if eyebrow.hasText {
elements.append(eyebrow)
}
if headline.hasText {
elements.append(headline)
}
if body.hasText {
elements.append(body)
}
if link.titleLabel?.text?.count ?? 0 > 0 {
elements.append(link)
}
return elements.count > 0 ? elements : nil
}
}

View File

@ -6,7 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class EyebrowHeadlineBodyLinkModel: MoleculeModelProtocol {
//--------------------------------------------------

View File

@ -6,77 +6,88 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers public class HeadLineBodyCaretLinkImage: Container {
let headlineBody = HeadlineBody(frame: .zero)
let caretButton = CaretLink(frame: .zero)
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
let headlineBody = HeadlineBody()
let caretButton = CaretLink()
let backgroundImageView = LoadImageView(pinnedEdges: .all)
let maxWidth: CGFloat = 350.0
static let heightConstant: CGFloat = 320.0
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
let maxWidth: CGFloat = 350
static let heightConstant: CGFloat = 320
var heightConstraint: NSLayoutConstraint?
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineBody.updateView(size)
caretButton.updateView(size)
backgroundImageView.updateView(size)
backgroundImageView.pinEdges(.all)
}
//--------------------------------------------------
// MARK: - Setup
//--------------------------------------------------
open override func setupView() {
super.setupView()
heightConstraint = heightAnchor.constraint(equalToConstant: Self.heightConstant)
heightConstraint?.isActive = true
backgroundImageView.addSizeConstraintsForAspectRatio = true
let container = MVMCoreUICommonViewsUtility.commonView()
addAndContain(container)
container.addSubview(headlineBody)
container.addSubview(caretButton)
//Headline view
headlineBody.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true
headlineBody.topAnchor.constraint(equalTo: container.topAnchor, constant: 0).isActive = true
headlineBody.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.67).isActive = true
let headLineBodyWidth = headlineBody.widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth)
headLineBodyWidth.priority = UILayoutPriority(250)
headLineBodyWidth.isActive = true
//Caret view
caretButton.translatesAutoresizingMaskIntoConstraints = false
caretButton.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0).isActive = true
container.bottomAnchor.constraint(equalTo: caretButton.bottomAnchor, constant: 0).isActive = true
caretButton.topAnchor.constraint(greaterThanOrEqualTo: headlineBody.bottomAnchor, constant: PaddingTwo).isActive = true
//Background image view
backgroundImageView.translatesAutoresizingMaskIntoConstraints = false
backgroundImageView.imageView.contentMode = .scaleAspectFill
backgroundImageView.pinEdges(.all)
addSubview(backgroundImageView)
NSLayoutConstraint.constraintPinSubview(toSuperview: backgroundImageView)
sendSubviewToBack(backgroundImageView)
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineBody.updateView(size)
caretButton.updateView(size)
backgroundImageView.updateView(size)
backgroundImageView.pinEdges(.all)
}
open override func reset() {
super.reset()
headlineBody.reset()
backgroundImageView.reset()
}
// MARK:- MoleculeViewProtocol
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 320
}
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? HeadlineBodyCaretLinkImageModel else { return }
headlineBody.set(with: model.headlineBody, delegateObject, additionalData)
caretButton.setOptional(with: model.caretLink, delegateObject, additionalData)
caretButton.isHidden = model.caretLink == nil

View File

@ -6,12 +6,19 @@
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
open class HeadlineBody: View {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
let headlineLabel = Label(fontStyle: .BoldTitleLarge)
let messageLabel = Label(fontStyle: .RegularBodySmall)
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
var spaceBetweenLabelsConstant = PaddingOne
var spaceBetweenLabels: NSLayoutConstraint?
var leftConstraintTitle: NSLayoutConstraint?
@ -19,21 +26,30 @@ open class HeadlineBody: View {
var leftConstraintMessage: NSLayoutConstraint?
var rightConstraintMessage: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
func hasText() -> Bool {
return headlineLabel.hasText || messageLabel.hasText
}
// MARK: - Styling
func style(with style: HeadlineBodyModel.Style?) {
switch style {
case .landingHeader:
styleLandingPageHeader()
case .header:
stylePageHeader()
case .item:
styleListItem()
case .itemHeader:
styleListItemDivider()
default: break
}
}
@ -61,18 +77,14 @@ open class HeadlineBody: View {
messageLabel.setFontStyle(.RegularBodySmall)
spaceBetweenLabelsConstant = 0
}
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineLabel.updateView(size)
messageLabel.updateView(size)
setSpacing()
}
//--------------------------------------------------
open override func setupView() {
super.setupView()
backgroundColor = .clear
clipsToBounds = true
@ -86,35 +98,42 @@ open class HeadlineBody: View {
view.addSubview(headlineLabel)
view.addSubview(messageLabel)
headlineLabel.setContentHuggingPriority(.required, for: .vertical)
messageLabel.setContentHuggingPriority(.required, for: .vertical)
view.setContentHuggingPriority(.required, for: .vertical)
headlineLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
headlineLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
spaceBetweenLabels = messageLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: spaceBetweenLabelsConstant)
spaceBetweenLabels?.isActive = true
leftConstraintTitle = headlineLabel.leftAnchor.constraint(equalTo: view.leftAnchor)
leftConstraintTitle?.isActive = true
rightConstraintTitle = view.rightAnchor.constraint(equalTo: headlineLabel.rightAnchor)
rightConstraintTitle?.isActive = true
leftConstraintMessage = messageLabel.leftAnchor.constraint(equalTo: view.leftAnchor)
leftConstraintMessage?.isActive = true
rightConstraintMessage = view.rightAnchor.constraint(equalTo: messageLabel.rightAnchor)
rightConstraintMessage?.isActive = true
view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor).isActive = true
}
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineLabel.updateView(size)
messageLabel.updateView(size)
setSpacing()
}
//--------------------------------------------------
// MARK: - Constraining
//--------------------------------------------------
public func setSpacing() {
if headlineLabel.hasText && messageLabel.hasText {
spaceBetweenLabels?.constant = spaceBetweenLabelsConstant
@ -122,7 +141,7 @@ open class HeadlineBody: View {
spaceBetweenLabels?.constant = 0
}
}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
@ -135,7 +154,7 @@ open class HeadlineBody: View {
super.set(with: model, delegateObject, additionalData)
guard let headlineBodyModel = model as? HeadlineBodyModel else { return }
style(with: headlineBodyModel.style)
headlineLabel.setOptional(with: headlineBodyModel.headline, delegateObject, additionalData)

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers open class HeadlineBodyButton: View {
//------------------------------------------------------

View File

@ -6,8 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class HeadlineBodyButtonModel: MoleculeModelProtocol {
//--------------------------------------------------
@ -17,7 +15,7 @@ public class HeadlineBodyButtonModel: MoleculeModelProtocol {
public static var identifier: String = "headlineBodyButton"
public var moleculeName: String = HeadlineBodyButtonModel.identifier
public var backgroundColor: Color?
public var headlineBody: HeadlineBodyModel
public var button: ButtonModel
public var buttonHeadlinePadding: CGFloat

View File

@ -6,40 +6,60 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
public class HeadlineBodyCaretLinkImageModel: ContainerModel, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "headlineBodyCaretLinkImage"
public var backgroundColor: Color?
public var caretLink: CaretLinkModel?
public var headlineBody: HeadlineBodyModel
public var image: ImageViewModel
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
init(headlineBody: HeadlineBodyModel, image: ImageViewModel) {
self.headlineBody = headlineBody
self.image = image
super.init()
}
//--------------------------------------------------
// MARK: - Defaults
//--------------------------------------------------
/// Defaults to set
public override func setDefaults() {
if useHorizontalMargins == nil {
useHorizontalMargins = true
}
if useVerticalMargins == nil {
useVerticalMargins = true
}
if topPadding == nil {
topPadding = PaddingDefault
}
if bottomPadding == nil {
bottomPadding = PaddingDefault
}
if image.height == nil {
image.height = HeadLineBodyCaretLinkImage.heightConstant
}
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
@ -47,7 +67,11 @@ public class HeadlineBodyCaretLinkImageModel: ContainerModel, MoleculeModelProto
case image
case caretLink
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
@ -56,7 +80,7 @@ public class HeadlineBodyCaretLinkImageModel: ContainerModel, MoleculeModelProto
caretLink = try typeContainer.decodeIfPresent(CaretLinkModel.self, forKey: .caretLink)
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)

View File

@ -6,33 +6,41 @@
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers public class HeadlineBodyLink: View {
let headlineBody = HeadlineBody(frame: .zero)
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
let headlineBody = HeadlineBody()
let link = Link()
var spaceBetweenConstant: CGFloat = 0.0
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
var spaceBetweenConstant: CGFloat = 0
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
var spaceBetween: NSLayoutConstraint?
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineBody.updateView(size)
link.updateView(size)
setSpacing()
}
//--------------------------------------------------
// MARK: - Setup
//--------------------------------------------------
open override func setupView() {
super.setupView()
guard subviews.count == 0 else {
return
}
guard subviews.isEmpty else { return }
addSubview(headlineBody)
addSubview(link)
headlineBody.styleListItem()
headlineBody.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
headlineBody.topAnchor.constraint(equalTo: topAnchor).isActive = true
headlineBody.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
rightAnchor.constraint(greaterThanOrEqualTo: headlineBody.rightAnchor).isActive = true
var constraint = rightAnchor.constraint(equalTo: headlineBody.rightAnchor)
@ -50,7 +58,10 @@ import UIKit
constraint.isActive = true
}
// MARK: - Constraining
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
public func setSpacing() {
if headlineBody.hasText() && (link.titleLabel?.text?.count ?? 0) > 0 {
spaceBetween?.constant = spaceBetweenConstant
@ -59,7 +70,17 @@ import UIKit
}
}
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol
//--------------------------------------------------
open override func updateView(_ size: CGFloat) {
super.updateView(size)
headlineBody.updateView(size)
link.updateView(size)
setSpacing()
}
open override func reset() {
super.reset()
headlineBody.reset()
@ -67,7 +88,6 @@ import UIKit
link.reset()
}
// MARK:- MoleculeViewProtocol
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? HeadlineBodyLinkModel else { return }

View File

@ -8,13 +8,22 @@
import Foundation
public struct HeadlineBodyLinkModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "headlineBodyLink"
public var moleculeName: String = HeadlineBodyLinkModel.identifier
public var headlineBody: HeadlineBodyModel
public var link: LinkModel
public var backgroundColor: Color?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(headlineBody: HeadlineBodyModel, link: LinkModel) {
self.headlineBody = headlineBody
self.link = link

View File

@ -8,11 +8,15 @@
import UIKit
// This class is only temporarily necessary. Eventually we will have initWithModel instad of just init for moleculeviews, which will remove this need.
open class StringAndMoleculeStack: MoleculeStackView {
override open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let model = model as? StackModelProtocol,
let molcules = model.molecules as? [MoleculeStackItemModel] else { return }
let molcules = model.molecules as? [MoleculeStackItemModel]
else { return }
for stackItemModel in molcules {
guard let stringAndMoleculeModel = stackItemModel.molecule as? StringAndMoleculeModel,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(stringAndMoleculeModel.molecule, delegateObject: delegateObject

View File

@ -6,9 +6,9 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
open class StringAndMoleculeView: View {
var label = Label(fontStyle: .RegularBodySmall)
var molecule: MoleculeViewProtocol
@ -45,11 +45,9 @@ open class StringAndMoleculeView: View {
override public func setupView() {
super.setupView()
guard subviews.count == 0 else {
return
}
translatesAutoresizingMaskIntoConstraints = false
guard subviews.count == 0 else { return }
addSubview(label)
addSubview(molecule)
@ -82,7 +80,7 @@ open class StringAndMoleculeView: View {
molecule.reset()
}
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let model = model as? StringAndMoleculeModel else { return }
label.text = model.string
@ -92,7 +90,7 @@ open class StringAndMoleculeView: View {
func updateLeftViewWidthConstraint(_ percent: CGFloat) {
percentage = percent
leftWidthConstraint?.isActive = false
leftWidthConstraint = label.widthAnchor.constraint(equalTo: widthAnchor, multiplier: CGFloat(percent/100), constant: 0)
leftWidthConstraint = label.widthAnchor.constraint(equalTo: widthAnchor, multiplier: CGFloat(percent / 100), constant: 0)
leftWidthConstraint?.isActive = true
}
}

View File

@ -6,8 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
open class ThreeHeadlineBodyLink: View {
//-------------------------------------------------------

View File

@ -16,11 +16,10 @@ public protocol CarouselPageControlProtocol {
func scrollViewDidScroll(_ collectionView: UICollectionView)
}
open class Carousel: View {
public let collectionView: CollectionView = {
let layout = UICollectionViewFlowLayout()
let layout = CarouselCollectionLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
@ -149,10 +148,9 @@ open class Carousel: View {
guard let carouselModel = model as? CarouselModel else { return }
accessibilityLabel = carouselModel.accessibilityText
collectionView.backgroundColor = backgroundColor
collectionView.layer.borderColor = backgroundColor?.cgColor
collectionView.layer.borderColor = UIColor.mvmCoolGray3.cgColor
collectionView.layer.borderWidth = (carouselModel.border ?? false) ? 1 : 0
backgroundColor = .white
(collectionView.collectionViewLayout as? CarouselCollectionLayout)?.useLines = carouselModel.border ?? false
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing = carouselModel.spacing ?? 0
itemWidthPercent = carouselModel.itemWidthPercent / 100.0
@ -375,7 +373,7 @@ extension Carousel: UICollectionViewDataSource {
open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return molecules?.count ?? 0
}
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let molecule = molecules?[indexPath.row],
let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil)

View File

@ -0,0 +1,65 @@
//
// CarouselCollectionLayout.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 10/22/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
open class CarouselCollectionLayout: UICollectionViewFlowLayout {
open var useLines = false
public override init() {
super.init()
registerViews()
}
required public init?(coder: NSCoder) {
super.init(coder: coder)
registerViews()
}
open func registerViews() {
register(LineDecorationView.self, forDecorationViewOfKind: LineDecorationView.elementKind)
}
open override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
// Lines are added between each item
guard useLines,
elementKind == LineDecorationView.elementKind else { return nil }
let attributes = UICollectionViewLayoutAttributes(forDecorationViewOfKind: elementKind, with: indexPath)
let itemFrame = layoutAttributesForItem(at: indexPath)?.frame ?? .zero
let width: CGFloat = 1.0
attributes.frame = CGRect(x: itemFrame.maxX + ((minimumLineSpacing - width) / 2.0), y: 0, width: width, height: itemFrame.height)
return attributes
}
func shouldHaveLine(for indexPath: IndexPath) -> Bool {
// No line for the final index
guard let numberOfSections = collectionView?.numberOfSections,
numberOfSections > 0,
let numberOfItemsInLastSection = collectionView?.numberOfItems(inSection: numberOfSections - 1),
numberOfItemsInLastSection > 0,
indexPath.section == numberOfSections - 1,
indexPath.row == numberOfItemsInLastSection - 1 else { return true }
return false
}
open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let allAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
guard useLines else { return allAttributes }
// Add line decorators.
var newAttributes = allAttributes
allAttributes.forEach { (attributes) in
guard shouldHaveLine(for: attributes.indexPath),
attributes.representedElementCategory == .cell,
let lineAttributes = layoutAttributesForDecorationView(ofKind: LineDecorationView.elementKind, at: attributes.indexPath),
rect.intersects(lineAttributes.frame) else { return }
newAttributes.append(lineAttributes)
}
return newAttributes
}
}

View File

@ -0,0 +1,20 @@
//
// LineDecorationView.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 10/22/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
public class LineDecorationView: UICollectionReusableView {
static public let elementKind = "line"
public override init(frame: CGRect) {
super.init(frame:frame)
backgroundColor = .mvmCoolGray3
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

View File

@ -9,12 +9,16 @@
import UIKit
open class MoleculeStackView: Stack<StackModel> {
var previousModel: MoleculeModelProtocol?
/// Convenience function, adds a molecule to a MoleculeStackItem to the MoleculeStack
func setup(with views: [View], lastItem: Bool) {
var models: [MoleculeStackItemModel] = []
for view in views {
guard let model = view.model else { return }
let stackItemModel = MoleculeStackItemModel(with: model)
@ -22,20 +26,22 @@ open class MoleculeStackView: Stack<StackModel> {
stackItems.append(stackItem)
models.append(stackItemModel)
}
if let stackModel = stackModel {
stackModel.molecules = models
} else {
model = StackModel(molecules: models)
}
restack()
}
// MARK: - Adding to stack
/// Can be subclassed to create views when we get stack item models and have no views yet
open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
open func createStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let stackItemModels = stackModel?.molecules else { return }
for model in stackItemModels {
if let stackItem = MoleculeObjectMapping.shared()?.createMolecule(model, delegateObject: delegateObject, additionalData: additionalData) as? MoleculeStackItem {
stackItems.append(stackItem)
@ -43,8 +49,8 @@ open class MoleculeStackView: Stack<StackModel> {
}
}
open override func setStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
// If the items in the stack are different, clear them, create new ones.
open override func setStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
// If the items in the stack are different, clear them, create new ones.
if (previousModel == nil) || Self.nameForReuse(with: previousModel!, delegateObject) != Self.nameForReuse(with: model, delegateObject) {
removeAllItemViews()
stackItems = []

View File

@ -6,8 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProtocol) {
//--------------------------------------------------
@ -24,7 +22,7 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
//--------------------------------------------------
// MARK: - Helpers
//--------------------------------------------------
open func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) {
let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant)
@ -34,8 +32,11 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
/// Restacks the existing items.
open func restack() {
removeAllItemViews()
guard let stackModel = stackModel else { return }
let stackItems = self.stackItems
self.stackItems = []
let lastItemIndex = stackModel.molecules.lastIndex { !$0.gone }
@ -48,6 +49,7 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
isAccessibilityElement = false
var accessibleViews: [Any] = []
for (index, view) in stackItems.enumerated() where !stackModel.molecules[index].gone {
accessibleViews.append(view)
}
@ -57,20 +59,20 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
/// Removes all stack items views from the view.
open func removeAllItemViews() {
for item in stackItems {
item.removeFromSuperview()
}
stackItems.forEach { $0.removeFromSuperview() }
}
/// A convenience function for when the stackItems are containers and we want to update them based on the contained molecules models. If model is nil, stackItem is set to gone. Restacks if necessary.
open func updateContainedMolecules(with models: [MoleculeModelProtocol?], _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard var stackModel = stackModel else { return }
var needsRestack = false
for (index, item) in stackItems.enumerated() {
guard let container = item as? UIView & ContainerProtocol,
let contained = container.view as? MoleculeViewProtocol else {
continue
}
let contained = container.view as? MoleculeViewProtocol
else { continue }
if let model = models[index] {
contained.set(with: model, delegateObject, additionalData)
if stackModel.molecules[index].gone {
@ -91,7 +93,7 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public override init(frame: CGRect) {
super.init(frame: frame)
}
@ -120,12 +122,15 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
/// Returns a Stack created with a StackModel and StackItems containing the passed in views.
public static func createStack(with views: [UIView], axis: NSLayoutConstraint.Axis? = nil, spacing: CGFloat? = nil) -> Stack<StackModel> {
var items: [StackItem] = []
var models: [StackItemModel] = []
for view in views {
items.append(StackItem(andContain: view))
models.append(StackItemModel())
}
let model = StackModel(molecules: models, axis: axis, spacing: spacing)
return Stack<StackModel>(with: model, stackItems: items)
}
@ -134,10 +139,12 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
public static func createStack(with viewModels:[(view: UIView, model: StackItemModel)], axis: NSLayoutConstraint.Axis? = nil, spacing: CGFloat? = nil) -> Stack<StackModel> {
var stackItems: [StackItem] = []
var models: [StackItemModel] = []
for item in viewModels {
stackItems.append(StackItem(andContain: item.view))
models.append(item.model)
}
let model = StackModel(molecules: models, axis: axis, spacing: spacing)
return Stack<StackModel>(with: model, stackItems: stackItems)
}
@ -145,10 +152,12 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
//--------------------------------------------------
// MARK: - MFViewProtocol
//--------------------------------------------------
open override func setupView() {
super.setupView()
guard contentView.superview == nil else { return }
MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0)
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .clear
@ -168,7 +177,7 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
open override func reset() {
super.reset()
backgroundColor = .clear
@ -223,13 +232,16 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
}
open override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
guard let model = model as? T else { return nil }
var modules: [String] = []
for case let item in model.molecules {
if let modulesForMolecule = (MoleculeObjectMapping.shared()?.getMoleculeClass(item))?.requiredModules(with: item, delegateObject, error: error) {
modules += modulesForMolecule
}
}
return modules.count > 0 ? modules : nil
}
@ -239,7 +251,9 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
/// Can be subclassed to set stack items with model when we already have views
open func setStackItemsFromModel(_ model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let models = stackModel?.molecules else { return }
for (index, element) in models.enumerated() {
(stackItems[index] as? MoleculeViewProtocol)?.set(with: element, delegateObject, additionalData)
}
@ -250,31 +264,39 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
//--------------------------------------------------
/// Sets the stack with StackItems containing the passed in views and creates a StackModel with StackItems.
open func setAndCreateModel(with views: [UIView]) {
var stackItems: [StackItem] = []
var models: [StackItemModel] = []
for view in views {
stackItems.append(StackItem(andContain: view))
models.append(StackItemModel())
}
self.stackItems = stackItems
model = StackModel(molecules: models)
}
/// Sets the stack with StackItems containing the passed in views and sets the StackModel with models.
open func set(with viewModels:[(view: UIView, model: StackItemModel)]) {
guard var stackModel = self.stackModel else { return }
var stackItems: [StackItem] = []
var models: [StackItemModel] = []
for item in viewModels {
stackItems.append(StackItem(andContain: item.view))
models.append(item.model)
}
stackModel.molecules = models
self.stackItems = stackItems
}
/// Gets the percent modifier. This value is used to help properly calculate percent for stack items when spacing is involved.
private func getTotalSpace() -> CGFloat {
guard let stackModel = stackModel else { return 0.0 }
var totalSpace: CGFloat = 0.0
var firstMoleculeFound = false
@ -289,11 +311,13 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
totalSpace += (stackModel.useStackSpacingBeforeFirstItem ? spacing : stackItemModel.spacing ?? 0)
}
}
return totalSpace
}
/// Adds the stack item view
private func addView(_ view: UIView,_ model: StackItemModelProtocol, totalSpacing: CGFloat, lastItem: Bool) {
guard let stackModel = self.stackModel else { return }
guard !model.gone else {
// Gone views do not show
@ -302,15 +326,15 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
}
contentView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
let spacing = model.spacing ?? stackModel.spacing
if let container = view as? ContainerProtocol {
let verticalAlignment = (model as? ContainerModelProtocol)?.verticalAlignment ?? (container.view as? MVMCoreUIViewConstrainingProtocol)?.verticalAlignment?() ?? (model.percent == nil && stackModel.axis == .vertical ? .fill : (stackModel.axis == .vertical ? .leading : .center))
let horizontalAlignment = (model as? ContainerModelProtocol)?.horizontalAlignment ?? (container.view as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() ?? (stackModel.axis == .vertical || model.percent == nil ? .fill : .leading)
container.alignHorizontal(horizontalAlignment)
container.alignVertical(verticalAlignment)
container.alignHorizontal(horizontalAlignment)
container.alignVertical(verticalAlignment)
}
let first = contentView.subviews.count == 1
if stackModel.axis == .vertical {
if first {
@ -350,6 +374,7 @@ open class Stack<T>: Container where T: (StackModelProtocol & MoleculeModelProto
pinView(contentView, toView: view, attribute: .right, relation: .equal, priority: .required, constant: 0)
}
}
stackItems.append(view)
}
}

View File

@ -6,20 +6,28 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
@objcMembers public class StackModel: ContainerModel, StackModelProtocol, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
static let defaultSpacing: CGFloat = 16.0
public class var identifier: String {
return "stack"
}
public var backgroundColor: Color?
public var molecules: [StackItemModelProtocol & MoleculeModelProtocol]
public var axis: NSLayoutConstraint.Axis = .vertical
public var spacing: CGFloat = StackModel.defaultSpacing
public var useStackSpacingBeforeFirstItem = false
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(molecules: [StackItemModelProtocol & MoleculeModelProtocol], axis: NSLayoutConstraint.Axis? = nil, spacing: CGFloat? = nil) {
self.molecules = molecules
if let axis = axis {
@ -30,7 +38,11 @@ import Foundation
}
super.init()
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
@ -38,7 +50,11 @@ import Foundation
case axis
case spacing
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
molecules = try typeContainer.decodeModels(codingKey: .molecules)
@ -51,7 +67,7 @@ import Foundation
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)

View File

@ -101,7 +101,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
return estimatedHeight
}
open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return moleculesInfo?.count ?? 0
}

View File

@ -8,10 +8,19 @@
import UIKit
open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
var observer: NSKeyValueObservation?
public var templateModel: StackPageTemplateModel?
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func handleNewData() {
topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
@ -20,9 +29,9 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
// For subclassing the model.
open func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> StackPageTemplateModel {
return try decoder.decode(StackPageTemplateModel.self, from: data)
return try decoder.decode(StackPageTemplateModel.self, from: data)
}
open override func parsePageJSON() throws {
try parseTemplate(json: loadObject?.pageJSON)
try super.parsePageJSON()
@ -48,21 +57,23 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
open override func viewForTop() -> UIView? {
guard let headerModel = templateModel?.header,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) else {
return nil
}
let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar)
else { return nil }
return molecule
}
open override func viewForMiddle() -> UIView? {
guard let moleculeStackModel = templateModel?.moleculeStack else { return nil }
// By default: Stack template stack has vertical space before the first item, dynamic stack items have default horizontal padding.
let stack = MoleculeStackView(frame: .zero)
moleculeStackModel.useStackSpacingBeforeFirstItem = true
for stackItem in moleculeStackModel.molecules {
guard let stackItem = stackItem as? MoleculeStackItemModel,
stackItem.useHorizontalMargins == nil else { continue }
stackItem.useHorizontalMargins == nil
else { continue }
stackItem.useHorizontalMargins = true
}
stack.set(with: moleculeStackModel, delegateObject() as? MVMCoreUIDelegateObject, nil)
@ -71,14 +82,16 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
override open func viewForBottom() -> UIView? {
guard let footerModel = templateModel?.footer,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) else {
return nil
}
let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar)
else { return nil }
return molecule
}
// MARK: - cache handling
//--------------------------------------------------
// MARK: - Cache Handling
//--------------------------------------------------
/// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map.
open func updateRequiredModules() {
if let requiredModules = requiredModules(), let pageType = pageType {

View File

@ -100,14 +100,15 @@ public typealias ButtonAction = (Button) -> ()
self.model = model
if let backgroundColor = model.backgroundColor {
self.backgroundColor = backgroundColor.uiColor
self.backgroundColor = backgroundColor.uiColor
}
if let model = model as? EnableableModelProtocol {
isEnabled = model.enabled
}
guard let model = model as? ButtonModelProtocol else { return }
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
}
@ -146,6 +147,8 @@ extension Button: MVMCoreViewProtocol {
/// Will be called only once.
open func setupView() {
isAccessibilityElement = true
accessibilityTraits = .button
translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false
titleLabel?.numberOfLines = 0

View File

@ -87,7 +87,7 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo
open func reset() {
molecule?.reset()
backgroundColor = .mvmWhite
backgroundColor = .clear
width = nil
}

View File

@ -199,7 +199,7 @@ import UIKit
caret.accessibilityTraits = .button
caret.size = .small(.vertical)
if let size = caret.size?.dimensions() {
caret.frame = CGRect(origin: CGPoint.zero, size: size)
caret.frame = CGRect(origin: .zero, size: size)
caretViewWidthSizeObject = MFSizeObject(standardSize: size.width, standardiPadPortraitSize: 9)
caretViewHeightSizeObject = MFSizeObject(standardSize: size.height, standardiPadPortraitSize: 16)
}

View File

@ -9,6 +9,7 @@
import UIKit
import MVMAnimationFramework
open class ThreeLayerTableViewController: ProgrammaticTableViewController {
// The three main views
private var topView: UIView?

View File

@ -485,8 +485,8 @@ import UIKit
// Needed otherwise when subclassed, the extension gets called.
open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {}
open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { return nil }
open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) {}
open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) {}
open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { }
open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { }
//--------------------------------------------------
// MARK: - MVMCoreUIDetailViewProtocol

View File

@ -8,7 +8,11 @@
import UIKit
open class Container: View, ContainerProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var view: UIView?
let containerHelper = ContainerHelper()
@ -17,10 +21,15 @@ open class Container: View, ContainerProtocol {
get { return model as? ContainerModelProtocol }
}
// MARK:- MoleculeViewProtocol
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.set(with: model, delegateObject, additionalData)
guard let containerModel = model as? ContainerModelProtocol else { return }
containerHelper.set(with: containerModel, for: view as? MVMCoreUIViewConstrainingProtocol)
}
@ -29,7 +38,10 @@ open class Container: View, ContainerProtocol {
(view as? MoleculeViewProtocol)?.reset()
}
// MARK:- ContainerProtocol
//--------------------------------------------------
// MARK: - ContainerProtocol
//--------------------------------------------------
open func alignHorizontal(_ alignment: UIStackView.Alignment) {
containerHelper.alignHorizontal(alignment)
}
@ -45,15 +57,17 @@ open class Container: View, ContainerProtocol {
// MARK: - MVMCoreViewProtocol
public extension Container {
override func updateView(_ size: CGFloat) {
super.updateView(size)
(view as? MVMCoreViewProtocol)?.updateView(size)
containerHelper.updateViewMargins(self, model: containerModel, size: size)
}
/// Will be called only once.
override func setupView() {
super.setupView()
isAccessibilityElement = false
backgroundColor = .clear
}

View File

@ -87,3 +87,8 @@
"CountDownHours" = " hours";
"CountDownMins" = " mins";
"CountDownSecs" = " secs";
// MARK: Star
"star" = "Star";
"star_percent" = "%d percent progress";
"stars_filled" = "%.1f out of %d stars";

View File

@ -66,3 +66,7 @@
"CountDownHours" = " horas";
"CountDownMins" = " min";
"CountDownSecs" = " seg";
// Star
"star" = "Estrella";
"star_percent" = "%@ porcentaje de progreso";
"stars_filled" = "%.1f de %d estrellas";

View File

@ -70,3 +70,8 @@
"CountDownHours" = " horas";
"CountDownMins" = " min";
"CountDownSecs" = " seg";
// Star
"star" = "Estrella";
"star_percent" = "%@ porcentaje de progreso";
"stars_filled" = "%.1f de %d estrellas";