Skip to content

Commit c1c04a9

Browse files
authored
[FluentList, ListItem, BottomSheetController] Updates for glass support (#2281)
* FluentList and BottomSheetController update for glass support * Update demo controller to showcase glass support * update tests
1 parent d37085f commit c1c04a9

7 files changed

Lines changed: 81 additions & 15 deletions

File tree

Demos/FluentUIDemo_iOS/FluentUI.Demo/Demos/BottomSheetDemoController.swift

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class BottomSheetDemoController: DemoController {
1616

1717
let optionTableView = UITableView(frame: .zero, style: .insetGrouped)
1818
optionTableView.translatesAutoresizingMaskIntoConstraints = false
19+
optionTableView.register(TableViewCell.self, forCellReuseIdentifier: TableViewCell.identifier)
1920
optionTableView.register(BooleanCell.self, forCellReuseIdentifier: BooleanCell.identifier)
2021
optionTableView.register(ActionsCell.self, forCellReuseIdentifier: ActionsCell.identifier)
2122
optionTableView.dataSource = self
@@ -42,6 +43,13 @@ class BottomSheetDemoController: DemoController {
4243
private func rebuildSheet() {
4344
// Tear down existing sheet
4445
if let existing = bottomSheetViewController {
46+
let contentViewController = swiftUIHostingController ?? contentNavigationController
47+
if contentViewController.parent === existing {
48+
contentViewController.willMove(toParent: nil)
49+
contentViewController.view.removeFromSuperview()
50+
contentViewController.removeFromParent()
51+
}
52+
4553
existing.willMove(toParent: nil)
4654
existing.view.removeFromSuperview()
4755
existing.removeFromParent()
@@ -58,17 +66,20 @@ class BottomSheetDemoController: DemoController {
5866
hostingVC.view.translatesAutoresizingMaskIntoConstraints = false
5967
swiftUIHostingController = hostingVC
6068

61-
sheetController = BottomSheetController(expandedContentView: hostingVC.view)
69+
sheetController = BottomSheetController(expandedContentView: hostingVC.view, style: bottomSheetStyle)
6270
sheetController.isFlexibleHeight = true
6371
sheetController.shouldHideCollapsedContent = false
6472
sheetController.prioritizesSheetPanWhenCollapsed = true
6573
} else {
66-
sheetController = BottomSheetController(headerContentView: headerView, expandedContentView: contentNavigationController.view)
74+
sheetController = BottomSheetController(headerContentView: headerView,
75+
expandedContentView: contentNavigationController.view,
76+
style: bottomSheetStyle)
6777
sheetController.hostedScrollView = personaListView
6878
sheetController.headerContentHeight = BottomSheetDemoController.headerHeight
6979
}
7080

7181
sheetController.delegate = self
82+
sheetController.usesAdaptiveBackground = useAdaptiveBackground
7283
sheetController.collapsedHeightResolver = { context in
7384
return context.containerTraitCollection.verticalSizeClass == .regular ? 100 : 70
7485
}
@@ -115,6 +126,19 @@ class BottomSheetDemoController: DemoController {
115126
bottomSheetViewController?.shouldAlwaysFillWidth = sender.isOn
116127
}
117128

129+
@objc private func toggleAdaptiveBackground(_ sender: BooleanCell) {
130+
useAdaptiveBackground = sender.isOn
131+
bottomSheetViewController?.usesAdaptiveBackground = sender.isOn
132+
}
133+
134+
private func selectBottomSheetStyle(_ style: BottomSheetControllerStyle) {
135+
guard bottomSheetStyle != style else {
136+
return
137+
}
138+
139+
bottomSheetStyle = style
140+
rebuildSheet()
141+
}
118142

119143
@objc private func toggleCollapsedContentHiding(_ sender: BooleanCell) {
120144
bottomSheetViewController?.shouldHideCollapsedContent.toggle()
@@ -319,19 +343,42 @@ class BottomSheetDemoController: DemoController {
319343

320344
private var useSwiftUIContent: Bool = false
321345

346+
private var useAdaptiveBackground: Bool = false
347+
348+
private var bottomSheetStyle: BottomSheetControllerStyle = .primary
349+
350+
private func makeStyleButton() -> UIButton {
351+
let button = Button()
352+
button.showsMenuAsPrimaryAction = true
353+
let styleOptions: [(title: String, style: BottomSheetControllerStyle)] = [
354+
("Primary", .primary),
355+
("Glass", .glass)
356+
]
357+
button.style = .subtle
358+
359+
button.setTitle(styleOptions.first(where: { $0.style == bottomSheetStyle })?.title, for: .normal)
360+
button.menu = UIMenu(title: "Style", options: .singleSelection, children: styleOptions.map { option in
361+
UIAction(title: option.title, state: option.style == bottomSheetStyle ? .on : .off) { [weak self] _ in
362+
self?.selectBottomSheetStyle(option.style)
363+
}
364+
})
365+
return button
366+
}
367+
322368
private let fpsOverlay: FPSOverlayView = {
323369
let overlay = FPSOverlayView()
324370
overlay.isHidden = true
325371
return overlay
326372
}()
327373

328-
329374
private var demoOptionItems: [[DemoItem]] {
330375
[
331376
[
377+
DemoItem(title: "Style", type: .menu, action: nil),
332378
DemoItem(title: "Expandable", type: .boolean, action: #selector(toggleExpandable), isOn: bottomSheetViewController?.isExpandable ?? true),
333379
DemoItem(title: "Hidden", type: .boolean, action: #selector(toggleHidden), isOn: bottomSheetViewController?.isHidden ?? false),
334380
DemoItem(title: "Should always fill width", type: .boolean, action: #selector(toggleFillWidth), isOn: bottomSheetViewController?.shouldAlwaysFillWidth ?? false),
381+
DemoItem(title: "Use adaptive background", type: .boolean, action: #selector(toggleAdaptiveBackground), isOn: bottomSheetViewController?.usesAdaptiveBackground ?? false),
335382
DemoItem(title: "Hide collapsed content", type: .boolean, action: #selector(toggleCollapsedContentHiding), isOn: collapsedContentHidingEnabled),
336383
DemoItem(title: "Flexible sheet height", type: .boolean, action: #selector(toggleFlexibleSheetHeight), isOn: bottomSheetViewController?.isFlexibleHeight ?? false),
337384
DemoItem(title: "Use custom handle accessibility label", type: .boolean, action: #selector(toggleHandleUsingCustomAccessibilityLabel), isOn: isHandleUsingCustomAccessibilityLabel),
@@ -382,6 +429,7 @@ class BottomSheetDemoController: DemoController {
382429
private enum DemoItemType {
383430
case action
384431
case boolean
432+
case menu
385433
case stepper
386434
}
387435

@@ -456,6 +504,13 @@ extension BottomSheetDemoController: UITableViewDataSource {
456504
}
457505
cell.bottomSeparatorType = .full
458506
return cell
507+
} else if item.type == .menu {
508+
guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier) as? TableViewCell else {
509+
return UITableViewCell()
510+
}
511+
512+
cell.setup(title: item.title, customAccessoryView: makeStyleButton())
513+
return cell
459514
}
460515

461516
return UITableViewCell()
@@ -518,7 +573,7 @@ extension BottomSheetDemoController: DemoAppearanceDelegate {
518573

519574
struct BottomSheetDemoListContentView: View {
520575
var body: some View {
521-
List {
576+
FluentList {
522577
Text("Cell with Swipe Action")
523578
.swipeActions {
524579
Button(action: {}, label: {
@@ -527,7 +582,7 @@ struct BottomSheetDemoListContentView: View {
527582
}
528583
Text("Cell without Swipe Action")
529584
}
530-
.listStyle(.plain)
585+
.fluentListStyle(.glass)
531586
}
532587
}
533588

Demos/FluentUIDemo_iOS/FluentUI.Demo/Demos/ListItemDemoController_SwiftUI.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ struct ListItemDemoView: View {
110110
Text(".plain").tag(FluentListStyle.plain)
111111
Text(".insetGrouped").tag(FluentListStyle.insetGrouped)
112112
Text(".inset").tag(FluentListStyle.inset)
113+
Text(".glass").tag(FluentListStyle.glass)
113114
}
114115
}
115116

Demos/FluentUIDemo_iOS/FluentUIDemoTests/BottomSheetControllerTest.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class BottomSheetControllerTest: BaseTest {
1616
}
1717

1818
func testExpandable() throws {
19-
let isExpandableSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 0)
19+
let isExpandableSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 1)
2020

2121
isExpandableSwitch.tap()
2222
sleep(1)
@@ -27,22 +27,22 @@ class BottomSheetControllerTest: BaseTest {
2727
let bottomSheet: XCUIElement = app.otherElements.containing(bottomSheetPredicate).element
2828
XCTAssert(bottomSheet.exists)
2929

30-
let isHiddenSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 1)
30+
let isHiddenSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 2)
3131
isHiddenSwitch.tap()
3232
sleep(1)
3333
XCTAssert(!bottomSheet.isHittable)
3434
}
3535

3636
func testFillWidth() throws {
37-
let shouldFillWidthSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 2)
37+
let shouldFillWidthSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 3)
3838

3939
shouldFillWidthSwitch.tap()
4040
sleep(1)
4141
XCTAssert(!app.otherElements.containing(NSPredicate(format: "identifier MATCHES %@", "Bottom Sheet View.*filled width.*")).element.exists)
4242
}
4343

4444
func testCollapsedContent() throws {
45-
let hideCollapsedContentSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 3)
45+
let hideCollapsedContentSwitch: XCUIElement = app.tables.element(boundBy: 0).cells.element(boundBy: 5)
4646

4747
hideCollapsedContentSwitch.tap()
4848
sleep(1)

Sources/FluentUI_iOS/Components/BottomSheet/BottomSheetController.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -832,11 +832,10 @@ public class BottomSheetController: UIViewController, Shadowable, TokenizedContr
832832
@available(iOS 26, *)
833833
private func makeGlassEffect(wantsGlass: Bool) -> UIGlassEffect {
834834
let glassEffect = UIGlassEffect(style: .regular)
835-
// Leave the material untinted for the pure glass look; only tint toward the solid
836-
// background when we want the opaque appearance (e.g. expanded + adaptive background).
837-
if !wantsGlass {
838-
glassEffect.tintColor = FluentTheme.shared.color(.background2)
839-
}
835+
// Tint toward the sheet's glass tint color. In the opaque (expanded) state use the full
836+
// color; in the pure-glass state apply it at 65% alpha so the material shows through.
837+
let tintColor = tokenSet[.backgroundColor].uiColor
838+
glassEffect.tintColor = wantsGlass ? tintColor.withAlphaComponent(0.65) : tintColor
840839
return glassEffect
841840
}
842841

Sources/FluentUI_iOS/Components/BottomSheet/BottomSheetTokenSet.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class BottomSheetTokenSet: ControlTokenSet<BottomSheetToken> {
3333
case .primary:
3434
return theme.color(.background2)
3535
case .glass:
36-
return .clear
36+
return .systemBackground
3737
}
3838
}
3939
case .cornerRadius:

Sources/FluentUI_iOS/Components/List/FluentList.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public enum FluentListStyle {
1313
case plain
1414
case insetGrouped
1515
case inset
16+
/// Uses the `insetGrouped` layout but renders the list and its items with a clear background,
17+
/// allowing content behind the list to show through.
18+
case glass
1619
}
1720

1821
/// This a wrapper around `SwiftUI.List` that has fluent style applied. It is intended to be used in conjunction with `FluentUI.FluentListSection` and `FluentUI.ListItem`
@@ -56,6 +59,12 @@ public struct FluentList<ListContent: View>: View {
5659
.listStyle(.plain)
5760
.scrollContentBackground(.hidden)
5861
.background(ListItemTokenSet.listBackgroundColor(for: .plain))
62+
case .glass:
63+
list
64+
.listStyle(.insetGrouped)
65+
.scrollContentBackground(.hidden)
66+
.listSectionSpacing(GlobalTokens.spacing(.size160))
67+
.environment(\.defaultMinListHeaderHeight, GlobalTokens.spacing(.size320))
5968
}
6069
}
6170

Sources/FluentUI_iOS/Components/List/ListItem.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ public struct ListItem<LeadingContent: View,
327327
styleType = .plain
328328
case .insetGrouped:
329329
styleType = .grouped
330+
case .glass:
331+
styleType = .clear
330332
}
331333
}
332334
return styleType

0 commit comments

Comments
 (0)