npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-ios26-tabs-custom-icon

v1.0.0

Published

Custom profile icons and avatars for iOS 26 native tabs in React Native

Readme

react-native-ios26-tabs-custom-icon

npm platform - ios

English version (README.md)

Кастомные иконки профиля и аватары для нативных табов iOS 26 в React Native.

Возможности

  • Любое изображение как иконка таба (URL, файл, base64, asset, RN require())
  • Круглые аватары профиля с настраиваемой рамкой (цвет/толщина)
  • Отдельный selectedUri для активного/неактивного состояния таба
  • Двухуровневый кеш: память (NSCache, 50 / 20MB) + диск (Caches)
  • Prefetch API — предзагрузка изображений до отображения
  • JS-дедупликация — повторные вызовы с теми же параметрами пропускаются
  • Отмена URLSession загрузок при повторных запросах
  • Защита от гонок — применяется только последний запрос для каждого таба
  • Асинхронная загрузка через URLSession (не блокирует UI)
  • Aspect-fill обрезка для аватаров
  • Retina-рендеринг (UIScreen.main.scale)
  • На iOS 26 использует новый UITab API, на старых версиях — UITabBarItem
  • React-хуки с автоочисткой при размонтировании
  • isAvailable() для проверки доступности модуля
  • Компонент AnimatedTabBar — схлопывается при скролле вниз, разворачивается вверх
  • Опциональные текстовые лейблы под иконками с автоматической анимацией скрытия
  • useScrollAnimation() с методом reset() для ручного разворота бара
  • useIsAvailable() — стабильный хук для условного рендеринга
  • useCacheInfo() — живая статистика кэша с автообновлением

Совместимость

| React Native | React | iOS | |-------------|-------|-----| | 0.73+ | 18.2+ | 15+ | | 0.76+ | 18.3+ | 15+ | | 0.79+ | 19.0+ | 15+ |

Фичи плавающего таб-бара iOS 26 требуют iOS 26+. На более ранних версиях iOS используется классический UITabBarItem API.

Установка

npm install react-native-ios26-tabs-custom-icon
cd ios && pod install

Автолинковка

React Native 0.73+ поддерживает автолинковку — библиотека подключится автоматически после pod install.

Быстрый старт

import {
  setTabIcon,
  setProfileAvatar,
  useProfileAvatar,
  preloadImages,
  isAvailable,
  clearCache,
} from 'react-native-ios26-tabs-custom-icon';

// Предзагрузка иконок при старте приложения
await preloadImages([
  'https://example.com/home.png',
  'https://example.com/search.png',
  'https://example.com/bell.png',
]);

// Проверка доступности нативного модуля
if (isAvailable()) {
  // Кастомная иконка из URL (мгновенно если предзагружена)
  await setTabIcon(0, { uri: 'https://example.com/home.png', size: 28, alwaysOriginal: true });

  // Через RN require()
  await setTabIcon(1, { source: require('./assets/search.png'), size: 26 });

  // С отдельным состоянием для активного таба
  await setTabIcon(2, {
    uri: 'https://example.com/bell.png',
    selectedUri: 'https://example.com/bell-filled.png',
    size: 26,
  });
}

// Круглый аватар для таба профиля
await setProfileAvatar(3, {
  uri: user.avatarUrl,
  size: 30,
  borderWidth: 2,
  borderColor: '#FF6B35',
});

// Хук — автосброс при размонтировании
function ProfileTab({ user }) {
  useProfileAvatar(3, { uri: user.avatarUrl, size: 30, borderWidth: 2, borderColor: '#FF6B35' });
  return <View />;
}

// Очистить память и диск
clearCache();

API

isAvailable(): boolean

Возвращает true если нативный модуль загружен (только iOS, требует нативную сборку).

setTabIcon(tabIndex, options): Promise<void>

| Параметр | Тип | По умолчанию | Описание | |----------|-----|--------------|----------| | uri | string | — | Путь к файлу или URL | | base64 | string | — | Изображение в base64 | | assetName | string | — | Имя ассета из бандла | | source | ImageRequireSource | — | RN require() изображение | | size | number | 28 | Размер в points | | alwaysOriginal | boolean | false | Сохранить оригинальные цвета | | selectedUri | string | — | Изображение для активного таба |

