Mga Pangunahing Punto
Pull-to-Refresh — isang pattern ng interface ng gumagamit kung saan hinihila ng user (pull down) ang isang listahan o lugar na pwedeng i-scroll pababa para i-update ang nilalaman. Biswal, ang kilos ay sinamahan ng isang indicator ng pagkarga (spinner) na lumilitaw sa tuktok ng screen at nawawala pagkatapos matanggap ang data. Ang pattern ay pinasikat ng Tweetie app para sa iPhone (2008) at kalaunan ay na-standardize ng Apple (iOS 6 — UIRefreshControl) at Google (Android Support Library — SwipeRefreshLayout).
Sa teknikal na pananaw, ang Pull-to-Refresh ay kombinasyon ng panning (pagsubaybay sa paggalaw ng daliri) at trigger kapag naabot ang threshold. Hinihila ng user ang listahan pababa, nilalampasan ang resistensya (resistive overscroll), at pagkatapos lumampas sa threshold (~80px sa iOS, ~64dp sa Android) magsisimula ang animation ng indicator at asynchronous na pagkarga. Kung bitawan ng user ang daliri bago ang threshold — babalik ang listahan sa orihinal na posisyon nang walang pag-update.
Ayon sa Material Design Guidelines, ang Pull-to-Refresh ay hindi dapat gamitin para sa nabigasyon o pagpapalit ng tab — ang tanging layunin nito ay pag-update ng data. Sa IT Sectr inilalapat namin ang Pull-to-Refresh sa mga news feed, listahan ng order, at chat, kung saan ang pagiging bago ng data ay kritikal para sa karanasan ng user.
UIRefreshControl — ang karaniwang control element ng iOS para sa Pull-to-Refresh, available mula noong iOS 6. Ang UIRefreshControl ay idinadagdag sa UITableViewController sa pamamagitan ng property na refreshControl (iOS 10+) o bilang subview ng table sa mas lumang bersyon. Naglalaman ito ng built-in na spinner na may nako-configure na kulay (tintColor), attribute na title, at attributed string na may label (halimbawa, “Nag-a-update...”).
Ang UIRefreshControl ay gumagana sa pamamagitan ng target-action mechanism: kapag na-activate ang kilos, tinatawag ang tinukoy na method (halimbawa, refresh(_:)). Sa loob ng method, isinasagawa ang asynchronous na pagkarga ng data. Pagkatapos makumpleto, tinatawag ang endRefreshing(), na nagtatago ng indicator na may animation. Awtomatikong pinamamahalaan ng UIRefreshControl ang sensitivity ng kilos — naa-activate lamang ito sa tuktok na posisyon ng table (contentOffset.y <= 0).
Ang property na tintColor ay nagtatakda ng kulay ng spinner. Ang mga attribute na title ay nagpapahintulot na magpakita ng tekstong “Na-update 2 minuto ang nakalipas” pagkatapos makumpleto. Mula iOS 10, sinusuportahan ng UIRefreshControl ang custom na animation sa pamamagitan ng UIActivityIndicatorView o persistent custom views. Sa IT Sectr inaayos namin ang tintColor sa brand at ipinapakita ang oras ng huling pag-update sa pamamagitan ng attributedTitle — pinapataas nito ang tiwala ng user sa data.
SwipeRefreshLayout — isang ViewGroup mula sa Android Support Library (androidx.swiperefreshlayout) na bumabalot ng nai-scroll na nilalaman (RecyclerView, NestedScrollView, ListView) at nagdaragdag ng functionality ng Pull-to-Refresh. Hindi tulad ng UIRefreshControl (na isang control, hindi container), ang SwipeRefreshLayout ay isang container na humaharang sa touch events ng anak at nag-a-activate ng update indicator kapag lumampas sa threshold.
Ang SwipeRefreshLayout ay gumagamit ng circular progress indicator ng Material Design na may configuration ng kulay sa pamamagitan ng setColorSchemeColors(). Ang method na setOnRefreshListener ay nagtatakda ng callback na onRefresh(), kung saan isinasagawa ang asynchronous na pagkarga. Pagkatapos makumpleto, tinatawag ang setRefreshing(false) para itago ang indicator. Mahalaga: ang setRefreshing(true) ay tumatawag muli ng onRefresh() — kaya para sa programmatic na pagsisimula ng pag-update, gumamit ng flag o post method.
Ang property na setProgressBackgroundColorSchemeResource ay nagbabago ng background ng indicator. setSize(SwipeRefreshLayout.LARGE) — laki ng spinner. Sa XML layout, bumabalot ang SwipeRefreshLayout ng RecyclerView: swipe_refresh_layout → recycler_view. Ayon sa Google I/O 2024, ang SwipeRefreshLayout ay ginagamit sa 85% ng Android apps na may content feed. Sa IT Sectr binabalot namin ang lahat ng screen na may asynchronously loaded na listahan sa SwipeRefreshLayout — nagbibigay ito ng pare-parehong UX sa lahat ng bersyon ng Android.
Mula Android 12 (Material You), inirerekomenda ng Google ang paggamit ng bagong Material Pull-to-Refresh mula sa library na material-1.6.0+ (androidx.compose.material3.pulltorefresh para sa Compose). Ang bagong API ay gumagamit ng animated na indicator na may suporta para sa spring animation at adaptive na kulay batay sa wallpaper. Ang SwipeRefreshLayout ay nananatiling compatible para sa mga bersyon na mas mababa sa Android 12.
Pull-to-Refresh — isang pattern na madaling ipatupad, ngunit naglalaman ng ilang karaniwang pagkakamali na nagpapababa ng UX. Suriin natin ang mga ito at mga paraan ng pag-iwas.
Sa IT Sectr nagdagdag kami ng pagsusuri ng isRefreshing sa bawat proyekto pagkatapos matuklasan ang mga duplicate na kahilingan sa log ng test server — lumabas na ang mga user na may mabilis na daliri ay nagti-trigger ng pag-update nang hanggang 3 beses nang magkakasunod.
Nagdaragdag ng Pull-to-Refresh sa UITableViewController na may custom na kulay ng spinner at attributed title. Pagkatapos ma-load ang data, ang indicator ay itatago.
import UIKit
class FeedTableViewController: UITableViewController {
private var items: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.refreshControl = UIRefreshControl()
refreshControl?.tintColor = .systemBlue
refreshControl?.attributedTitle = NSAttributedString(
string: “Hilahin para i-update”
)
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()
}
}
}
Ang property na tableView.refreshControl (iOS 10+) ay nagtatakda ng UIRefreshControl. Ang addTarget na may kaganapang .valueChanged ay nagti-trigger kapag na-activate ang kilos. Ang endRefreshing() ay sapilitan — kung wala ito, ang indicator ay iikot nang walang hanggan. Ang asynchronous na pagkarga ay ginagaya sa pamamagitan ng DispatchQueue.main.asyncAfter — sa totoong proyekto ito ay magiging URLSession o async/await.
Bumabalot ng RecyclerView sa SwipeRefreshLayout na may custom na kulay ng indicator. Ang onRefresh ay nag-uumpisa ng pagkarga at itinatago ang indicator pagkatapos makumpleto.
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
}
}
Ang setColorSchemeColors ay nagtatakda ng mga kulay ng umiikot na Material Design indicator. Ang isRefreshing = false ay sapilitang tinatawag sa finally upang itago ang indicator kahit na may error sa pagkarga. Ang ViewModelScope.launch ay nagpapatakbo ng coroutine sa lifecycle ng fragment — kapag nawasak ang fragment, ang coroutine ay awtomatikong kinakansela, na pumipigil sa pagtagas ng memorya.
Ang modernong SwiftUI ay nagbibigay ng modifier na .refreshable, na awtomatikong nagdaragdag ng Pull-to-Refresh sa List o ScrollView.
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()
}
}
}
Ang modifier na .refreshable ay tumatanggap ng async-closure na isinasagawa sa Pull-to-Refresh. Awtomatikong ipinapakita at itinatago ng SwiftUI ang update indicator, pinamamahalaan ang race condition (hindi nagsisimula ng reload hanggang sa matapos ang kasalukuyan) at iniaangkop ang animation sa platform. Para sa iOS 15+ ito ang preferred na paraan ng pagpapatupad ng Pull-to-Refresh sa SwiftUI.
Mga Madalas Itanong
Oo, ang SwiftUI ay nagbibigay ng modifier na .refreshable para sa List o ScrollView, available mula noong iOS 15. Sa loob ng closure, isinasagawa ang async code ng pagkarga ng data. Awtomatikong pinamamahalaan ng SwiftUI ang update indicator at hinaharangan ang muling pag-trigger hanggang sa matapos ang kasalukuyang pagkarga — ito ang karaniwang recommended na diskarte para sa mga bagong proyekto.
Gamitin ang flag na isRefreshing: itakda ang true sa simula ng pagkarga at false pagkatapos makumpleto. Sa iOS, awtomatikong hinaharangan ng UIRefreshControl ang muling pagtawag hanggang sa matawag ang endRefreshing(). Sa Android, suriin ang SwipeRefreshLayout.isRefreshing sa simula ng onRefresh(): kung true — return. Ginagarantiyahan nito ang isang kahilingan bawat kilos.
Ang UIRefreshControl at SwipeRefreshLayout ay naa-activate lamang sa tuktok na posisyon ng listahan (contentOffset == 0). Hindi kasama ng arkitektura ang conflict: habang ang listahan ay naka-scroll kahit 1px, ang Pull-to-Refresh na kilos ay hindi maa-activate. Kung magkaroon ng conflict — suriin ang nestedScrollingEnabled sa Android o ang pagkakaroon ng custom na GestureRecognizer na humaharang sa pagpindot.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din