diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx index 22dce85..02e79fb 100644 --- a/docs/docs/docs/guides/usage-with-react-navigation.mdx +++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx @@ -35,7 +35,7 @@ function SettingsScreen() { export default function App() { return ( - + -Whether to ignore the top safe area. +Whether to ignore the top safe area. Defaults to `true`. #### `screenOptions` diff --git a/example/src/Examples/NativeBottomTabsEmbeddedStacks.tsx b/example/src/Examples/NativeBottomTabsEmbeddedStacks.tsx index 24b1072..4230ea4 100644 --- a/example/src/Examples/NativeBottomTabsEmbeddedStacks.tsx +++ b/example/src/Examples/NativeBottomTabsEmbeddedStacks.tsx @@ -67,7 +67,7 @@ function ChatStackScreen() { function NativeBottomTabsEmbeddedStacks() { return ( - + UIView { return view } - + func updateUIView(_ uiView: UIView, context: Context) {} } @@ -52,7 +52,7 @@ struct TabViewImpl: View { @ObservedObject var props: TabViewProps var onSelect: (_ key: String) -> Void var onLongPress: (_ key: String) -> Void - + var body: some View { TabView(selection: $props.selectedPage) { ForEach(props.children.indices, id: \.self) { index in @@ -63,7 +63,7 @@ struct TabViewImpl: View { guard let key = props.items.filter({ !$0.hidden || $0.key == props.selectedPage })[safe: index]?.key else { return } - + if isLongPress { onLongPress(key) emitHapticFeedback(longPress: true) @@ -84,20 +84,20 @@ struct TabViewImpl: View { } } } - + @ViewBuilder private func renderTabItem(at index: Int) -> some View { let tabData = props.items[safe: index] let isHidden = tabData?.hidden ?? false let isFocused = props.selectedPage == tabData?.key - + if !isHidden || isFocused { let child = props.children[safe: index] ?? UIView() let icon = props.icons[index] - + RepresentableView(view: child) .ignoresTopSafeArea( - props.ignoresTopSafeArea ?? false, + props.ignoresTopSafeArea, frame: child.frame ) .tabItem { @@ -120,13 +120,13 @@ struct TabViewImpl: View { #endif } } - + func emitHapticFeedback(longPress: Bool = false) { #if os(iOS) if !props.hapticFeedbackEnabled { return } - + if longPress { UINotificationFeedbackGenerator().notificationOccurred(.success) } else { @@ -141,16 +141,16 @@ private func configureAppearance(for appearanceType: String, appearance: UITabBa if (appearanceType == "transparent") { return appearance } - + switch appearanceType { case "opaque": appearance.configureWithOpaqueBackground() default: appearance.configureWithDefaultBackground() } - + UITabBar.appearance().scrollEdgeAppearance = appearance - + return appearance } @@ -166,7 +166,7 @@ private func configureAppearance(inactiveTint inactiveTintColor: UIColor?, appea setTabBarItemColors(appearance.inlineLayoutAppearance, inactiveColor: inactiveTintColor) setTabBarItemColors(appearance.compactInlineLayoutAppearance, inactiveColor: inactiveTintColor) } - + return appearance } @@ -176,15 +176,15 @@ private func updateTabBarAppearance(props: TabViewProps) { 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 } @@ -192,7 +192,7 @@ private func updateTabBarAppearance(props: TabViewProps) { UITabBar.appearance().barTintColor = props.barTintColor UITabBar.appearance().isTranslucent = props.translucent } - + UITabBar.appearance().standardAppearance = appearance } @@ -201,7 +201,7 @@ struct TabItem: View { var icon: UIImage? var sfSymbol: String? var labeled: Bool? - + var body: some View { if let icon { Image(uiImage: icon) @@ -230,7 +230,7 @@ extension View { self } } - + @ViewBuilder func tabBadge(_ data: String?) -> some View { if #available(iOS 15.0, macOS 15.0, visionOS 2.0, tvOS 15.0, *) { @@ -247,7 +247,7 @@ extension View { self } } - + @ViewBuilder func ignoresTopSafeArea( _ flag: Bool, @@ -264,7 +264,7 @@ extension View { .frame(idealWidth: frame.width, idealHeight: frame.height) } } - + @ViewBuilder func configureAppearance(props: TabViewProps) -> some View { self @@ -287,7 +287,7 @@ extension View { updateTabBarAppearance(props: props) } } - + @ViewBuilder func tintColor(_ color: UIColor?) -> some View { if let color { @@ -301,7 +301,7 @@ extension View { self } } - + // Allows TabView to use unfilled SFSymbols. // By default they are always filled. @ViewBuilder diff --git a/ios/TabViewProvider.swift b/ios/TabViewProvider.swift index 1e65f44..487e62f 100644 --- a/ios/TabViewProvider.swift +++ b/ios/TabViewProvider.swift @@ -41,98 +41,98 @@ import React private var coalescingKey: UInt16 = 0 private var imageLoader: RCTImageLoaderProtocol? private var iconSize = CGSize(width: 27, height: 27) - + @objc var onPageSelected: RCTDirectEventBlock? - + @objc var onTabLongPress: RCTDirectEventBlock? - + @objc public var icons: NSArray? { didSet { loadIcons(icons) } } - + @objc public var children: [UIView] = [] { didSet { props.children = children } } - + @objc public var sidebarAdaptable: Bool = false { didSet { props.sidebarAdaptable = sidebarAdaptable } } - - + + @objc public var disablePageAnimations: Bool = false { didSet { props.disablePageAnimations = disablePageAnimations } } - + @objc public var labeled: Bool = true { didSet { props.labeled = labeled } } - - @objc public var ignoresTopSafeArea: Bool = false { + + @objc public var ignoresTopSafeArea: Bool = true { didSet { props.ignoresTopSafeArea = ignoresTopSafeArea } } - + @objc public var selectedPage: NSString? { didSet { props.selectedPage = selectedPage as? String } } - + @objc public var hapticFeedbackEnabled: Bool = true { didSet { props.hapticFeedbackEnabled = hapticFeedbackEnabled } } - + @objc public var scrollEdgeAppearance: NSString? { didSet { props.scrollEdgeAppearance = scrollEdgeAppearance as? String } } - + @objc public var translucent: Bool = true { didSet { props.translucent = translucent } } - + @objc var items: NSArray? { didSet { props.items = parseTabData(from: items) } } - + @objc public var barTintColor: UIColor? { didSet { props.barTintColor = barTintColor } } - + @objc public var activeTintColor: UIColor? { didSet { props.activeTintColor = activeTintColor } } - + @objc public var inactiveTintColor: UIColor? { didSet { props.inactiveTintColor = inactiveTintColor } } - + // New arch specific properties - + @objc public var itemsData: [TabInfo] = [] { didSet { props.items = itemsData @@ -144,21 +144,21 @@ import React self.delegate = delegate self.imageLoader = imageLoader } - + public override func didUpdateReactSubviews() { props.children = reactSubviews() } - + public override func layoutSubviews() { super.layoutSubviews() setupView() } - + private func setupView() { if self.hostingController != nil { return } - + self.hostingController = UIHostingController(rootView: TabViewImpl(props: props) { key in self.delegate?.onPageSelected(key: key, reactTag: self.reactTag) } onLongPress: { key in @@ -172,7 +172,7 @@ import React hostingController.didMove(toParent: parentViewController) } } - + private func loadIcons(_ icons: NSArray?) { // TODO: Diff the arrays and update only changed items. // Now if the user passes `unfocusedIcon` we update every item. @@ -200,11 +200,11 @@ import React } } } - + private func parseTabData(from array: NSArray?) -> [TabInfo] { guard let array else { return [] } var items: [TabInfo] = [] - + for value in array { if let itemDict = value as? [String: Any] { items.append( @@ -219,7 +219,7 @@ import React ) } } - + return items } } diff --git a/src/TabView.tsx b/src/TabView.tsx index e9c5eb9..37792ff 100644 --- a/src/TabView.tsx +++ b/src/TabView.tsx @@ -39,7 +39,7 @@ interface Props { */ ignoresTopSafeArea?: boolean; /** - * Whether to disable page animations between tabs. (iOS only) + * Whether to disable page animations between tabs. (iOS only) Defaults to `true`. */ disablePageAnimations?: boolean; /** diff --git a/src/TabViewNativeComponent.ts b/src/TabViewNativeComponent.ts index 3da589b..cbd797a 100644 --- a/src/TabViewNativeComponent.ts +++ b/src/TabViewNativeComponent.ts @@ -1,6 +1,9 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; import type { ColorValue, ProcessedColorValue, ViewProps } from 'react-native'; -import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; +import type { + DirectEventHandler, + WithDefault, +} from 'react-native/Libraries/Types/CodegenTypes'; //@ts-ignore import type { ImageSource } from 'react-native/Libraries/Image/ImageSource'; @@ -31,7 +34,7 @@ export interface TabViewProps extends ViewProps { rippleColor?: ColorValue; activeTintColor?: ColorValue; inactiveTintColor?: ColorValue; - ignoresTopSafeArea?: boolean; + ignoresTopSafeArea?: WithDefault; disablePageAnimations?: boolean; activeIndicatorColor?: ColorValue; hapticFeedbackEnabled?: boolean;