setProfileAvatar(tabIndex, options): Promise<void>

| Параметр | Тип | По умолчанию | Описание | |----------|-----|--------------|----------| | uri | string | — | URL или путь к изображению | | base64 | string | — | Изображение в base64 | | size | number | 30 | Диаметр аватара (pt) | | borderWidth | number | 1.5 | Толщина рамки (pt) | | borderColor | string | "#007AFF" | Цвет рамки (hex) |

resetTabIcon(tabIndex): Promise<void>

Сбросить иконку таба к дефолтной.

preloadImages(uris): Promise<number>

Предзагрузить изображения в память и на диск. Возвращает количество успешно загруженных. Используйте при старте приложения для мгновенного отображения иконок.

const loaded = await preloadImages([
  'https://example.com/icon1.png',
  'https://example.com/icon2.png',
]);
console.log(`${loaded} изображений предзагружено`);

clearCache(): void

Очистить кеш изображений (память + диск). JS-кэши дедупликации очищаются всегда, даже если нативный модуль недоступен.

getCacheInfo(): CacheInfo | null

Возвращает { countLimit, totalCostLimit, diskCacheSizeBytes } или null если модуль недоступен.

Хуки

useTabIcon(tabIndex, options | null)
useProfileAvatar(tabIndex, options | null)

Передайте null для сброса. Автоматически сбрасывает иконку при размонтировании компонента.

useIsAvailable(): boolean

Возвращает true если нативный модуль доступен. Стабилен между ре-рендерами — безопасно использовать в условном рендеринге.

const available = useIsAvailable();
if (!available) return <FallbackBanner />;

useCacheInfo(intervalMs?): CacheInfo | null

Опрашивает getCacheInfo() с заданным интервалом (по умолчанию 5000 мс). Возвращает null если модуль недоступен.

const cache = useCacheInfo(8000);
console.log(cache?.diskCacheSizeBytes); // байт на диске

Анимированный таб-бар

JS-компонент нижней навигационной панели, которая схлопывается при прокрутке вниз и разворачивается обратно при прокрутке вверх. Анимация через React Native Animated API.

useScrollAnimation()

Возвращает анимированное значение и обработчик скролла для подключения к ScrollView и таб-бару.

const { expandedAnim, onScroll, scrollEventThrottle, reset } = useScrollAnimation();

// развернуть бар при смене таба
const handleTabPress = (tab: string) => {
  setActiveTab(tab);
  reset();
};

| Возвращает | Тип | Описание | |------------|-----|----------| | expandedAnim | Animated.Value | 1 = развёрнут, 0 = схлопнут. Передаётся в AnimatedTabBar. | | onScroll | function | Передаётся в onScroll пропс ScrollView / FlatList. | | scrollEventThrottle | number | Всегда 16. Используйте как scrollEventThrottle пропс. | | reset | () => void | Принудительно разворачивает бар — вызывайте при смене таба или открытии модала. |

Логика: прокрутка вниз >6 пикс — схлопывает; вверх >6 пикс — разворачивает; в пределах 10 пикс от верха — всегда разворачивает.

<AnimatedTabBar />

import { AnimatedTabBar, useScrollAnimation } from 'react-native-ios26-tabs-custom-icon';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function MyScreen() {
  const { expandedAnim, onScroll, scrollEventThrottle, reset } = useScrollAnimation();
  const insets = useSafeAreaInsets();

  const handleTabPress = (route: string) => {
    go(route);
    reset(); // разворачиваем бар при каждом нажатии на таб
  };

  const tabs = [
    { icon: <HomeIcon />,   label: 'Главная',      onPress: () => handleTabPress('home'),    active: true },
    { icon: <SearchIcon />, label: 'Поиск',        onPress: () => handleTabPress('search')               },
    { icon: <PlusIcon />,                           onPress: () => handleTabPress('create')               }, // только иконка
    { icon: <BellIcon />,   label: 'Уведомления',  onPress: () => handleTabPress('alerts')               },
    { icon: <UserIcon />,   label: 'Профиль',      onPress: () => handleTabPress('profile')              },
  ];

  return (
    <View style={{ flex: 1 }}>
      <FlatList
        data={items}
        onScroll={onScroll}
        scrollEventThrottle={scrollEventThrottle}
        contentContainerStyle={{ paddingBottom: 100 }}
        renderItem={({ item }) => <Card item={item} />}
      />
      <AnimatedTabBar
        tabs={tabs}
        expandedAnim={expandedAnim}
        bottomInset={insets.bottom}
      />
    </View>
  );
}

