Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions Sources/FluentUI_iOS/Components/CommandBar/CommandBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public protocol CommandBarDelegate: AnyObject {
Provide `itemGroups` in `init` to set the buttons in the CommandBar. Optional `leadingItemGroups` and `trailingItemGroups` add buttons in leading and trailing positions. Each `CommandBarItem` will be represented as a button.
*/
@objc(MSFCommandBar)
public class CommandBar: UIView, TokenizedControl {
public class CommandBar: UIView, Shadowable, TokenizedControl {
// Hierarchy:
//
// isScrollable = true
Expand Down Expand Up @@ -97,15 +97,18 @@ public class CommandBar: UIView, TokenizedControl {
commandBarContainerStackView = UIStackView()
commandBarContainerStackView.axis = .horizontal
commandBarContainerStackView.translatesAutoresizingMaskIntoConstraints = false
commandBarContainerStackView.isLayoutMarginsRelativeArrangement = true

super.init(frame: .zero)

configureHierarchy()
updateBackgroundColor()
updateShadow()

// Update appearance whenever `tokenSet` changes.
tokenSet.registerOnUpdate(for: self) { [weak self] in
self?.updateBackgroundColor()
self?.updateShadow()
self?.updateButtonTokens()
}
}
Expand Down Expand Up @@ -147,7 +150,11 @@ public class CommandBar: UIView, TokenizedControl {
public override func layoutSubviews() {
super.layoutSubviews()

if #available(iOS 26, *) {
layer.cornerRadius = bounds.height / 2
}
updateShadow()
updateScrollViewShadow()
}

#if DEBUG
Expand All @@ -171,6 +178,10 @@ public class CommandBar: UIView, TokenizedControl {
}
#endif

// MARK: - Shadowable
public var ambientShadow: CALayer?
public var keyShadow: CALayer?

// MARK: - TokenizedControl

public typealias TokenSetKeyType = CommandBarTokenSet.Tokens
Expand Down Expand Up @@ -296,6 +307,7 @@ public class CommandBar: UIView, TokenizedControl {
commandBarContainerStackView.addArrangedSubview(leadingCommandGroupsView)
commandBarContainerStackView.addArrangedSubview(containerView)
commandBarContainerStackView.addArrangedSubview(trailingCommandGroupsView)
commandBarContainerStackView.directionalLayoutMargins = stackViewLayoutMargins()

updateViewHierarchy()
updateMainCommandGroupsViewConstraints()
Expand Down Expand Up @@ -358,6 +370,20 @@ public class CommandBar: UIView, TokenizedControl {
NSLayoutConstraint.activate(mainCommandGroupsViewConstraints)
}

private func stackViewLayoutMargins() -> NSDirectionalEdgeInsets {
var padding: CGFloat
if #available(iOS 26, *) {
padding = CommandBarTokenSet.barInsets
} else {
padding = 0
}
return NSDirectionalEdgeInsets(top: 0,
leading: leadingCommandGroupsView.isHidden ? 0 : padding,
bottom: 0,
trailing: trailingCommandGroupsView.isHidden ? 0 : padding,
)
}

private func scrollViewContentInset() -> UIEdgeInsets {
let fixedButtonSpacing = CommandBarTokenSet.itemInterspace
return UIEdgeInsets(top: 0,
Expand All @@ -367,7 +393,7 @@ public class CommandBar: UIView, TokenizedControl {
)
}

private func updateShadow() {
private func updateScrollViewShadow() {
var locations: [CGFloat] = [0, 0, 1]

if !leadingCommandGroupsView.isHidden {
Expand All @@ -385,6 +411,11 @@ public class CommandBar: UIView, TokenizedControl {
containerMaskLayer.locations = locations.map { NSNumber(value: Float($0)) }
}

private func updateShadow() {
let shadowInfo = tokenSet[.shadow].shadowInfo
shadowInfo.applyShadow(to: self)
}

private func updateBackgroundColor() {
backgroundColor = tokenSet[.backgroundColor].uiColor
}
Expand All @@ -400,6 +431,7 @@ public class CommandBar: UIView, TokenizedControl {
commandGroupsView.itemGroups = items ?? []

commandGroupsView.isHidden = commandGroupsView.itemGroups.isEmpty
commandBarContainerStackView.directionalLayoutMargins = stackViewLayoutMargins()
scrollView.contentInset = scrollViewContentInset()
}
}
Expand All @@ -408,7 +440,7 @@ public class CommandBar: UIView, TokenizedControl {

extension CommandBar: UIScrollViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
updateShadow()
updateScrollViewShadow()

delegate?.commandBarDidScroll(self)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ class CommandBarButton: UIButton {
var buttonConfiguration = UIButton.Configuration.plain()
buttonConfiguration.imagePadding = CommandBarTokenSet.buttonImagePadding
buttonConfiguration.contentInsets = CommandBarTokenSet.buttonContentInsets
buttonConfiguration.background.cornerRadius = 0
if #available(iOS 26, *) {
buttonConfiguration.background.cornerRadius = tokenSet[.cornerRadius].float
} else {
buttonConfiguration.background.cornerRadius = 0
}
configuration = buttonConfiguration

setContentHuggingPriority(.required, for: .horizontal)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class CommandBarButtonGroupView: UIView {
stackView.spacing = CommandBarTokenSet.itemInterspace

stackView.clipsToBounds = true
stackView.layer.cornerRadius = tokenSet[.groupBorderRadius].float
stackView.layer.cornerRadius = tokenSet[.cornerRadius].float
stackView.layer.cornerCurve = .continuous
return stackView
}()
Expand All @@ -83,8 +83,10 @@ class CommandBarButtonGroupView: UIView {
stackView.axis = .vertical

stackView.addArrangedSubview(buttonStackView)
if title != nil {
stackView.addArrangedSubview(groupLabel)
if #unavailable(iOS 26) {
if title != nil {
stackView.addArrangedSubview(groupLabel)
}
}
addSubview(stackView)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ public enum CommandBarToken: Int, TokenSetKey {
/// The background color of the Command Bar.
case backgroundColor

/// The border radius for each group of item(s) inside the Command Bar.
case groupBorderRadius
/// The corner radius for each group of item(s) inside the Command Bar.
/// On iOS 26+, this is also the corner radius for each Command Bar Button.
case cornerRadius

/// The background color of a single Command Bar Item when in rest.
case itemBackgroundColorRest
Expand Down Expand Up @@ -47,6 +48,9 @@ public enum CommandBarToken: Int, TokenSetKey {

/// The font of a Command Bar Item Group label.
case itemGroupLabelFont

/// The shadows used by the `CommandBar`.
case shadow
}

/// Design token set for the `CommandBar` control.
Expand All @@ -57,11 +61,15 @@ public class CommandBarTokenSet: ControlTokenSet<CommandBarToken> {
case .backgroundColor:
return .uiColor { theme.color(.background2) }

case .groupBorderRadius:
case .cornerRadius:
return .float { GlobalTokens.corner(.radius120) }

case .itemBackgroundColorRest:
return .uiColor { theme.color(.background5) }
if #available(iOS 26, *) {
return .uiColor { .clear }
} else {
return .uiColor { theme.color(.background5) }
}

case .itemBackgroundColorHover:
return .uiColor { theme.color(.background5) }
Expand Down Expand Up @@ -92,6 +100,13 @@ public class CommandBarTokenSet: ControlTokenSet<CommandBarToken> {

case .itemGroupLabelFont:
return .uiFont { theme.typography(.caption2, adjustsForContentSizeCategory: false) }

case .shadow:
if #available(iOS 26, *) {
return .shadowInfo { theme.shadow(.shadow08) }
} else {
return .shadowInfo { theme.shadow(.clear) }
}
}
}
}
Expand All @@ -101,7 +116,13 @@ public class CommandBarTokenSet: ControlTokenSet<CommandBarToken> {

extension CommandBarTokenSet {
/// The spacing between each Command Bar Group.
static let groupInterspace: CGFloat = GlobalTokens.spacing(.size80)
static var groupInterspace: CGFloat {
if #available(iOS 26, *) {
GlobalTokens.spacing(.size20)
} else {
GlobalTokens.spacing(.size80)
}
}

/// The spacing between each Command Bar Group for iPad.
static let groupInterspaceWide: CGFloat = GlobalTokens.spacing(.size160)
Expand All @@ -116,7 +137,13 @@ extension CommandBarTokenSet {
static let dismissGradientWidth: CGFloat = GlobalTokens.spacing(.size160)

/// The edge inset values for the Command Bar.
static let barInsets: CGFloat = GlobalTokens.spacing(.size80)
static var barInsets: CGFloat {
if #available(iOS 26, *) {
GlobalTokens.spacing(.size40)
} else {
GlobalTokens.spacing(.size80)
}
}

/// The edge inset values for the Command Bar Button.
static let buttonContentInsets = NSDirectionalEdgeInsets(top: 8.0,
Expand Down
Loading