Merge branch 'feature/notificationView' into 'develop'

Notification View

See merge request BPHV_MIPS/vds_ios!148
This commit is contained in:
Bruce, Matt R 2024-02-14 16:57:28 +00:00
commit fce97ddd96
6 changed files with 139 additions and 34 deletions

View File

@ -12,6 +12,7 @@
44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; };
5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; };
5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; };
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; };
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; };
EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; };
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; };
@ -173,6 +174,7 @@
44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = "<group>"; };
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = "<group>"; };
EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = "<group>"; };
EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = "<group>"; };
EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = "<group>"; };
@ -347,6 +349,7 @@
children = (
445BA07729C07B3D0036A7C5 /* Notification.swift */,
44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */,
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */,
);
path = Notification;
sourceTree = "<group>";
@ -928,6 +931,7 @@
files = (
EAEEECA42B1F934600531FC2 /* IconChangeLog.txt in Resources */,
EA3362042891E14D0071C351 /* VerizonNHGeTX-Bold.otf in Resources */,
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */,
EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */,
EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */,
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */,

View File

@ -7,6 +7,7 @@
import Foundation
import UIKit
import Combine
/// UICollectionView subclassed to deal with Changing the size of itself based on its children and layout and changes of its contentSize.
@objc(VDSSelfSizingCollectionView)
@ -34,10 +35,13 @@ public final class SelfSizingCollectionView: UICollectionView {
// MARK: - Private Properties
//--------------------------------------------------
private var contentSizeObservation: NSKeyValueObservation?
private var collectionViewHeight: NSLayoutConstraint?
private var anyCancellable: AnyCancellable?
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
/// The natural size for the receiving view, considering only properties of the view itself.
public override var intrinsicContentSize: CGSize {
let contentSize = self.contentSize
@ -63,12 +67,16 @@ 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
}
}
}
}

View File

