Önemli Noktalar
Pull-to-Refresh, kullanıcının içeriği yenilemek için bir listeyi veya kaydırılabilir alanı aşağı çektiği (pull down) bir kullanıcı arayüzü desenidir. Görsel olarak, harekete ekranın üst kısmında beliren ve veri alındıktan sonra kaybolan bir yükleme göstergesi (spinner) eşlik eder. Desen, iPhone için Tweetie uygulaması (2008) tarafından popüler hale getirilmiş ve daha sonra Apple (iOS 6 — UIRefreshControl) ve Google (Android Support Library — SwipeRefreshLayout) tarafından standartlaştırılmıştır.
Teknik açıdan Pull-to-Refresh, kaydırma (parmak yer değiştirmesini izleme) ve bir eşiğe ulaşıldığında tetiklemenin bir kombinasyonudur. Kullanıcı, direncin (dirençli aşırı kaydırma) üstesinden gelerek listeyi aşağı çeker ve eşiği (~80px iOS'ta, ~64dp Android'te) aştıktan sonra gösterge animasyonu ve asenkron yükleme başlar. Kullanıcı parmağını eşikten önce bırakırsa, liste yenilenmeden orijinal konumuna geri döner.
Material Design Kurallarına göre, Pull-to-Refresh gezinme veya sekme değiştirme için kullanılmamalıdır — tek amacı veri yenilemedir. IT Sectr'de, veri tazeliğinin kullanıcı deneyimi için kritik olduğu haber akışlarında, sipariş listelerinde ve sohbetlerde Pull-to-Refresh kullanıyoruz.
UIRefreshControl, Pull-to-Refresh için standart iOS denetimidir ve iOS 6'dan beri kullanılabilir. UIRefreshControl, refreshControl özelliği (iOS 10+) aracılığıyla UITableViewController'a veya önceki sürümlerde tablonun bir alt görünümü olarak eklenir. Özelleştirilebilir renk (tintColor), title özelliği ve bir etiketle nitelikli dize (örneğin, "Güncelleniyor...") içeren yerleşik bir Spinner içerir.
UIRefreshControl, target-action mekanizması aracılığıyla çalışır: hareket etkinleştirildiğinde, belirtilen yöntem çağrılır (örneğin, refresh(_:)). Yöntemin içinde asenkron veri yükleme gerçekleştirilir. Tamamlandıktan sonra, animasyonla göstergeyi gizleyen endRefreshing() çağrılır. UIRefreshControl, hareket hassasiyetini otomatik olarak yönetir — yalnızca tablo üst konumdayken tetiklenir (contentOffset.y <= 0).
tintColor özelliği Spinner'ın rengini ayarlar. attributedTitle, tamamlandıktan sonra "2 dakika önce güncellendi" gibi metin gösterilmesini sağlar. iOS 10'dan itibaren UIRefreshControl, UIActivityIndicatorView veya kalıcı özel görünümler aracılığıyla özel animasyonları destekler. IT Sectr'de, tintColor'ı markaya göre yapılandırıyor ve attributedTitle aracılığıyla son güncelleme zamanını gösteriyoruz — bu, kullanıcıların verilere olan güvenini artırıyor.
SwipeRefreshLayout, Android Support Library'den (androidx.swiperefreshlayout) kaydırılabilir içeriği (RecyclerView, NestedScrollView, ListView) sarmalayan ve Pull-to-Refresh işlevselliği ekleyen bir ViewGroup'tür. UIRefreshControl'ün (bir denetim olan, kap olmayan) aksine, SwipeRefreshLayout alt öğenin dokunma olaylarını kesen ve eşik aşıldığında yenileme göstergesini tetikleyen bir kaptır.
SwipeRefreshLayout, setColorSchemeColors() aracılığıyla renk özelleştirmesi olan dairesel bir Material Design ilerleme göstergesi kullanır. setOnRefreshListener yöntemi, asenkron yüklemenin gerçekleştirildiği onRefresh() geri çağırmasını ayarlar. Tamamlandıktan sonra, göstergeyi gizlemek için setRefreshing(false) çağrılır. Önemli: setRefreshing(true) onRefresh()'i tekrar çağırır — bu nedenle programlı olarak yenileme başlatmak için bir flag veya post yöntemi kullanın.
setProgressBackgroundColorSchemeResource özelliği göstergenin arka planını değiştirir. setSize(SwipeRefreshLayout.LARGE) Spinner boyutunu ayarlar. XML düzeninde, SwipeRefreshLayout RecyclerView'ı sarar: swipe_refresh_layout → recycler_view. Google I/O 2024'e göre, SwipeRefreshLayout içerik akışlı Android uygulamalarının %85'inde kullanılmaktadır. IT Sectr'de, asenkron olarak yüklenen listelere sahip tüm ekranları SwipeRefreshLayout ile sarıyoruz — bu, tüm Android sürümlerinde tutarlı bir UX sağlar.
Android 12'den (Material You) itibaren Google, material-1.6.0+ kitaplığından (Compose için androidx.compose.material3.pulltorefresh) yeni Material Pull-to-Refresh kullanılmasını önermektedir. Yeni API, yay animasyonu desteği ve duvar kağıdına dayalı uyarlanabilir renk ile animasyonlu bir gösterge kullanır. SwipeRefreshLayout, Android 12'nin altındaki sürümlerle uyumlu kalır.
Pull-to-Refresh uygulaması basit bir desendir, ancak kullanıcı deneyimini düşüren birkaç yaygın hata içerir. Bunları ve önleme yöntemlerini inceleyelim.
IT Sectr'de, test sunucusu günlüklerinde yinelenen istekler keşfettikten sonra her projeye isRefreshing kontrolü ekledik — hızlı parmaklı kullanıcıların yenilemeyi art arda 3 defaya kadar tetiklediği ortaya çıktı.
Özel Spinner rengi ve nitelikli başlık ile UITableViewController'a Pull-to-Refresh ekler. Veri yüklendikten sonra gösterge gizlenir.
import UIKit
class FeedTableViewController: UITableViewController {
private var items: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.refreshControl = UIRefreshControl()
refreshControl?.tintColor = .systemBlue
refreshControl?.attributedTitle = NSAttributedString(
string: "Yenilemek için çekin"
)
refreshControl?.addTarget(
self,
action: #selector(refreshData),
for: .valueChanged
)
}
@objc private func refreshData() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
self.items = FeedService().fetchLatest()
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
}
tableView.refreshControl özelliği (iOS 10+) UIRefreshControl'ü ayarlar. .valueChanged olayı ile addTarget, hareket etkinleştirildiğinde tetiklenir. endRefreshing() zorunludur — onsuz gösterge sonsuza kadar döner. Asenkron yükleme DispatchQueue.main.asyncAfter ile simüle edilmiştir — gerçek bir projede URLSession veya async/await kullanın.
Özel gösterge renkleriyle SwipeRefreshLayout içinde RecyclerView'ı sarar. onRefresh yüklemeyi başlatır ve tamamlandıktan sonra göstergeyi gizler.
class FeedFragment : Fragment() {
private var _binding: FragmentFeedBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentFeedBinding.inflate(inflater, container, false)
binding.swipeRefreshLayout.setColorSchemeColors(
resources.getColor(R.color.brand_blue, null),
resources.getColor(R.color.brand_green, null)
)
binding.swipeRefreshLayout.setOnRefreshListener {
loadData()
}
return binding.root
}
private fun loadData() {
viewModelScope.launch {
try {
val result = repository.getLatestFeed()
adapter.submitList(result)
} finally {
binding.swipeRefreshLayout.isRefreshing = false
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
setColorSchemeColors dönen Material Design göstergesinin renklerini ayarlar. isRefreshing = false, yükleme hatası durumunda bile göstergeyi gizlemek için finally bloğunda çağrılır. ViewModelScope.launch, parçacık yaşam döngüsü içinde bir coroutine yürütür — parçacık yok edildiğinde, coroutine otomatik olarak iptal edilir ve bellek sızıntılarını önler.
Modern SwiftUI, List veya ScrollView'e otomatik olarak Pull-to-Refresh ekleyen .refreshable değiştiricisini sağlar.
import SwiftUI
struct FeedView: View {
@State private var items: [String] = []
var body: some View {
List(items, id: \.self) { item in
Text(item)
}
.refreshable {
items = await FeedService().fetchLatestAsync()
}
}
}
.refreshable değiştiricisi, Pull-to-Refresh'te yürütülen bir async kapanışı alır. SwiftUI otomatik olarak yenileme göstergesini gösterir ve gizler, durum yarışlarını yönetir (mevcut yenileme tamamlanana kadar yeni bir yenileme başlatmaz) ve animasyonu platforma uyarlar. iOS 15+ için, SwiftUI'de Pull-to-Refresh uygulamanın tercih edilen yoludur.
Sıkça Sorulan Sorular
Evet, SwiftUI, List veya ScrollView için .refreshable değiştiricisini sağlar ve iOS 15'ten itibaren kullanılabilir. Kapanış içinde asenkron veri yükleme kodu yürütülür. SwiftUI otomatik olarak yenileme göstergesini yönetir ve mevcut yükleme tamamlanana kadar tekrarlanan tetiklemeleri engeller — bu, yeni projeler için standart önerilen yaklaşımdır.
isRefreshing flag'i kullanın: yükleme başlangıcında true, tamamlandıktan sonra false olarak ayarlayın. iOS'ta, endRefreshing() çağrılana kadar UIRefreshControl otomatik olarak tekrarlanan çağrıları engeller. Android'te, onRefresh() başlangıcında SwipeRefreshLayout.isRefreshing'i kontrol edin: true ise — return yapın. Bu, her hareket için bir istek garanti eder.
UIRefreshControl ve SwipeRefreshLayout yalnızca liste üst konumdayken tetiklenir (contentOffset == 0). Mimari çakışmayı ortadan kaldırır: liste 1px bile kaydırıldığı sürece Pull-to-Refresh hareketi etkinleşmez. Bir çakışma olursa, Android'te nestedScrollingEnabled'i veya dokunmaları engelleyen özel GestureRecognizer'ların varlığını kontrol edin.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun