Merge branch 'develop' into feature/pagination
This commit is contained in:
commit
9822d4e777
@ -102,7 +102,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
||||
/// Instead of use labelText and labelTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
|
||||
open var labelAttributedText: NSAttributedString? {
|
||||
didSet {
|
||||
label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true)
|
||||
label.attributedText = labelAttributedText
|
||||
setNeedsUpdate()
|
||||
}
|
||||
@ -117,7 +116,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
||||
/// Instead of use childText and childTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
|
||||
open var childAttributedText: NSAttributedString? {
|
||||
didSet {
|
||||
childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true)
|
||||
childLabel.attributedText = childAttributedText
|
||||
setNeedsUpdate()
|
||||
}
|
||||
|
||||
@ -34,10 +34,17 @@ public final class SelfSizingCollectionView: UICollectionView {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private var contentSizeObservation: NSKeyValueObservation?
|
||||
private var collectionViewHeight: NSLayoutConstraint?
|
||||
private var anyCancellable: AnyCancellable?
|
||||
|
||||
private var contentSizeSubject = CurrentValueSubject<CGSize, Never>(.zero)
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public var contentSizePublisher: AnyPublisher<CGSize, Never> {
|
||||
contentSizeSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
@ -45,7 +52,6 @@ public final class SelfSizingCollectionView: UICollectionView {
|
||||
/// The natural size for the receiving view, considering only properties of the view itself.
|
||||
public override var intrinsicContentSize: CGSize {
|
||||
let contentSize = self.contentSize
|
||||
//print(#function, contentSize)
|
||||
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
||||
}
|
||||
|
||||
@ -67,18 +73,17 @@ public final class SelfSizingCollectionView: UICollectionView {
|
||||
//ensure autoLayout uses intrinsic height
|
||||
setContentHuggingPriority(.required, for: .vertical)
|
||||
setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate()
|
||||
|
||||
// Observing the value of contentSize seems to be the only reliable way to get the contentSize after the collection view lays out its subviews.
|
||||
self.contentSizeObservation = self.observe(\.contentSize, options: [.old, .new]) { [weak self] _, change in
|
||||
// If we don't specify `options: [.old, .new]`, the change.oldValue and .newValue will always be `nil`.
|
||||
if change.newValue != change.oldValue {
|
||||
self?.invalidateIntrinsicContentSize()
|
||||
if let height = change.newValue?.height {
|
||||
self?.collectionViewHeight?.constant = height
|
||||
collectionViewHeight = height(constant: 0, priority: .defaultHigh)
|
||||
|
||||
anyCancellable = self.publisher(for: \.contentSize, options: [.new])
|
||||
.sink { [weak self] compare in
|
||||
guard let self else { return }
|
||||
if compare.height != self.collectionViewHeight?.constant {
|
||||
self.invalidateIntrinsicContentSize()
|
||||
self.collectionViewHeight?.constant = compare.height
|
||||
self.contentSizeSubject.send(compare)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -102,6 +102,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
|
||||
//--------------------------------------------------
|
||||
open func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
backgroundColor = .clear
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
accessibilityCustomActions = []
|
||||
|
||||
@ -9,6 +9,7 @@ import Foundation
|
||||
import UIKit
|
||||
import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
/// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings.
|
||||
@objc(VDSButtonGroup)
|
||||
@ -98,6 +99,8 @@ open class ButtonGroup: View {
|
||||
buttons.forEach { $0.surface = surface }
|
||||
}
|
||||
}
|
||||
|
||||
open var contentSizePublisher: AnyPublisher<CGSize, Never> { collectionView.contentSizePublisher }
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
@ -108,6 +111,7 @@ open class ButtonGroup: View {
|
||||
$0.delegate = self
|
||||
}
|
||||
|
||||
/// CollectionView that renders the array of buttonBase obects.
|
||||
fileprivate lazy var collectionView: SelfSizingCollectionView = {
|
||||
|
||||
return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with {
|
||||
|
||||
@ -42,6 +42,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private enum TextSetMode {
|
||||
case text
|
||||
case attributedText
|
||||
}
|
||||
|
||||
private var textSetMode: TextSetMode = .text
|
||||
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
private var edgeInsets: UIEdgeInsets { textStyle.edgeInsets }
|
||||
@ -102,10 +109,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
||||
//--------------------------------------------------
|
||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||
open var shouldUpdateView: Bool = true
|
||||
|
||||
/// Determines if the label should use its own attributedText property instead of rendering the attributedText propert
|
||||
/// based of other local properties, such as textStyle, textColor, surface, etc... The default value is false.
|
||||
open var useAttributedText: Bool = false
|
||||
|
||||
/// Will determine if a scaled font should be used for the font.
|
||||
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
|
||||
@ -128,19 +131,40 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
||||
|
||||
/// Line break mode for the label, default is set to word wrapping.
|
||||
open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }}
|
||||
|
||||
private var _text: String?
|
||||
|
||||
|
||||
/// Text that will be used in the label.
|
||||
override open var text: String? {
|
||||
get { _text }
|
||||
override open var text: String! {
|
||||
get { super.text }
|
||||
set {
|
||||
if _text != newValue || newValue != attributedText?.string {
|
||||
_text = newValue
|
||||
useAttributedText = false
|
||||
attributes?.removeAll()
|
||||
setNeedsUpdate()
|
||||
textSetMode = .text
|
||||
styleText(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
///AttributedText that will be used in the label.
|
||||
override open var attributedText: NSAttributedString? {
|
||||
get { super.attributedText }
|
||||
set {
|
||||
textSetMode = .attributedText
|
||||
styleAttributedText(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
override open var font: UIFont! {
|
||||
didSet {
|
||||
if let font, initialSetupPerformed {
|
||||
textStyle = TextStyle.convert(font: font)
|
||||
}
|
||||
setNeedsUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
override open var textColor: UIColor! {
|
||||
didSet {
|
||||
if let textColor, initialSetupPerformed {
|
||||
textColorConfiguration = SurfaceColorConfiguration(textColor, textColor).eraseToAnyColorable()
|
||||
}
|
||||
setNeedsUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,13 +186,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
||||
//--------------------------------------------------
|
||||
open func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
//register for ContentSizeChanges
|
||||
NotificationCenter
|
||||
.Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification)
|
||||
.sink { [weak self] notification in
|
||||
self?.setNeedsUpdate()
|
||||
}.store(in: &subscribers)
|
||||
|
||||
backgroundColor = .clear
|
||||
numberOfLines = 0
|
||||
lineBreakMode = .byWordWrapping
|
||||
@ -200,30 +224,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
||||
}
|
||||
|
||||
open func updateView() {
|
||||
if !useAttributedText {
|
||||
if let text {
|
||||
accessibilityCustomActions = []
|
||||
|
||||
//create the primary string
|
||||
let mutableText = NSMutableAttributedString.mutableText(for: text,
|
||||
textStyle: textStyle,
|
||||
useScaledFont: useScaledFont,
|
||||
textColor: textColorConfiguration.getColor(self),
|
||||
alignment: textAlignment,
|
||||
lineBreakMode: lineBreakMode)
|
||||
|
||||
applyAttributes(mutableText)
|
||||
|
||||
//set the attributed text
|
||||
attributedText = mutableText
|
||||
|
||||
//force a drawText
|
||||
setNeedsDisplay()
|
||||
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
restyleText()
|
||||
|
||||
//force a drawText
|
||||
setNeedsDisplay()
|
||||
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
open func updateAccessibility() {
|
||||
@ -269,6 +276,56 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Methods
|
||||
//--------------------------------------------------
|
||||
private func restyleText() {
|
||||
if textSetMode == .text {
|
||||
styleText(text)
|
||||
} else {
|
||||
styleAttributedText(attributedText)
|
||||
}
|
||||
}
|
||||
|
||||
private func styleText(_ newValue: String!) {
|
||||
defer { invalidateIntrinsicContentSize() }
|
||||
guard let newValue else {
|
||||
// We don't need to use attributed text
|
||||
super.attributedText = nil
|
||||
super.text = newValue
|
||||
return
|
||||
}
|
||||
|
||||
accessibilityCustomActions = []
|
||||
|
||||
//create the primary string
|
||||
let mutableText = NSMutableAttributedString.mutableText(for: newValue,
|
||||
textStyle: textStyle,
|
||||
useScaledFont: useScaledFont,
|
||||
textColor: textColorConfiguration.getColor(self),
|
||||
alignment: textAlignment,
|
||||
lineBreakMode: lineBreakMode)
|
||||
|
||||
applyAttributes(mutableText)
|
||||
|
||||
// Set attributed text to match typography
|
||||
super.attributedText = mutableText
|
||||
|
||||
}
|
||||
|
||||
private func styleAttributedText(_ newValue: NSAttributedString?) {
|
||||
defer { invalidateIntrinsicContentSize() }
|
||||
guard let newValue = newValue else {
|
||||
// We don't need any additional styling
|
||||
super.attributedText = newValue
|
||||
return
|
||||
}
|
||||
|
||||
let mutableText = NSMutableAttributedString(attributedString: newValue)
|
||||
|
||||
applyAttributes(mutableText)
|
||||
|
||||
// Modify attributed text to match typography
|
||||
super.attributedText = mutableText
|
||||
}
|
||||
|
||||
private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) {
|
||||
actions = []
|
||||
|
||||
|
||||
@ -100,7 +100,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
|
||||
/// If provided, the RadioBox textAttributedText will be rendered.
|
||||
open var textAttributedText: NSAttributedString? {
|
||||
didSet {
|
||||
textLabel.useAttributedText = !(textAttributedText?.string.isEmpty ?? true)
|
||||
textLabel.attributedText = textAttributedText
|
||||
setNeedsUpdate()
|
||||
}
|
||||
@ -115,7 +114,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
|
||||
/// If provided, the RadioBox subTextAttributedText will be rendered.
|
||||
open var subTextAttributedText: NSAttributedString? {
|
||||
didSet {
|
||||
subTextLabel.useAttributedText = !(subTextAttributedText?.string.isEmpty ?? true)
|
||||
subTextLabel.attributedText = subTextAttributedText
|
||||
setNeedsUpdate()
|
||||
}
|
||||
@ -130,7 +128,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
|
||||
/// If provided, the RadioBox subTextRightAttributedText will be rendered.
|
||||
open var subTextRightAttributedText: NSAttributedString? {
|
||||
didSet {
|
||||
subTextRightLabel.useAttributedText = !(subTextRightAttributedText?.string.isEmpty ?? true)
|
||||
subTextRightLabel.attributedText = subTextRightAttributedText
|
||||
setNeedsUpdate()
|
||||
}
|
||||
|
||||
@ -94,6 +94,7 @@ open class TextView: UITextView, ViewProtocol {
|
||||
//--------------------------------------------------
|
||||
open func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
backgroundColor = .clear
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
accessibilityCustomActions = []
|
||||
|
||||
@ -68,6 +68,7 @@ open class TitleLockup: View {
|
||||
/// Label used to render the eyebrow model.
|
||||
open var eyebrowLabel = Label().with {
|
||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
$0.setContentHuggingPriority(.required, for: .vertical)
|
||||
}
|
||||
|
||||
/// Model used in rendering the eyebrow label.
|
||||
@ -77,6 +78,7 @@ open class TitleLockup: View {
|
||||
/// Label used to render the title model.
|
||||
open var titleLabel = Label().with {
|
||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
$0.setContentHuggingPriority(.required, for: .vertical)
|
||||
$0.accessibilityTraits.insert([.header])
|
||||
}
|
||||
|
||||
@ -87,6 +89,7 @@ open class TitleLockup: View {
|
||||
/// Label used to render the subtitle model.
|
||||
open var subTitleLabel = Label().with {
|
||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
$0.setContentHuggingPriority(.required, for: .vertical)
|
||||
}
|
||||
|
||||
/// Model used in rendering the subtitle label.
|
||||
@ -380,7 +383,7 @@ open class TitleLockup: View {
|
||||
}
|
||||
|
||||
//pin the last view to the bottom of this view
|
||||
previousView?.pinBottom(0, .defaultHigh)
|
||||
previousView?.pinBottom(0)
|
||||
|
||||
//debugging for borders
|
||||
eyebrowLabel.debugBorder(show: hasDebugBorder, color: .green)
|
||||
|
||||
@ -6,15 +6,17 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
/// Enum that is matched up for the Verizon fonts.
|
||||
public enum Font: String, FontProtocol {
|
||||
public enum Font: FontProtocol {
|
||||
case edsBold
|
||||
case edsRegular
|
||||
case dsLight
|
||||
case etxBold
|
||||
case etxRegular
|
||||
|
||||
case custom(UIFont)
|
||||
|
||||
public var fontName: String {
|
||||
switch self {
|
||||
case .edsBold:
|
||||
@ -27,11 +29,32 @@ public enum Font: String, FontProtocol {
|
||||
return "VerizonNHGeTX-Bold"
|
||||
case .etxRegular:
|
||||
return "VerizonNHGeTX-Regular"
|
||||
case .custom(let font):
|
||||
return font.fontName
|
||||
}
|
||||
}
|
||||
|
||||
public static var allCases: [Font] {
|
||||
[.edsBold, .edsRegular, .dsLight, .etxBold, .etxRegular]
|
||||
}
|
||||
|
||||
/// File Extension for each of the Font enums.
|
||||
public var fontFileExtension: String {
|
||||
return "otf"
|
||||
}
|
||||
|
||||
/// Returns a UIFont for the fontName and size given.
|
||||
/// - Parameters:
|
||||
/// - size: Size of the font
|
||||
/// - Returns: UIFont for the fontName and Size.
|
||||
public func font(ofSize size: CGFloat) -> UIFont{
|
||||
DispatchQueue.once(block: { self.register() })
|
||||
switch self {
|
||||
case .custom(let font):
|
||||
return font
|
||||
default:
|
||||
guard let found = UIFont(name: self.fontName, size: size) else { return .systemFont(ofSize: size) }
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,9 +9,10 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
/// Used in Classes that require Fonts
|
||||
public protocol FontProtocol: CaseIterable, RawRepresentable, Hashable {
|
||||
public protocol FontProtocol {
|
||||
var fontFileExtension: String { get }
|
||||
var fontName: String { get }
|
||||
static var allCases: [Self] { get }
|
||||
}
|
||||
|
||||
extension FontProtocol {
|
||||
|
||||
@ -209,6 +209,13 @@ extension TextStyle {
|
||||
boldMicro
|
||||
]
|
||||
}
|
||||
|
||||
public static func convert(font: UIFont) -> TextStyle {
|
||||
guard let found = allCases.first(where: { font.fontName == $0.fontFace.fontName && font.pointSize == $0.pointSize} ) else {
|
||||
return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize)
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
|
||||
extension TextStyle {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user