Skip to content

Commit

Permalink
fix: appearance handling on iOS (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski authored Nov 9, 2024
1 parent 7b761e6 commit f0ac627
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 75 deletions.
13 changes: 13 additions & 0 deletions ios/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,16 @@ extension UIImage {
return resizedImage
}
}

extension View {
@ViewBuilder
func introspectTabView(closure: @escaping (UITabBarController) -> Void) -> some View {
self
.introspect(
.tabView,
on: .iOS(.v14, .v15, .v16, .v17, .v18),
.tvOS(.v14,.v15, .v16, .v17, .v18),
customize: closure
)
}
}
7 changes: 2 additions & 5 deletions ios/TabItemEventModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@ struct TabItemEventModifier: ViewModifier {

func body(content: Content) -> some View {
content
.introspect(.tabView, on: .iOS(.v14, .v15, .v16, .v17, .v18)) { tabController in
.introspectTabView(closure: { tabController in
handle(tabController: tabController)
}
.introspect(.tabView, on: .tvOS(.v14, .v15, .v16, .v17, .v18)) { tabController in
handle(tabController: tabController)
}
})
}

func handle(tabController: UITabBarController) {
Expand Down
124 changes: 54 additions & 70 deletions ios/TabViewImpl.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Foundation
import SwiftUI
import React
@_spi(Advanced) import SwiftUIIntrospect


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


var body: some View {
TabView(selection: $props.selectedPage) {
Expand All @@ -72,9 +76,15 @@ struct TabViewImpl: View {
emitHapticFeedback()
}
})
.introspectTabView(closure: { tabController in
tabBar = tabController.tabBar
})
.onChange(of: tabBar) { newValue in
updateTabBarAppearance(props: props, tabBar: tabBar)
}
.configureAppearance(props: props, tabBar: tabBar)
.tintColor(props.selectedActiveTintColor)
.getSidebarAdaptable(enabled: props.sidebarAdaptable ?? false)
.configureAppearance(props: props)
.onChange(of: props.selectedPage ?? "") { newValue in
if (props.disablePageAnimations) {
UIView.setAnimationsEnabled(false)
Expand Down Expand Up @@ -136,66 +146,6 @@ struct TabViewImpl: View {
}
}

@available(iOS 15.0, *)
private func configureAppearance(for appearanceType: String, appearance: UITabBarAppearance) -> UITabBarAppearance {
if (appearanceType == "transparent") {
return appearance
}

switch appearanceType {
case "opaque":
appearance.configureWithOpaqueBackground()
default:
appearance.configureWithDefaultBackground()
}

UITabBar.appearance().scrollEdgeAppearance = appearance

return appearance
}

private func setTabBarItemColors(_ itemAppearance: UITabBarItemAppearance, inactiveColor: UIColor) {
itemAppearance.normal.iconColor = inactiveColor
itemAppearance.normal.titleTextAttributes = [.foregroundColor: inactiveColor]
}

private func configureAppearance(inactiveTint inactiveTintColor: UIColor?, appearance: UITabBarAppearance) -> UITabBarAppearance {
// @see https://stackoverflow.com/a/71934882
if let inactiveTintColor {
setTabBarItemColors(appearance.stackedLayoutAppearance, inactiveColor: inactiveTintColor)
setTabBarItemColors(appearance.inlineLayoutAppearance, inactiveColor: inactiveTintColor)
setTabBarItemColors(appearance.compactInlineLayoutAppearance, inactiveColor: inactiveTintColor)
}

return appearance
}

private func updateTabBarAppearance(props: TabViewProps) {
var appearance = UITabBarAppearance()
appearance = configureAppearance(
inactiveTint: props.inactiveTintColor,
appearance: appearance
)


if #available(iOS 15.0, *) {
appearance = configureAppearance(for: props.scrollEdgeAppearance ?? "", appearance: appearance)

if props.translucent == false {
appearance.configureWithOpaqueBackground()
}

if props.barTintColor != nil {
appearance.backgroundColor = props.barTintColor
}
} else {
UITabBar.appearance().barTintColor = props.barTintColor
UITabBar.appearance().isTranslucent = props.translucent
}

UITabBar.appearance().standardAppearance = appearance
}

struct TabItem: View {
var title: String?
var icon: UIImage?
Expand All @@ -215,6 +165,43 @@ struct TabItem: View {
}
}

private func updateTabBarAppearance(props: TabViewProps, tabBar: UITabBar?) {
guard let tabBar else { return }
let appearanceType = props.scrollEdgeAppearance

if (appearanceType == "transparent") {
tabBar.barTintColor = props.barTintColor
tabBar.isTranslucent = props.translucent
tabBar.unselectedItemTintColor = props.inactiveTintColor
return
}

let appearance = UITabBarAppearance()

switch appearanceType {
case "opaque":
appearance.configureWithOpaqueBackground()
default:
appearance.configureWithDefaultBackground()
}
appearance.backgroundColor = props.barTintColor

if let inactiveTintColor = props.inactiveTintColor {
let itemAppearance = UITabBarItemAppearance()
itemAppearance.normal.iconColor = inactiveTintColor
itemAppearance.normal.titleTextAttributes = [.foregroundColor: inactiveTintColor]

appearance.stackedLayoutAppearance = itemAppearance
appearance.inlineLayoutAppearance = itemAppearance
appearance.compactInlineLayoutAppearance = itemAppearance
}

tabBar.standardAppearance = appearance
if #available(iOS 15.0, *) {
tabBar.scrollEdgeAppearance = appearance.copy()
}
}

extension View {
@ViewBuilder
func getSidebarAdaptable(enabled: Bool) -> some View {
Expand Down Expand Up @@ -268,25 +255,22 @@ extension View {
}

@ViewBuilder
func configureAppearance(props: TabViewProps) -> some View {
func configureAppearance(props: TabViewProps, tabBar: UITabBar?) -> some View {
self
.onAppear() {
updateTabBarAppearance(props: props)
}
.onChange(of: props.barTintColor) { newValue in
updateTabBarAppearance(props: props)
updateTabBarAppearance(props: props, tabBar: tabBar)
}
.onChange(of: props.scrollEdgeAppearance) { newValue in
updateTabBarAppearance(props: props)
updateTabBarAppearance(props: props, tabBar: tabBar)
}
.onChange(of: props.translucent) { newValue in
updateTabBarAppearance(props: props)
updateTabBarAppearance(props: props, tabBar: tabBar)
}
.onChange(of: props.inactiveTintColor) { newValue in
updateTabBarAppearance(props: props)
updateTabBarAppearance(props: props, tabBar: tabBar)
}
.onChange(of: props.selectedActiveTintColor) { newValue in
updateTabBarAppearance(props: props)
updateTabBarAppearance(props: props, tabBar: tabBar)
}
}

Expand Down

0 comments on commit f0ac627

Please sign in to comment.