Реализация просмотра PDF-документов в мобильном приложении
PDF-вьювер — задача, которая выглядит тривиальной, пока не сталкиваешься с 200-страничным PDF на iPhone SE: скролл дёргается, память растёт, приложение получает SIGKILL от iOS. Проблема в рендеринге: каждая страница PDF — это векторный документ, требующий растеризации в растровое изображение под конкретный масштаб и разрешение экрана.
Нативный рендеринг vs WebView
<WebView source={{ uri: 'file://...' }} — самый быстрый способ показать PDF. iOS WebKit рендерит PDF нативно. Минусы: нет контроля над UI (не добавить custom toolbar, аннотации, поиск), PDF открывается целиком в память. На 50+ страницах — риск OOM.
Нативный рендеринг через PDFKit (iOS) и PdfRenderer (Android) даёт полный контроль, но требует нативных модулей в React Native.
react-native-pdf: готовое решение
react-native-pdf — React Native обёртка над нативными PDF API обеих платформ. Под капотом: PDFKit на iOS, PdfRenderer на Android. Постраничный рендеринг — в памяти только видимые страницы + 1–2 буферных.
import Pdf from 'react-native-pdf';
const PDFViewer = ({ uri }: { uri: string }) => {
const [totalPages, setTotalPages] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
return (
<Pdf
source={{ uri, cache: true }} // кэшируем загруженный файл
onLoadComplete={(numberOfPages) => setTotalPages(numberOfPages)}
onPageChanged={(page) => setCurrentPage(page)}
onError={(error) => console.error(error)}
style={{ flex: 1 }}
enablePaging // листание постранично, а не скролл
horizontal // горизонтальный режим
fitPolicy={0} // 0 = fit width, 1 = fit height, 2 = fit both
scale={1.0}
minScale={0.5}
maxScale={3.0}
/>
);
};
cache: true — первая загрузка сохраняет файл в директорию кэша приложения. Повторное открытие не делает HTTP-запрос. Важно: кэш не управляется автоматически, нужна очистка по TTL или размеру.
Проблема с большими PDF: ленивая загрузка страниц
При открытии PDF из URL библиотека скачивает весь файл перед рендерингом. 50 МБ PDF — пользователь ждёт. Правильное решение: HTTP Range requests, если сервер поддерживает Accept-Ranges: bytes.
Нативная реализация для iOS через PDFDocument(url:) поддерживает progressive rendering при URLSession с Range-запросами. Для React Native — custom native module, который использует PDFDocument с CGPDFDataProvider для потоковой загрузки.
Для большинства проектов проще: показываем первую страницу как placeholder (thumbnail, предварительно сгенерированный на сервере через pdf2pic или ghostscript), пока загружается полный файл.
Поиск по тексту и аннотации
react-native-pdf поддерживает поиск через pdfRef.current?.startSearch(query) — нативный поиск по тексту документа. Подсветка совпадений встроена.
Аннотации (выделение, заметки, подписи) — отдельная задача. PSPDFKit — коммерческий SDK ($199/мес) с полным набором аннотаций. Для open-source: PDFTron (теперь Apryse) с free-тиром.
Безопасность: защищённые PDF
PDF с паролем: react-native-pdf поддерживает password prop. Корпоративные DRM-защищённые PDF (Adobe AEPD, Microsoft IRM) — нативные библиотеки операторов, в RN не реализованы напрямую.
Flutter: syncfusion_flutter_pdfviewer
Syncfusion предоставляет SfPdfViewer — полнофункциональный PDF вьювер для Flutter с постраничным рендерингом, поиском и выделением. Community-лицензия бесплатна при доходе до $1 млн/год.
SfPdfViewer.network(
'https://cdn.example.com/document.pdf',
onPageChanged: (PdfPageChangedDetails details) {
setState(() => _currentPage = details.newPageNumber);
},
)
Оценка
PDF вьювер с кэшированием, поиском и прогрессивной загрузкой: 2–3 недели для одной платформы. Кросс-платформенная реализация с аннотациями: 4–6 недель.







