মূল পয়েন্ট
Pull-to-Refresh একটি ইউজার ইন্টারফেস প্যাটার্ন যেখানে ব্যবহারকারী কন্টেন্ট রিফ্রesh করতে একটি তালিকা বা স্ক্রোলযোগ্য এলাকা নিচে টানে (pull down)। দৃশ্যত, অঙ্গভঙ্গিটি একটি লোডিং নির্দেশক (স্পিনার) সহ আসে যা স্ক্রিনের শীর্ষে দেখা যায় এবং ডেটা প্রাপ্তির পরে অদৃশ্য হয়ে যায়। প্যাটার্নটি iPhone এর জন্য Tweetie অ্যাপ (2008) দ্বারা জনপ্রিয় হয়েছিল এবং পরবর্তীতে Apple (iOS 6 — UIRefreshControl) এবং Google (Android Support Library — SwipeRefreshLayout) দ্বারা মান নির্ধারণ করা হয়েছিল।
প্রযুক্তিগত দৃষ্টিকোণ থেকে, Pull-to-Refresh হল প্যানিং (আঙুলের স্থানচ্যুতি ট্র্যাক করা) এবং একটি থ্রেশহোল্ডে পৌঁছালে ট্রিগারের সংমিশ্রণ। ব্যবহারকারী প্রতিরোধ (প্রতিরোধী ওভারস্ক্রোল) অতিক্রম করে তালিকা নিচে টানে এবং থ্রেশহোল্ড (~80px iOS এ, ~64dp Android এ) অতিক্রম করার পরে নির্দেশকের অ্যানিমেশন এবং অ্যাসিঙ্ক্রোনাস লোডিং শুরু হয়। যদি ব্যবহারকারী থ্রেশহোল্ডের আগে আঙুল ছেড়ে দেয়, তাহলে তালিকা রিফ্রesh না করেই তার মূল অবস্থানে ফিরে আসে।
Material Design Guidelines অনুসারে, Pull-to-Refresh ন্যাভিগেশন বা ট্যাব পরিবর্তনের জন্য ব্যবহার করা উচিত নয় — এর একমাত্র উদ্দেশ্য ডেটা রিফ্রesh। IT Sectr এ, আমরা নিউজ ফিড, অর্ডার ফিড এবং চ্যাটে Pull-to-Refresh ব্যবহার করি যেখানে ডেটার সতেজতা ব্যবহারকারীর অভিজ্ঞতার জন্য গুরুত্বপূর্ণ।
UIRefreshControl Pull-to-Refresh এর জন্য মানক iOS নিয়ন্ত্রণ, যা iOS 6 থেকে উপলব্ধ। UIRefreshControl refreshControl প্রপার্টি (iOS 10+) এর মাধ্যমে UITableViewController এ বা পুরনো সংস্করণে টেবিলের সাবভিউ হিসাবে যোগ করা হয়। এতে কাস্টমাইজযোগ্য রঙ (tintColor), title অ্যাট্রিবিউট এবং একটি লেবেল সহ অ্যাট্রিবিউটেড স্ট্রিং (যেমন "আপডেট হচ্ছে...") সহ একটি বিল্ট-ইন স্পিনার রয়েছে।
UIRefreshControl target-action মেকানিজমের মাধ্যমে কাজ করে: যখন অঙ্গভঙ্গি সক্রিয় হয়, নির্দিষ্ট মেথড কল করা হয় (যেমন refresh(_:))। মেথডের ভিতরে, অ্যাসিঙ্ক্রোনাস ডেটা লোডিং করা হয়। শেষ হলে, অ্যানিমেশন সহ নির্দেশক লুকানোর জন্য endRefreshing() কল করা হয়। UIRefreshControl স্বয়ংক্রিয়ভাবে অঙ্গভঙ্গির সংবেদনশীলতা পরিচালনা করে — এটি কেবল তখনই ট্রিগার হয় যখন টেবিল উপরের অবস্থানে থাকে (contentOffset.y <= 0)।
tintColor প্রপার্টি স্পিনারের রঙ সেট করে। attributedTitle শেষ হওয়ার পরে "২ মিনিট আগে আপডেট করা হয়েছে" এর মতো টেক্সট দেখাতে দেয়। iOS 10 থেকে, UIRefreshControl UIActivityIndicatorView বা স্থায়ী কাস্টম ভিউ এর মাধ্যমে কাস্টম অ্যানিমেশন সমর্থন করে। IT Sectr এ, আমরা ব্র্যান্ড অনুযায়ী tintColor কনফিগার করি এবং attributedTitle এর মাধ্যমে শেষ আপডেটের সময় দেখাই — এটি ব্যবহারকারীদের ডেটার উপর আস্থা বাড়ায়।
SwipeRefreshLayout Android Support Library (androidx.swiperefreshlayout) থেকে একটি ViewGroup যা স্ক্রোলযোগ্য কন্টেন্ট (RecyclerView, NestedScrollView, ListView) র্যাপ করে এবং Pull-to-Refresh কার্যকারিতা যোগ করে। UIRefreshControl (যা একটি নিয়ন্ত্রণ, কন্টেইনার নয়) এর বিপরীতে, SwipeRefreshLayout একটি কন্টেইনার যা চাইল্ডের টাচ ইভেন্ট ইন্টারসেপ্ট করে এবং থ্রেশহোল্ড অতিক্রম করলে রিফ্রesh নির্দেশক ট্রিগার করে।
SwipeRefreshLayout setColorSchemeColors() এর মাধ্যমে রঙ কাস্টমাইজেশন সহ একটি বৃত্তাকার Material Design প্রগতি নির্দেশক ব্যবহার করে। setOnRefreshListener মেথড onRefresh() কলব্যack সেট করে, যাতে অ্যাসিঙ্ক্রোনাস লোডিং করা হয়। শেষ হওয়ার পরে, নির্দেশক লুকানোর জন্য setRefreshing(false) কল করা হয়। গুরুত্বপূর্ণ: setRefreshing(true) আবার onRefresh() কল করে — তাই প্রোগ্রাম্যাটিকভাবে রিফ্রesh শুরু করতে, একটি ফ্ল্যাগ বা post-মেথড ব্যবহার করুন।
setProgressBackgroundColorSchemeResource প্রপার্টি নির্দেশকের পটভূমি পরিবর্তন করে। setSize(SwipeRefreshLayout.LARGE) স্পিনারের আকার সেট করে। XML লেআউটে, SwipeRefreshLayout RecyclerView র্যাপ করে: swipe_refresh_layout → recycler_view। Google I/O 2024 অনুসারে, SwipeRefreshLayout 85% Android অ্যাপে কন্টেন্ট ফিড সহ ব্যবহৃত হয়। IT Sectr এ, আমরা অ্যাসিঙ্ক্রোনাসভাবে লোড করা তালিকা সহ সমস্ত স্ক্রিন SwipeRefreshLayout এ র্যাপ করি — এটি সমস্ত Android সংস্করণে একটি অভিন্ন UX প্রদান করে।
Android 12 (Material You) থেকে শুরু করে, Google material-1.6.0+ লাইব্রেরি (Compose এর জন্য androidx.compose.material3.pulltorefresh) থেকে নতুন Material Pull-to-Refresh ব্যবহার করার পরামর্শ দেয়। নতুন API স্প্রিং অ্যানিমেশন সমর্থন এবং ওয়ালপেপারের উপর ভিত্তি করে অভিযোজিত রঙ সহ একটি অ্যানিমেটেড নির্দেশক ব্যবহার করে। SwipeRefreshLayout Android 12 এর নিচের সংস্করণগুলির জন্য সামঞ্জস্যপূর্ণ থাকে।
Pull-to-Refresh বাস্তবায়নে সহজ প্যাটার্ন, কিন্তু এতে বেশ কয়েকটি সাধারণ ভুল রয়েছে যা UX হ্রাস করে। আসুন সেগুলি এবং প্রতিরোধের উপায়গুলি দেখি।
IT Sectr এ, আমরা টেস্ট সার্ভার লগে ডুপ্লিকেট অনুরোধ খুঁজে পাওয়ার পরে প্রতিটি প্রকল্পে isRefreshing চেক যুক্ত করেছি — দেখা গেছে যে দ্রুত আঙুলের ব্যবহারকারীরা টানা 3 বার পর্যন্ত রিফ্রesh ট্রিগার করছিল।
কাস্টম স্পিনার রঙ এবং অ্যাট্রিবিউটেড শিরোনাম সহ UITableViewController এ Pull-to-Refresh যোগ করে। ডেটা লোড হওয়ার পরে, নির্দেশক লুকিয়ে যায়।
import UIKit
class FeedTableViewController: UITableViewController {
private var items: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.refreshControl = UIRefreshControl()
refreshControl?.tintColor = .systemBlue
refreshControl?.attributedTitle = NSAttributedString(
string: "রিফ্রesh করতে টানুন"
)
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 প্রপার্টি (iOS 10+) UIRefreshControl সেট করে। .valueChanged ইভেন্ট সহ addTarget অঙ্গভঙ্গি সক্রিয় হলে ফায়ার হয়। endRefreshing() বাধ্যতামূলক — এটি ছাড়া নির্দেশক অনির্দিষ্টকালের জন্য ঘুরতে থাকে। অ্যাসিঙ্ক্রোনাস লোডিং DispatchQueue.main.asyncAfter দিয়ে সিমুলেটেড — বাস্তব প্রকল্পে, URLSession বা async/await ব্যবহার করুন।
কাস্টম নির্দেশক রঙ সহ SwipeRefreshLayout এ RecyclerView র্যাপ করে। onRefresh লোডিং শুরু করে এবং শেষ হওয়ার পরে নির্দেশক লুকায়।
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 ঘূর্ণায়মান Material Design নির্দেশকের রঙ সেট করে। isRefreshing = false finally ব্লকে কল করা হয় যাতে লোডিং ত্রুটিতেও নির্দেশক লুকিয়ে যায়। ViewModelScope.launch ফ্র্যাগমেন্ট জীবনচক্রের মধ্যে একটি coroutine চালায় — ফ্র্যাগমেন্ট ধ্বংস হলে, coroutine স্বয়ংক্রিয়ভাবে বাতিল হয়, মেমরি লিক প্রতিরোধ করে।
আধুনিক SwiftUI .refreshable মডিফায়ার প্রদান করে যা স্বয়ংক্রিয়ভাবে List বা ScrollView এ Pull-to-Refresh যোগ করে।
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 মডিফায়ার একটি async-ক্লোজার নেয় যা Pull-to-Refresh এ এক্সিকিউট হয়। SwiftUI স্বয়ংক্রিয়ভাবে রিফ্রesh নির্দেশক দেখায় এবং লুকায়, স্টেট রেস পরিচালনা করে (বর্তমান রিফ্রesh শেষ না হওয়া পর্যন্ত নতুন রিফ্রesh শুরু করে না), এবং প্ল্যাটফর্ম অনুযায়ী অ্যানিমেশন অভিযোজিত করে। iOS 15+ এর জন্য, SwiftUI এ Pull-to-Refresh বাস্তবায়নের এটি পছন্দের উপায়।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
হ্যাঁ, SwiftUI List বা ScrollView এর জন্য .refreshable মডিফায়ার প্রদান করে, যা iOS 15 থেকে উপলব্ধ। ক্লোজারের ভিতরে, async ডেটা লোডিং কোড এক্সিকিউট হয়। SwiftUI স্বয়ংক্রিয়ভাবে রিফ্রesh নির্দেশক পরিচালনা করে এবং বর্তমান লোডিং শেষ না হওয়া পর্যন্ত পুনরাবৃত্ত ট্রিগার ব্লক করে — এটি নতুন প্রকল্পের জন্য প্রস্তাবিত মানক পদ্ধতি।
isRefreshing ফ্ল্যাগ ব্যবহার করুন: লোডিং শুরুতে true এবং শেষে false সেট করুন। iOS এ, UIRefreshControl স্বয়ংক্রিয়ভাবে পুনরাবৃত্ত কল ব্লক করে যতক্ষণ না endRefreshing() কল করা হয়। Android এ, onRefresh() এর শুরুতে SwipeRefreshLayout.isRefreshing চেক করুন: true হলে — return করুন। এটি প্রতি অঙ্গভঙ্গিতে একটি অনুরোধ নিশ্চিত করে।
UIRefreshControl এবং SwipeRefreshLayout কেবল তখনই ট্রিগার হয় যখন তালিকা উপরের অবস্থানে থাকে (contentOffset == 0)। আর্কিটেকচার দ্বন্দ্ব দূর করে: যতক্ষণ তালিকা 1px স্ক্রোল করা থাকে, Pull-to-Refresh অঙ্গভঙ্গি সক্রিয় হয় না। যদি দ্বন্দ্ব দেখা দেয়, Android এ nestedScrollingEnabled বা টাচ ইন্টারসেপ্ট করা কাস্টম GestureRecognizer চেক করুন।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন