react-native-ios26-tabs-custom-icon
v1.0.0
Published
Custom profile icons and avatars for iOS 26 native tabs in React Native
Maintainers
Readme
react-native-ios26-tabs-custom-icon
Кастомные иконки профиля и аватары для нативных табов 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 использует новый
UITabAPI, на старых версиях —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 используется классический
UITabBarItemAPI.
Установка
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
