Niekończące się przewijanie (Infinite Scroll) to technika automatycznego ładowania treści po osiągnięciu przez użytkownika dolnej granicy bieżącej listy. Według danych UX Design Collective, 2024, Infinite Scroll zwiększa czas sesji w mediach społecznościowych o 40–60% w porównaniu z paginacją. W programowaniu mobilnym ta technika jest realizowana poprzez kombinację scroll listeners i zapytań API z cursor-based paginacją. Niekończące się przewijanie stało się de facto standardem dla strumieni treści, ale wymaga starannej implementacji, aby uniknąć problemów z wydajnością i nawigacją.
Najważniejsze
Niekończące się przewijanie (Infinite Scroll) to wzorzec ładowania danych, w którym nowe elementy są automatycznie dodawane na koniec listy podczas przewijania. Użytkownik nie klika przycisków „Dalej“ ani „Wczytaj więcej“ — system sam określa moment, w którym należy zażądać kolejnej porcji danych, i płynnie wstawia nowe rekordy do istniejącej listy.
Infinite Scroll stał się popularny dzięki mediom społecznościowym — Twitter, Instagram i TikTok używają go jako głównego mechanizmu dostarczania treści. Według danych Nielsen Norman Group (2024), Infinite Scroll zwiększa zaangażowanie o 30–50% w aplikacjach treściowych, ponieważ zmniejsza obciążenie poznawcze: użytkownik nie musi podejmować decyzji o przejściu na następną stronę. Jednak w zadaniach wymagających precyzyjnej nawigacji (wyszukiwanie, porównywanie produktów) Infinite Scroll może obniżać efektywność.
Technicznie niekończące się przewijanie składa się z trzech komponentów: scroll listener (śledzi pozycję przewijania), threshold (odległość od końca listy do wyzwolenia ładowania) i pagination mechanism (zapytanie do API i wstawienie danych). Prawidłowe ustawienie progu jest krytyczne: przy zbyt wczesnym wyzwoleniu (1000 px do końca) użytkownik otrzyma niepotrzebne zapytania, przy zbyt późnym (50 px) — zauważy pauzę w ładowaniu.
Architektura Infinite Scroll opiera się na modelu zdarzeniowym: komponent listy generuje zdarzenie po osiągnięciu progu przewijania, ViewModel je przetwarza i wywołuje repozytorium w celu załadowania kolejnej porcji danych. Po otrzymaniu odpowiedzi nowe elementy są wstawiane do listy, a UI jest aktualizowane przez adapter. Ten łańcuch musi być asynchroniczny i nie blokować wątku UI.
Podstawowy algorytm Infinite Scroll obejmuje cztery kroki. Inicjalizacja: przy pierwszym otwarciu ekranu ładowana jest pierwsza porcja danych (page 1 lub cursor = null). Śledzenie: scroll listener sprawdza, czy użytkownik osiągnął próg — zwykle 200–500 px do końca listy. Ładowanie: wysyłane jest zapytanie do API z parametrami paginacji, na UI wyświetlany jest wskaźnik ładowania (spinner w stopce). Wstawianie: nowe elementy są dodawane do adaptera, pozycja przewijania jest korygowana, aby uniknąć skoku.
Krytycznym aspektem jest debounce zapytań. Jeśli użytkownik szybko przewinie do końca, wyzwalacz może zadziałać kilka razy przed otrzymaniem odpowiedzi z serwera. Bez debounce prowadzi to do zduplikowanych zapytań (race condition). Rozwiązaniem jest blokowanie nowego zapytania, dopóki poprzednie się nie zakończy. Flaga isLoading w ViewModel zapobiega wielokrotnym wywołaniom: ustaw isLoading = true przy wysyłaniu zapytania, reset przy otrzymaniu odpowiedzi lub błędu.
Na platformach mobilnych do Infinite Scroll używane są specjalistyczne mechanizmy. Na iOS jest to prefetchDataSource w UICollectionView, który automatycznie żąda danych dla komórek poza ekranem. Na Androidzie — Paging 3 Library od Google, która zapewnia gotową architekturę z PagingSource, PagingData i PagingDataAdapter. Paging 3 obsługuje RemoteMediator do łączenia danych sieciowych i lokalnych oraz automatycznie zarządza stanem ładowania.
Paginacja offset-based używa parametrów page i size: page=2, size=20 zwraca rekordy 21–40. To podejście jest proste w implementacji, ale ma fundamentalny problem — jeśli między zapytaniami do bazy danych zostaną dodane lub usunięte rekordy, przesunięcie się zaburza (użytkownik widzi duplikaty lub luki). W strumieniach z wysoką częstotliwością zmian (newsy, komentarze) paginacja offset-based daje nieprawidłowe wyniki.
Paginacja cursor-based używa unikalnego identyfikatora ostatniego elementu (kursora): after=id_12345&limit=20. Serwer zwraca 20 rekordów następujących po wskazanym kursorze. To podejście gwarantuje spójność danych niezależnie od wstawień i usunięć. Według GraphQL Best Practices (2024), paginacja cursor-based jest zalecana dla wszystkich aplikacji czasu rzeczywistego, w których dane dynamicznie się zmieniają.
Wybór między podejściami zależy od typu aplikacji. W mediach społecznościowych (Instagram, TikTok) — tylko cursor-based, ponieważ strumień stale się aktualizuje. W katalogach z rzadkimi zmianami (kategorie produktów sklepu internetowego) paginacja offset-based jest dopuszczalna. W scenariuszach hybrydowych Google zaleca Paging 3 RemoteMediator, który łączy paginację cursor-based z sieci z paginacją offset-based z lokalnej bazy danych Room.
Na Androidzie standardowym podejściem jest biblioteka Paging 3 z Jetpack. PagingSource określa źródło danych (sieć lub BD), PagingData zawiera fragmenty danych, a PagingDataAdapter wyświetla je w RecyclerView. Paging 3 automatycznie zarządza prefetch distance, retry i refresh. Do integracji z siecią używany jest RemoteMediator: ładuje dane z API, zapisuje w Room i informuje PagingSource o aktualizacji. Według Google I/O 2024, ponad 60% aplikacji Android z Infinite Scroll używa Paging 3.
Przykład podstawowej implementacji Paging 3:
class FeedPagingSource(
private val api: FeedApi
) : PagingSource<String, Post>() {
override suspend fun load(
params: LoadParams<String>
): LoadResult<String, Post> {
val response = api.getFeed(
cursor = params.key,
limit = params.loadSize
)
return LoadResult.Page(
data = response.items,
prevKey = null,
nextKey = response.nextCursor
)
}
}
Na iOS do implementacji Infinite Scroll używa się UICollectionView z prefetchDataSource. Protokół UICollectionViewDataSourcePrefetching zawiera metodę collectionView(_:prefetchItemsAt:), która jest wywoływana, gdy system przewiduje przewinięcie do określonych index paths. W przeciwieństwie do Android Paging 3, na iOS nie ma wbudowanej biblioteki paginacji — programiści implementują ją ręcznie lub używają rozwiązań zewnętrznych, takich jak RxSwift + NSLayoutConstraint lub pipeline’y oparte na Combine.
Przykład prefetch na iOS:
extension FeedViewController: UICollectionViewDataSourcePrefetching {
func collectionView(
_ collectionView: UICollectionView,
prefetchItemsAt indexPaths: [IndexPath]
) {
let lastRow = collectionView.numberOfItems(inSection: 0) - 1
if indexPaths.contains(IndexPath(row: lastRow, section: 0)) {
viewModel.loadNextPage()
}
}
}
SwiftUI zapewnia bardziej deklaratywne podejście poprzez modyfikator onAppear. Programista umieszcza ProgressView na końcu listy, a gdy się pojawi, wywołuje ładowanie następnej strony. Według Apple WWDC 2024, nowe API AsyncSequence i Swift Algorithms upraszczają implementację niekończącego się przewijania, dostarczając wbudowane operatory chunking i debounce.
Głównym problemem UX Infinite Scroll jest utrata stopki i nawigacji. W sklepach internetowych użytkownik często chce przejść do stopki po kontakty lub linki. Niekończące się przewijanie czyni stopkę niedostępną — stale przesuwa się w dół wraz z ładowaniem. Rozwiązaniem jest dodanie pływającego przycisku szybkiego przewijania na górę (FAB) lub przypięcie stopki oddzielnie od listy.
Drugim problemem jest brak historii przewijania. Jeśli użytkownik zobaczył interesujący produkt na pozycji 3, przewinął do 50., a następnie kliknął „Wstecz“ — wraca na początek listy i musi ponownie przewijać do pozycji 50. Rozwiązaniem jest zapisywanie pozycji przewijania w ViewModel lub użycie state restoration na poziomie Activity/UIViewController. iOS obsługuje NSUserActivity do przywracania pozycji, Android — onSaveInstanceState.
Trzecim problemem jest wydajność przy tysiącach elementów. Jeśli wirtualizacja nie jest skonfigurowana, po 500–1000 załadowanych elementów aplikacja zaczyna zwalniać z powodu wzrostu zużycia pamięci. Rozwiązaniem jest użycie wirtualizacji RecyclerView lub UICollectionView, która przechowuje w pamięci tylko widoczne + prefetched komórki. Okresowe czyszczenie starych danych (discard pages dalej niż N stron) również zmniejsza obciążenie.
Często zadawane pytania
Niekończące się przewijanie (Infinite Scroll) to technika automatycznego ładowania treści po osiągnięciu przez użytkownika dolnej granicy listy. Nowe dane są płynnie dodawane bez konieczności klikania przycisków paginacji. Stosowane w mediach społecznościowych, strumieniach wiadomości i katalogach z dynamiczną treścią.
Paginacja wymaga ręcznego przechodzenia między stronami (przyciski „1, 2, 3“), a Infinite Scroll ładuje dane automatycznie. Paginacja jest przewidywalna i zachowuje kontekst nawigacji, Infinite Scroll zwiększa zaangażowanie, ale utrudnia dostęp do stopki i historii przewijania. Wybór zależy od rodzaju treści i celów aplikacji.
Na Androidzie zalecana jest biblioteka Paging 3 z Jetpack. Zapewnia PagingSource dla źródła danych, PagingData dla fragmentów i PagingDataAdapter dla RecyclerView. Paging 3 automatycznie zarządza prefetch, stanem ładowania i ponownymi próbami. W scenariuszach hybrydowych offline/online używaj RemoteMediator.
Zduplikowane zapytania są zapobiegane poprzez flagę debounce isLoading. Gdy pierwsze zapytanie jest wysyłane, flaga jest ustawiana na true i blokuje nowe wywołania do momentu otrzymania odpowiedzi. Po pomyślnej odpowiedzi flaga jest resetowana. Dodatkowo można użyć anulowania korutyn (Kotlin) lub Cancellable (Swift) przy przewijaniu wstecz.
Infinite Scroll nie jest odpowiedni dla e-commerce z wyszukiwaniem i porównywaniem produktów, dla aplikacji z ważną stopką (kontakty, linki), dla stron z wynikami wyszukiwania (użytkownik musi wrócić do konkretnego elementu) oraz dla stron statystyk/raportów, gdzie ważna jest całkowita liczba. W tych przypadkach używaj klasycznej paginacji lub przycisku „Wczytaj więcej“.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również