@ -9,7 +9,6 @@ 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)
@ -63,7 +62,7 @@ open class ButtonGroup: View {
open var buttons: [ButtonBase] = [] { didSet { setNeedsUpdate() } }
private var _childWidth: ChildWidth?
/// If provided, width of Button components will be rendered based on this value. If omitted, default button widths are rendered.
open var childWidth: ChildWidth? {
get { _childWidth }
@ -103,7 +102,6 @@ open class ButtonGroup: View {
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
fileprivate var collectionViewHeight: NSLayoutConstraint?
fileprivate lazy var positionLayout = ButtonGroupPositionLayout().with {
$0.position = .center
@ -132,8 +130,6 @@ open class ButtonGroup: View {
super.setup()
addSubview(collectionView)
collectionView.pinToSuperView()
collectionViewHeight = heightAnchor.constraint(equalToConstant: VDS.Button.Size.large.height)
collectionViewHeight?.activate()
}
//--------------------------------------------------
@ -184,7 +180,6 @@ open class ButtonGroup: View {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.collectionView.collectionViewLayout.invalidateLayout()
self.collectionViewHeight?.constant = self.collectionView.intrinsicContentSize.height
}
}
}

View File

@ -8,7 +8,6 @@
import Foundation
import UIKit
class ButtonCollectionViewRow {
var attributes = [ButtonLayoutAttributes]()
@ -193,6 +192,9 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
// get the rect size of the button
itemSize = delegate.collectionView(collectionView, sizeForItemAtIndexPath: indexPath)
// ensure the width is not greater than the collectionViewWidth
itemSize.width = min(itemSize.width, collectionViewWidth)
// determine if the current button will fit in the row
let rowItemCount = rows.last?.attributes.count ?? 0
@ -229,7 +231,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
// create the custom layout attribute
let attributes = ButtonLayoutAttributes(spacing: itemSpacing, button: itemButtonBase, forCellWith: indexPath)
attributes.frame = CGRect(x: 0, y: 0, width: itemSize.width, height: itemSize.height)
attributes.frame = CGRect(x: 0, y: 0, width: min(itemSize.width, collectionViewWidth), height: itemSize.height)
// add it to the array
rows.last?.add(attribute: attributes)

View File

@ -65,21 +65,29 @@ open class Notification: View {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .horizontal
$0.spacing = VDSLayout.Spacing.space2X.value
$0.spacing = UIDevice.isIPad ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space2X.value
}
private var labelsView = UIStackView().with {
$0.spacing = VDSLayout.Spacing.space1X.value
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.alignment = .fill
$0.distribution = .equalSpacing
$0.axis = .vertical
}
private var labelButtonView = UIStackView().with {
private var labelButtonView = View().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.distribution = .fillEqually
$0.axis = .vertical
$0.spacing = VDSLayout.Spacing.space2X.value
}
private var labelButtonViewSpacing: CGFloat {
let spacing: CGFloat = UIDevice.isIPad ? 20 : 16
return switch layout {
case .vertical:
0
case .horizontal:
spacing
}
}
internal var onCloseSubscriber: AnyCancellable?
@ -164,8 +172,8 @@ open class Notification: View {
/// Add this attribute determine your type of Notification.
open var style: Style = .info { didSet { setNeedsUpdate()}}
var _layout: Layout = .vertical
private var _layout: Layout = .vertical
/// Determines the orientation of buttons and text in the Notification.
open var layout: Layout {
@ -215,6 +223,15 @@ open class Notification: View {
return 1232
}
private var labelViewWidthConstraint: NSLayoutConstraint?
private var labelViewBottomConstraint: NSLayoutConstraint?
private var labelViewAndButtonViewConstraint: NSLayoutConstraint?
private var buttonViewTopConstraint: NSLayoutConstraint?
private var typeIconWidthConstraint: NSLayoutConstraint?
private var closeIconWidthConstraint: NSLayoutConstraint?
private var buttonGroupCenterYConstraint: NSLayoutConstraint?
private var buttonGroupBottomConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
@ -240,15 +257,34 @@ open class Notification: View {
])
maxWidthConstraint = layoutGuide.widthAnchor.constraint(lessThanOrEqualToConstant: maxViewWidth)
labelButtonView.addArrangedSubview(labelsView)
labelButtonView.addSubview(labelsView)
labelsView
.pinTop()
.pinLeading()
labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: 1.0)
labelViewWidthConstraint?.activate()
labelViewBottomConstraint = labelButtonView.bottomAnchor.constraint(equalTo: labelsView.bottomAnchor)
labelButtonView.addSubview(buttonGroup)
buttonGroup
.pinTrailing()
buttonGroupBottomConstraint = labelButtonView.bottomAnchor.constraint(equalTo: buttonGroup.bottomAnchor)
buttonGroupCenterYConstraint = buttonGroup.centerYAnchor.constraint(equalTo: labelButtonView.centerYAnchor)
labelViewAndButtonViewConstraint = buttonGroup.topAnchor.constraint(equalTo: labelsView.bottomAnchor, constant: VDSLayout.Spacing.space3X.value)
buttonGroup.widthAnchor.constraint(equalTo: labelsView.widthAnchor).activate()
mainStackView.addArrangedSubview(typeIcon)
mainStackView.addArrangedSubview(labelButtonView)
mainStackView.addArrangedSubview(closeButton)
typeIconWidthConstraint = typeIcon.width(constant: typeIcon.size.dimensions.width)
closeIconWidthConstraint = closeButton.width(constant: closeButton.size.dimensions.width)
//labels
titleLabel.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
subTitleLabel.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
//TODO: Need to add setup animation for displaying the Notification view for iOS.
}
/// Resets to default settings.
@ -296,6 +332,12 @@ open class Notification: View {
setConstraints()
}
/// Override to check the screen width to determine cornerRadius
open override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = UIScreen.main.bounds.width == bounds.width ? 0 : 4.0
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
@ -324,7 +366,6 @@ open class Notification: View {
} else {
subTitleLabel.removeFromSuperview()
}
}
private func updateButtons() {
@ -342,28 +383,28 @@ open class Notification: View {
secondaryButton.onClick = secondaryButtonModel.onClick
buttons.append(secondaryButton)
}
labelViewWidthConstraint?.deactivate()
if buttons.isEmpty {
labelsView.setCustomSpacing(0, after: subTitleLabel)
buttonGroup.removeFromSuperview()
buttonGroup.isHidden = true
labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor)
buttonGroup.buttons.removeAll()
} else {
labelsView.setCustomSpacing(VDSLayout.Spacing.space3X.value, after: subTitleLabel)
buttonGroup.buttons = buttons
labelButtonView.axis = layout == .vertical ? .vertical : .horizontal
labelButtonView.addArrangedSubview(buttonGroup)
buttonGroup
.pinLeading()
.pinTrailing()
buttonGroup.isHidden = false
labelViewWidthConstraint = labelsView.widthAnchor.constraint(equalTo: labelButtonView.widthAnchor, multiplier: layout == .vertical ? 1.0 : 0.5, constant: layout == .vertical ? 0 : -labelButtonViewSpacing)
}
labelViewWidthConstraint?.activate()
}
private func setConstraints() {
maxWidthConstraint?.constant = maxViewWidth
maxWidthConstraint?.isActive = UIDevice.isIPad
labelViewAndButtonViewConstraint?.isActive = layout == .vertical && !buttonGroup.buttons.isEmpty
typeIconWidthConstraint?.constant = typeIcon.size.dimensions.width
closeIconWidthConstraint?.constant = closeButton.size.dimensions.width
labelViewBottomConstraint?.isActive = layout == .horizontal || buttonGroup.buttons.isEmpty
buttonGroupCenterYConstraint?.isActive = layout == .horizontal
buttonGroupBottomConstraint?.isActive = layout == .vertical
}
}

View File

@ -0,0 +1,55 @@
MM/DD/YYYY
----------------
12/30/2021
----------------
- Updated Hover and Active state trigger specs. If triggered by mouse, Active same as Hover. If not, Active same as Default.
03/03/2022
----------------
- Initial Brand 3.0 handoff
03/07/2022
----------------
- Added Native positioning examples
05/03/2022
----------------
- Finalized Native Positioning and Triggers sections.
07/28/2022
----------------
- Added note to Anatomy that Border radius is only for Inline Notifications.
11/30/2022
----------------
- Added "(web only)" to any instance of "keyboard focus"
12/13/2022
----------------
- Replaced focus border pixel and style & spacing values with tokens.
01/12/2023
----------------
- Change VDS Button to Button in AnatomyMoved Button combinations from Anatomy to Elements.
04/12/2023
----------------
- Updated hex colors to reflect updated color tokens values.
- Updated visuals on the Native frame to reflect new guideline that the notification surface color should be based on whatever it is placed upon, or based on the color of the top navigation bar when displaying above the global nav. Added spec notation about this also.
05/04/2023
----------------
- Button Icon Threading - CloseButton
- Anatomy section updated with info on Button Icon
- CloseButton updated with VDS Button Icon details
- Updated Viewport section with Button Icon Config
- Removed Elements section. Button combination is now part of Configurations section
- Updated visuals in all sections to cater to changes caused by Button Icon threading
11/21/2023
----------------
- Added hideCloseButton property to align with React component build
- Added dev note to Error variant
- Removed close button from Error variant examples
- Removed Web/App Discrepancies list