Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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