Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4e6764d
feat: create LegacyTabView implementation
47PADO47 Jun 14, 2025
5de6709
feat: create NewTabView implementation
47PADO47 Jun 14, 2025
2a15cde
refactor: use different TabView depending on os version
47PADO47 Jun 14, 2025
592141e
feat: Create AnyTabView protocol
47PADO47 Jun 20, 2025
ed6f9e0
feat: Implement AnyTabView protocol in TabViews
47PADO47 Jun 20, 2025
c1c1205
refactor: Use AnyTabView as tabContent
47PADO47 Jun 20, 2025
5ec3e2f
feat: Create TabAppearModifier.swift
47PADO47 Jun 20, 2025
dfec076
refactor: Use tabAppear modifier
47PADO47 Jun 20, 2025
2f444ac
fix: move LegacyTabView measureView
47PADO47 Jun 20, 2025
69837bd
chore: remove tabViewCustomization
47PADO47 Jun 20, 2025
f0dfc85
fix: remove props from AnyTabView as AnyTabView is internal
47PADO47 Jun 20, 2025
c610cda
fix: revert tabContent to some View
47PADO47 Jun 20, 2025
8fd1f7b
fix: revert move LegacyTabView measureView
47PADO47 Jun 20, 2025
dc4ac1b
feat: create LegacyTabView implementation
47PADO47 Jun 14, 2025
d5f0dac
feat: create NewTabView implementation
47PADO47 Jun 14, 2025
23cb5f3
refactor: use different TabView depending on os version
47PADO47 Jun 14, 2025
da02dba
feat: Create AnyTabView protocol
47PADO47 Jun 20, 2025
f330ee0
feat: Implement AnyTabView protocol in TabViews
47PADO47 Jun 20, 2025
b4a22a8
refactor: Use AnyTabView as tabContent
47PADO47 Jun 20, 2025
68c5aa1
feat: Create TabAppearModifier.swift
47PADO47 Jun 20, 2025
d15a863
refactor: Use tabAppear modifier
47PADO47 Jun 20, 2025
8444bfc
fix: move LegacyTabView measureView
47PADO47 Jun 20, 2025
65ada5f
chore: remove tabViewCustomization
47PADO47 Jun 20, 2025
5e8b5ca
fix: remove props from AnyTabView as AnyTabView is internal
47PADO47 Jun 20, 2025
9b77571
fix: revert tabContent to some View
47PADO47 Jun 20, 2025
d2d4b34
fix: revert move LegacyTabView measureView
47PADO47 Jun 20, 2025
7a0f01b
fix: working badges on new tabview
47PADO47 Jul 7, 2025
830d04b
chore: run swiftlint
47PADO47 Jul 7, 2025
0a127e5
chore: remove nil tab role
47PADO47 Jul 7, 2025
1828b18
fix: move .tag outside tabItem
47PADO47 Jul 7, 2025
1bad022
Merge branch 'main' of https://github.com/padosoft/react-native-botto…
47PADO47 Jul 7, 2025
59c6481
Merge branch 'main' of https://github.com/padosoft/react-native-botto…
47PADO47 Jul 7, 2025
f4d97b7
Merge branch 'main' into main
47PADO47 Jul 11, 2025
e8c9ed2
 fix: move .tab modifiers in LegacyTabView
47PADO47 Jul 12, 2025
bbcb542
refactor: update NewTabView .badge
47PADO47 Jul 12, 2025
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
33 changes: 33 additions & 0 deletions packages/react-native-bottom-tabs/ios/TabAppearModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import SwiftUI

struct TabAppearContext {
let index: Int
let tabData: TabInfo
let props: TabViewProps
let updateTabBarAppearance: () -> Void
let onSelect: (_ key: String) -> Void
}

struct TabAppearModifier: ViewModifier {
let context: TabAppearContext

func body(content: Content) -> some View {
content.onAppear {
#if !os(macOS)
context.updateTabBarAppearance()
#endif

#if os(iOS)
if context.index >= 4, context.props.selectedPage != context.tabData.key {
context.onSelect(context.tabData.key)
}
#endif
}
}
}

extension View {
func tabAppear(using context: TabAppearContext) -> some View {
self.modifier(TabAppearModifier(context: context))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import SwiftUI

public protocol AnyTabView: View {
var onLayout: (_ size: CGSize) -> Void { get }
var onSelect: (_ key: String) -> Void { get }
var updateTabBarAppearance: () -> Void { get }
}
56 changes: 56 additions & 0 deletions packages/react-native-bottom-tabs/ios/TabView/LegacyTabView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import SwiftUI

struct LegacyTabView: AnyTabView {
@ObservedObject var props: TabViewProps

var onLayout: (CGSize) -> Void
var onSelect: (String) -> Void
var updateTabBarAppearance: () -> Void

@ViewBuilder
var body: some View {
TabView(selection: $props.selectedPage) {
ForEach(props.children.indices, id: \.self) { index in
renderTabItem(at: index)
}
.measureView { size in
onLayout(size)
}
}
.hideTabBar(props.tabBarHidden)
}

@ViewBuilder
private func renderTabItem(at index: Int) -> some View {
if let tabData = props.items[safe: index] {
let isFocused = props.selectedPage == tabData.key

if !tabData.hidden || isFocused {
let icon = props.icons[index]
let child = props.children[safe: index] ?? PlatformView()
let context = TabAppearContext(
index: index,
tabData: tabData,
props: props,
updateTabBarAppearance: updateTabBarAppearance,
onSelect: onSelect
)

RepresentableView(view: child)
.ignoresSafeArea(.container, edges: .all)
.tabItem {
TabItem(
title: tabData.title,
icon: icon,
sfSymbol: tabData.sfSymbol,
labeled: props.labeled
)
.accessibilityIdentifier(tabData.testID ?? "")
}
.tag(tabData.key)
.tabBadge(tabData.badge)
.tabAppear(using: context)
}
}
}
}
54 changes: 54 additions & 0 deletions packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import SwiftUI

@available(iOS 18, macOS 15, visionOS 2, tvOS 18, *)
struct NewTabView: AnyTabView {
@ObservedObject var props: TabViewProps

var onLayout: (CGSize) -> Void
var onSelect: (String) -> Void
var updateTabBarAppearance: () -> Void

@ViewBuilder
var body: some View {
TabView(selection: $props.selectedPage) {
ForEach(props.children.indices, id: \.self) { index in
if let tabData = props.items[safe: index] {
let isFocused = props.selectedPage == tabData.key

if !tabData.hidden || isFocused {
let icon = props.icons[index]

let platformChild = props.children[safe: index] ?? PlatformView()
let child = RepresentableView(view: platformChild)
let context = TabAppearContext(
index: index,
tabData: tabData,
props: props,
updateTabBarAppearance: updateTabBarAppearance,
onSelect: onSelect
)

Tab(value: tabData.key) {
child
.ignoresSafeArea(.container, edges: .all)
.tabAppear(using: context)
} label: {
TabItem(
title: tabData.title,
icon: icon,
sfSymbol: tabData.sfSymbol,
labeled: props.labeled
)
}
.badge(tabData.badge.flatMap { !$0.isEmpty ? Text($0) : nil })
.accessibilityIdentifier(tabData.testID ?? "")
}
}
}
}
.measureView { size in
onLayout(size)
}
.hideTabBar(props.tabBarHidden)
}
}
Loading
Loading