From 0d6b50fb2fe8153a81ab25a8fb2837b50f221bd9 Mon Sep 17 00:00:00 2001 From: Joanna Qu <55368679+joannaquu@users.noreply.github.com> Date: Fri, 10 Oct 2025 15:23:06 -0700 Subject: [PATCH] update ccb to ios 26 --- .../Components/CommandBar/CommandBar.swift | 38 ++++++++++++++++-- .../CommandBar/CommandBarButton.swift | 6 ++- .../CommandBarButtonGroupView.swift | 8 ++-- .../CommandBar/CommandBarTokenSet.swift | 39 ++++++++++++++++--- 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/Sources/FluentUI_iOS/Components/CommandBar/CommandBar.swift b/Sources/FluentUI_iOS/Components/CommandBar/CommandBar.swift index 74889930f..37bc72d5a 100644 --- a/Sources/FluentUI_iOS/Components/CommandBar/CommandBar.swift +++ b/Sources/FluentUI_iOS/Components/CommandBar/CommandBar.swift @@ -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 @@ -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() } } @@ -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 @@ -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 @@ -296,6 +307,7 @@ public class CommandBar: UIView, TokenizedControl { commandBarContainerStackView.addArrangedSubview(leadingCommandGroupsView) commandBarContainerStackView.addArrangedSubview(containerView) commandBarContainerStackView.addArrangedSubview(trailingCommandGroupsView) + commandBarContainerStackView.directionalLayoutMargins = stackViewLayoutMargins() updateViewHierarchy() updateMainCommandGroupsViewConstraints() @@ -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, @@ -367,7 +393,7 @@ public class CommandBar: UIView, TokenizedControl { ) } - private func updateShadow() { + private func updateScrollViewShadow() { var locations: [CGFloat] = [0, 0, 1] if !leadingCommandGroupsView.isHidden { @@ -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 } @@ -400,6 +431,7 @@ public class CommandBar: UIView, TokenizedControl { commandGroupsView.itemGroups = items ?? [] commandGroupsView.isHidden = commandGroupsView.itemGroups.isEmpty + commandBarContainerStackView.directionalLayoutMargins = stackViewLayoutMargins() scrollView.contentInset = scrollViewContentInset() } } @@ -408,7 +440,7 @@ public class CommandBar: UIView, TokenizedControl { extension CommandBar: UIScrollViewDelegate { public func scrollViewDidScroll(_ scrollView: UIScrollView) { - updateShadow() + updateScrollViewShadow() delegate?.commandBarDidScroll(self) } diff --git a/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButton.swift b/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButton.swift index 67063cc5c..e0bf3c9b2 100644 --- a/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButton.swift +++ b/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButton.swift @@ -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) diff --git a/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButtonGroupView.swift b/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButtonGroupView.swift index ba11357f9..eed0b95d1 100644 --- a/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButtonGroupView.swift +++ b/Sources/FluentUI_iOS/Components/CommandBar/CommandBarButtonGroupView.swift @@ -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 }() @@ -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) diff --git a/Sources/FluentUI_iOS/Components/CommandBar/CommandBarTokenSet.swift b/Sources/FluentUI_iOS/Components/CommandBar/CommandBarTokenSet.swift index 135b0a6d9..de020754e 100644 --- a/Sources/FluentUI_iOS/Components/CommandBar/CommandBarTokenSet.swift +++ b/Sources/FluentUI_iOS/Components/CommandBar/CommandBarTokenSet.swift @@ -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 @@ -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. @@ -57,11 +61,15 @@ public class CommandBarTokenSet: ControlTokenSet { 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) } @@ -92,6 +100,13 @@ public class CommandBarTokenSet: ControlTokenSet { 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) } + } } } } @@ -101,7 +116,13 @@ public class CommandBarTokenSet: ControlTokenSet { 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) @@ -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,