Skip to content

Commit f0ac627

Browse files
authored
fix: appearance handling on iOS (#133)
1 parent 7b761e6 commit f0ac627

File tree

3 files changed

+69
-75
lines changed

3 files changed

+69
-75
lines changed

ios/Extensions.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,16 @@ extension UIImage {
3737
return resizedImage
3838
}
3939
}
40+
41+
extension View {
42+
@ViewBuilder
43+
func introspectTabView(closure: @escaping (UITabBarController) -> Void) -> some View {
44+
self
45+
.introspect(
46+
.tabView,
47+
on: .iOS(.v14, .v15, .v16, .v17, .v18),
48+
.tvOS(.v14,.v15, .v16, .v17, .v18),
49+
customize: closure
50+
)
51+
}
52+
}

ios/TabItemEventModifier.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,9 @@ struct TabItemEventModifier: ViewModifier {
1818

1919
func body(content: Content) -> some View {
2020
content
21-
.introspect(.tabView, on: .iOS(.v14, .v15, .v16, .v17, .v18)) { tabController in
21+
.introspectTabView(closure: { tabController in
2222
handle(tabController: tabController)
23-
}
24-
.introspect(.tabView, on: .tvOS(.v14, .v15, .v16, .v17, .v18)) { tabController in
25-
handle(tabController: tabController)
26-
}
23+
})
2724
}
2825

2926
func handle(tabController: UITabBarController) {

ios/TabViewImpl.swift

Lines changed: 54 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Foundation
22
import SwiftUI
33
import React
4+
@_spi(Advanced) import SwiftUIIntrospect
5+
46

57
/**
68
Props that component accepts. SwiftUI view gets re-rendered when ObservableObject changes.
@@ -52,6 +54,8 @@ struct TabViewImpl: View {
5254
@ObservedObject var props: TabViewProps
5355
var onSelect: (_ key: String) -> Void
5456
var onLongPress: (_ key: String) -> Void
57+
@Weak var tabBar: UITabBar?
58+
5559

5660
var body: some View {
5761
TabView(selection: $props.selectedPage) {
@@ -72,9 +76,15 @@ struct TabViewImpl: View {
7276
emitHapticFeedback()
7377
}
7478
})
79+
.introspectTabView(closure: { tabController in
80+
tabBar = tabController.tabBar
81+
})
82+
.onChange(of: tabBar) { newValue in
83+
updateTabBarAppearance(props: props, tabBar: tabBar)
84+
}
85+
.configureAppearance(props: props, tabBar: tabBar)
7586
.tintColor(props.selectedActiveTintColor)
7687
.getSidebarAdaptable(enabled: props.sidebarAdaptable ?? false)
77-
.configureAppearance(props: props)
7888
.onChange(of: props.selectedPage ?? "") { newValue in
7989
if (props.disablePageAnimations) {
8090
UIView.setAnimationsEnabled(false)
@@ -136,66 +146,6 @@ struct TabViewImpl: View {
136146
}
137147
}
138148

139-
@available(iOS 15.0, *)
140-
private func configureAppearance(for appearanceType: String, appearance: UITabBarAppearance) -> UITabBarAppearance {
141-
if (appearanceType == "transparent") {
142-
return appearance
143-
}
144-
145-
switch appearanceType {
146-
case "opaque":
147-
appearance.configureWithOpaqueBackground()
148-
default:
149-
appearance.configureWithDefaultBackground()
150-
}
151-
152-
UITabBar.appearance().scrollEdgeAppearance = appearance
153-
154-
return appearance
155-
}
156-
157-
private func setTabBarItemColors(_ itemAppearance: UITabBarItemAppearance, inactiveColor: UIColor) {
158-
itemAppearance.normal.iconColor = inactiveColor
159-
itemAppearance.normal.titleTextAttributes = [.foregroundColor: inactiveColor]
160-
}
161-
162-
private func configureAppearance(inactiveTint inactiveTintColor: UIColor?, appearance: UITabBarAppearance) -> UITabBarAppearance {
163-
// @see https://stackoverflow.com/a/71934882
164-
if let inactiveTintColor {
165-
setTabBarItemColors(appearance.stackedLayoutAppearance, inactiveColor: inactiveTintColor)
166-
setTabBarItemColors(appearance.inlineLayoutAppearance, inactiveColor: inactiveTintColor)
167-
setTabBarItemColors(appearance.compactInlineLayoutAppearance, inactiveColor: inactiveTintColor)
168-
}
169-
170-
return appearance
171-
}
172-
173-
private func updateTabBarAppearance(props: TabViewProps) {
174-
var appearance = UITabBarAppearance()
175-
appearance = configureAppearance(
176-
inactiveTint: props.inactiveTintColor,
177-
appearance: appearance
178-
)
179-
180-
181-
if #available(iOS 15.0, *) {
182-
appearance = configureAppearance(for: props.scrollEdgeAppearance ?? "", appearance: appearance)
183-
184-
if props.translucent == false {
185-
appearance.configureWithOpaqueBackground()
186-
}
187-
188-
if props.barTintColor != nil {
189-
appearance.backgroundColor = props.barTintColor
190-
}
191-
} else {
192-
UITabBar.appearance().barTintColor = props.barTintColor
193-
UITabBar.appearance().isTranslucent = props.translucent
194-
}
195-
196-
UITabBar.appearance().standardAppearance = appearance
197-
}
198-
199149
struct TabItem: View {
200150
var title: String?
201151
var icon: UIImage?
@@ -215,6 +165,43 @@ struct TabItem: View {
215165
}
216166
}
217167

168+
private func updateTabBarAppearance(props: TabViewProps, tabBar: UITabBar?) {
169+
guard let tabBar else { return }
170+
let appearanceType = props.scrollEdgeAppearance
171+
172+
if (appearanceType == "transparent") {
173+
tabBar.barTintColor = props.barTintColor
174+
tabBar.isTranslucent = props.translucent
175+
tabBar.unselectedItemTintColor = props.inactiveTintColor
176+
return
177+
}
178+
179+
let appearance = UITabBarAppearance()
180+
181+
switch appearanceType {
182+
case "opaque":
183+
appearance.configureWithOpaqueBackground()
184+
default:
185+
appearance.configureWithDefaultBackground()
186+
}
187+
appearance.backgroundColor = props.barTintColor
188+
189+
if let inactiveTintColor = props.inactiveTintColor {
190+
let itemAppearance = UITabBarItemAppearance()
191+
itemAppearance.normal.iconColor = inactiveTintColor
192+
itemAppearance.normal.titleTextAttributes = [.foregroundColor: inactiveTintColor]
193+
194+
appearance.stackedLayoutAppearance = itemAppearance
195+
appearance.inlineLayoutAppearance = itemAppearance
196+
appearance.compactInlineLayoutAppearance = itemAppearance
197+
}
198+
199+
tabBar.standardAppearance = appearance
200+
if #available(iOS 15.0, *) {
201+
tabBar.scrollEdgeAppearance = appearance.copy()
202+
}
203+
}
204+
218205
extension View {
219206
@ViewBuilder
220207
func getSidebarAdaptable(enabled: Bool) -> some View {
@@ -268,25 +255,22 @@ extension View {
268255
}
269256

270257
@ViewBuilder
271-
func configureAppearance(props: TabViewProps) -> some View {
258+
func configureAppearance(props: TabViewProps, tabBar: UITabBar?) -> some View {
272259
self
273-
.onAppear() {
274-
updateTabBarAppearance(props: props)
275-
}
276260
.onChange(of: props.barTintColor) { newValue in
277-
updateTabBarAppearance(props: props)
261+
updateTabBarAppearance(props: props, tabBar: tabBar)
278262
}
279263
.onChange(of: props.scrollEdgeAppearance) { newValue in
280-
updateTabBarAppearance(props: props)
264+
updateTabBarAppearance(props: props, tabBar: tabBar)
281265
}
282266
.onChange(of: props.translucent) { newValue in
283-
updateTabBarAppearance(props: props)
267+
updateTabBarAppearance(props: props, tabBar: tabBar)
284268
}
285269
.onChange(of: props.inactiveTintColor) { newValue in
286-
updateTabBarAppearance(props: props)
270+
updateTabBarAppearance(props: props, tabBar: tabBar)
287271
}
288272
.onChange(of: props.selectedActiveTintColor) { newValue in
289-
updateTabBarAppearance(props: props)
273+
updateTabBarAppearance(props: props, tabBar: tabBar)
290274
}
291275
}
292276

0 commit comments

Comments
 (0)