Пропсы

| Пропс | Тип | По умолчанию | Описание | |-------|-----|--------------|----------| | tabs | TabItem[] | обязательный | Описание вкладок | | expandedAnim | Animated.Value | всегда развёрнут | Значение из useScrollAnimation | | bottomInset | number | 0 | Высота зоны home indicator (safe area) |

TabItem

| Поле | Тип | Описание | |------|-----|----------| | icon | ReactNode | Иконка (любой компонент) | | onPress | () => void | Обработчик нажатия | | active | boolean | Подсвечивает вкладку фоновой пилюлей | | label | string | Текстовый лейбл под иконкой (опционально) | | labelStyle | TextStyle | Переопределение стиля лейбла | | id | string | Стабильный React-ключ — укажите если у вкладок нет лейблов и порядок может меняться |

Поведение лейблов

| label указан | Бар развёрнут | Бар схлопнут | |---|---|---| | ✅ да | иконка + текст | только иконка (текст плавно исчезает) | | ❌ нет | только иконка | только иконка |

Анимация лейблов работает автоматически. Можно свободно миксовать вкладки с лейблами и без.

Производительность

  • Дисковый кеш — изображения сохраняются между перезапусками, без сети при холодном старте
  • Memory cache — NSCache с лимитом 50 / 20MB для мгновенного доступа
  • JS-дедупликация — повторный вызов setTabIcon с теми же параметрами — no-op
  • Отмена загрузок — быстрое переключение аватаров отменяет предыдущие запросы
  • Prefetch — предзагрузка всех иконок при старте для отображения без задержки

Запуск примера

cd example
npm install
cd ios && pod install && cd ..
npx react-native run-ios

Типы

| Тип | Описание | |-----|----------| | BaseImageOptions | Общие поля uri, base64, size | | TabIconOptions | Параметры для setTabIcon / useTabIcon | | AvatarOptions | Параметры для setProfileAvatar / useProfileAvatar | | CacheInfo | Форма возвращаемого значения getCacheInfo() | | TabItem | Описание одной вкладки для AnimatedTabBar | | AnimatedTabBarProps | Пропсы компонента <AnimatedTabBar /> | | ScrollAnimationResult | Возвращаемый тип useScrollAnimation() | | TabBarTheme | (планируется) цвет и скругление для AnimatedTabBar | | TabBadgeOptions | (планируется) бейдж с числом или точкой на иконке | | ScrollAnimationConfig | (планируется) кастомные пороги и длительность анимации |


Архитектура

src/
  NativeTabsCustomIcon.ts   JS-мост — кеш дедупликации, резолвер source, mock-режим
  hooks.ts                  useTabIcon, useProfileAvatar, useIsAvailable, useCacheInfo
  types.ts                  Все экспортируемые TypeScript-типы
  index.ts                  Публичный API (barrel)
  components/
    AnimatedTabBar.tsx      Анимированный нижний таб-бар
  hooks/
    useScrollAnimation.ts   Детектор направления скролла → Animated.Value

ios/NativeTabsCustomIcon/
  NativeTabsCustomIcon.m        ← ObjC bridge
  NativeTabsCustomIcon.swift    ← Нативная реализация
    ├── Загрузка изображений (URLSession async, отменяемая)
    ├── Двухуровневый кеш (NSCache память + диск)
    ├── Потокобезопасная защита от гонок (NSLock + UUID)
    ├── Рендер круглого аватара (aspect-fill + clip)
    └── Поиск tab bar (iOS 26 UITab / legacy UITabBarItem)

Лицензия

MIT