From 8c1cf6a682b841fd5ac0754731256d6cdb032f56 Mon Sep 17 00:00:00 2001 From: rdlabo Date: Fri, 23 Oct 2026 18:39:32 +0900 Subject: [PATCH 1/2] fix: apply selection tint to SwiftUI vertical tabs --- .../Components/NativeUIVerticalBars.swift | 3 +++ .../NativeUISnapshotTests.swift | 21 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/capacitor-native-ui/ios/Sources/NativeUIPlugin/Components/NativeUIVerticalBars.swift b/packages/capacitor-native-ui/ios/Sources/NativeUIPlugin/Components/NativeUIVerticalBars.swift index cb74276c0..0b01de1cd 100644 --- a/packages/capacitor-native-ui/ios/Sources/NativeUIPlugin/Components/NativeUIVerticalBars.swift +++ b/packages/capacitor-native-ui/ios/Sources/NativeUIPlugin/Components/NativeUIVerticalBars.swift @@ -241,6 +241,8 @@ private struct NativeUIVerticalBarsView: View { TabView(selection: Binding(get: { model.selection }, set: { model.select($0) })) { ForEach(model.tabs) { item in NativeUIVerticalBarsPage(model: model) + // Keep toolbar and search accents independent of the tab selection. + .tint(nil) .tag(item.id) .tabItem { NativeUIVerticalBarsLabel(item: item) } .modifier(NativeUIVerticalBarsBadge(badge: item.badge)) @@ -249,6 +251,7 @@ private struct NativeUIVerticalBarsView: View { .accessibilityIdentifier(item.id) } } + .tint(model.tabTint.map { Color(uiColor: NativeUIRendering.color($0)) }) .modifier(NativeUIExtensionAccessory(content: model.accessory)) } } diff --git a/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift b/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift index 1bb40cc6e..b7c684880 100644 --- a/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift +++ b/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift @@ -52,6 +52,20 @@ final class NativeUISnapshotTests: XCTestCase { if let tabs = parent as? UITabBarController { return tabs } return parent.children.lazy.compactMap { tabController($0) }.first } + func assertTint(_ actual: UIColor?, _ expected: UIColor?, traits: UITraitCollection, + file: StaticString = #filePath, line: UInt = #line) throws { + func components(_ color: UIColor?) throws -> [CGFloat] { + let color = try XCTUnwrap(color, file: file, line: line).resolvedColor(with: traits) + var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 + XCTAssertTrue(color.getRed(&red, green: &green, blue: &blue, alpha: &alpha), file: file, line: line) + return [red, green, blue, alpha] + } + // SwiftUI may resolve a dynamic UIColor into a different RGB color + // space with tiny rounding differences. Compare the rendered components. + for (actual, expected) in zip(try components(actual), try components(expected)) { + XCTAssertEqual(actual, expected, accuracy: 0.001, file: file, line: line) + } + } var systemTint: UIColor? for (index, tint) in ([nil, "rgb(255, 0, 0)", "rgb(255, 0, 0)", "rgb(128, 0, 128)", nil] as [String?]).enumerated() { var value = control(["kind": "tab-bar", "placement": "vertical-bars", "items": [ @@ -65,7 +79,12 @@ final class NativeUISnapshotTests: XCTestCase { try await Task.sleep(nanoseconds: 500_000_000) let tabs = try XCTUnwrap(tabController(owner)) if index == 0 { systemTint = tabs.tabBar.tintColor } - XCTAssertEqual(tabs.tabBar.tintColor, tint.map { NativeUIRendering.color($0) } ?? systemTint) + try assertTint(tabs.tabBar.tintColor, tint.map { NativeUIRendering.color($0) } ?? systemTint, traits: tabs.traitCollection) + if let tint { + // On iOS 27.1, the visible SwiftUI rail follows its controller's + // accent; setting only the legacy UITabBar property leaves it blue. + try assertTint(tabs.view.tintColor, NativeUIRendering.color(tint), traits: tabs.traitCollection) + } XCTAssertEqual(tabs.selectedIndex, index % 2) XCTAssertEqual(tabs.traitCollection.userInterfaceStyle, index == 3 ? .dark : .light) XCTAssertNil(tabs.tabBar.items?.first?.standardAppearance, "Keep the native background and state appearances") From e4df6c7af05fa760f2425f1eb08b9b1f6a77a38e Mon Sep 17 00:00:00 2001 From: rdlabo Date: Fri, 23 Oct 2026 18:44:33 +0900 Subject: [PATCH 2/2] test: verify vertical tab tint resets to system appearance --- .../Tests/NativeUIPluginTests/NativeUISnapshotTests.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift b/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift index b7c684880..ed4ea314b 100644 --- a/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift +++ b/packages/capacitor-native-ui/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift @@ -67,6 +67,7 @@ final class NativeUISnapshotTests: XCTestCase { } } var systemTint: UIColor? + var controllerSystemTint: UIColor? for (index, tint) in ([nil, "rgb(255, 0, 0)", "rgb(255, 0, 0)", "rgb(128, 0, 128)", nil] as [String?]).enumerated() { var value = control(["kind": "tab-bar", "placement": "vertical-bars", "items": [ item(["id": "first", "label": "First", "selected": index % 2 == 0]), @@ -78,8 +79,11 @@ final class NativeUISnapshotTests: XCTestCase { rail.apply(try decode([value]).controls, rendering: NativeUIControlRendering(), edge: "right", animated: false) try await Task.sleep(nanoseconds: 500_000_000) let tabs = try XCTUnwrap(tabController(owner)) - if index == 0 { systemTint = tabs.tabBar.tintColor } + if index == 0 { systemTint = tabs.tabBar.tintColor; controllerSystemTint = tabs.view.tintColor } try assertTint(tabs.tabBar.tintColor, tint.map { NativeUIRendering.color($0) } ?? systemTint, traits: tabs.traitCollection) + if tint == nil { + try assertTint(tabs.view.tintColor, controllerSystemTint, traits: tabs.traitCollection) + } if let tint { // On iOS 27.1, the visible SwiftUI rail follows its controller's // accent; setting only the legacy UITabBar property leaves it blue.