সবাইকে অভিবাদন! এই নিবন্ধে আমরা শিখতে যাচ্ছি কিভাবে iOS অ্যাপে UISearchController ব্যবহার করতে হয়।
আমরা কি নির্মাণ করতে যাচ্ছি?
আমরা একটি মুভি অনুসন্ধান অ্যাপ্লিকেশন তৈরি করতে যাচ্ছি যা TMDB API ব্যবহার করে মুভির তথ্য আনয়ন করে এবং ব্যবহারকারীর অনুসন্ধান প্রশ্নের উপর ভিত্তি করে একটি UICollectionView ব্যবহার করে প্রদর্শন করে৷
প্রকল্প সেটআপ
Xcode খুলুন এবং একটি নতুন ফাঁকা iOS অ্যাপ প্রকল্প তৈরি করুন - নিশ্চিত করুন যে আপনি UIKit নির্বাচন করেছেন এবং SwiftUI নয়৷
এই অ্যাপটিতে আমরা MVC প্যাটার্ন ব্যবহার করতে যাচ্ছি তাই নিম্নলিখিত গ্রুপ এবং সুইফট ফাইলগুলি তৈরি করে প্রকল্পটি সংগঠিত করুন:
এখন আপনার Xcode প্রকল্প বন্ধ করুন। টার্মিনাল খুলুন এবং আপনার প্রকল্প ডিরেক্টরিতে যান। সিনেমার পোস্টার ইমেজগুলিকে অ্যাসিঙ্ক্রোনাসলি ডাউনলোড এবং ক্যাশে করার জন্য এখানে আমাদের SD WebImage Cocoa Pods যোগ করতে হবে।
টার্মিনালে নিম্নলিখিত কমান্ডটি টাইপ করুন:
pod init
এখন আপনি যখন ডিরেক্টরির বিষয়বস্তু তালিকাভুক্ত করবেন, আপনি দেখতে পাবেন যে একটি নতুন পডফাইল রয়েছে। যেকোনো টেক্সট এডিটর ব্যবহার করে ফাইল খুলুন (এখানে আমি ভিম ব্যবহার করেছি)। আপনার পডফাইলটি সম্পাদনা করুন যাতে এটি নীচের চিত্রের মতো দেখায়। পডফাইল সংরক্ষণ করুন এবং বন্ধ করুন।
এখন যেহেতু আমরা SD WebImage নির্দিষ্ট করেছি, আমরা নিচের কমান্ডটি চালিয়ে নির্ভরতাগুলি ইনস্টল করতে পারি:
pod install
আপনি দেখতে পাচ্ছেন, আমরা সফলভাবে আমাদের iOS প্রকল্পে SD WebImage পড যোগ করেছি। এখন Xcode-এ আমাদের প্রজেক্ট খুলতে নিচের কমান্ডটি চালান।
open PROJECT_NAME.xcworkspace
Xcode খোলার পরে, Command+B
টিপে আপনি আপনার প্রকল্প তৈরি করেছেন তা নিশ্চিত করুনইউআইকিট এবং প্রোগ্রাম্যাটিক ইউআই ব্যবহার করে কীভাবে ইউজার ইন্টারফেস ডিজাইন করবেন
আমাদের অ্যাপটির সার্চ বার ধরে রাখার জন্য তিনটি UIElement নেভিগেশন বার, প্রকৃত অনুসন্ধানের জন্য UISearchBarController এবং অনুসন্ধান ফলাফল প্রদর্শনের জন্য একটি UICollectionView প্রয়োজন।
আপনার Scenedelegate.swift ফাইলটি খুলুন এবং এর ভিতরে নিম্নলিখিত কোডটি যোগ করুন যা সেশন পদ্ধতিতে সংযুক্ত হবে:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: scene) window?.rootViewController=UINavigationController(rootViewController:HomeVC())
window?.makeKeyAndVisible()
}
যেহেতু আমরা একটি প্রোগ্রামেটিক UI ব্যবহার করছি, তাই প্রথমে আমাদের রুট ভিউ কন্ট্রোলার উল্লেখ করতে হবে - অর্থাৎ, প্রথম স্ক্রীন যা ব্যবহারকারী অ্যাপটি চালু করার সময় প্রদর্শিত হবে।
এখানে এই অ্যাপটিতে আমরা শুধুমাত্র একটি ভিউ কন্ট্রোলার ব্যবহার করছি, তাই আমরা এটিকে একটি UINavigationController-এর ভিতরে মোড়ানো। এটি একটি নেভিগেশন বার প্রদান করে যেখানে আমরা আমাদের UISearchController রাখতে পারি।
HomeVC.swift ফাইল খুলুন এবং নিম্নলিখিত বৈশিষ্ট্য যোগ করুন:
private var SearchBar: UISearchController = {
let sb = UISearchController()
sb.searchBar.placeholder = "Enter the movie name"
sb.searchBar.searchBarStyle = .minimal
return sb
}()
private var MovieCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: UIScreen.main.bounds.width/3 - 10, height: 200)
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.register(MovieCell.self, forCellWithReuseIdentifier: MovieCell.ID)
return cv
}()
প্রথমে আমরা আমাদের UISearchController তৈরি করি এবং এর বৈশিষ্ট্যগুলি যেমন স্থানধারক পাঠ্য এবং শৈলী কনফিগার করি।
তারপরে আমরা একটি UICollectionView তৈরি করি এবং আমাদের সংগ্রহের দৃশ্যটি ব্যবহার করা উচিত এমন বিন্যাসের ধরণটি নির্দিষ্ট করি। এই ক্ষেত্রে এটি হল UICollectionViewFlowLayout এবং অন্যান্য বৈশিষ্ট্য যেমন স্ক্রোলের দিকনির্দেশ, আইটেমের আকার এবং একটি কাস্টম কালেকশনভিউ সেল ক্লাস নির্দিষ্ট করা যা আমরা আমাদের প্রকল্পে পরে তৈরি করব৷
HomeVC ক্লাসের ভিতরে একটি নতুন ফাংশন তৈরি করুন এবং আমাদের UICollectionView-এর জন্য প্রোগ্রাম্যাটিকভাবে স্বয়ংক্রিয়-লেআউট সীমাবদ্ধতাগুলি কনফিগার করতে নিম্নলিখিত কোড যোগ করুন:
//MARK: - HELPERS
func configureUI(){
MovieCollectionView.translatesAutoresizingMaskIntoConstraints = false
MovieCollectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
MovieCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
MovieCollectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
MovieCollectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
প্রথমে আমরা বলি যে আমাদের অটো রিসাইজিং মাস্ককে সীমাবদ্ধতায় রূপান্তর করতে হবে না। তারপর আমরা আমাদের ভিউ কন্ট্রোলারের চার দিকে আমাদের সংগ্রহের দৃশ্যকে পিন করি।
viewDidLoad()
এর ভিতরে পদ্ধতি কোডের নিম্নলিখিত লাইন যোগ করুন:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Movie Search"
view.backgroundColor = .systemBackground
SearchBar.searchResultsUpdater = self
navigationItem.searchController = SearchBar
view.addSubview(MovieCollectionView)
MovieCollectionView.delegate = self
MovieCollectionView.dataSource = self
configureUI()
}
এখানে আমরা প্রথমে আমাদের ViewController-এর শিরোনাম উল্লেখ করি এবং তারপরে ব্যাকগ্রাউন্ড কালার যা সিস্টেম ব্যাকগ্রাউন্ড কালার। ডিভাইসটি হালকা মোডে থাকলে এটি একটি সাদা ব্যাকগ্রাউন্ড দেখায়। যদি এটি অন্ধকার মোডে থাকে তবে এটি একটি অন্ধকার পটভূমি দেখায়।
তারপরে আমরা সার্চ রেজাল্ট আপডেটার হিসাবে বর্তমান ভিউ কন্ট্রোলার সেট করি, তারপরে নেভিগেশন বারে আমাদের সার্চ কন্ট্রোলার যোগ করি, ভিউ কন্ট্রোলারে UICollectionView যোগ করি এবং ডেলিগেট এবং ডেটাসোর্স সেটআপ করি। অবশেষে আমরা অটো-লেআউট ব্যবহার করে UICollectionView পিন করি।
HomeVC-এর জন্য একটি এক্সটেনশন তৈরি করুন এবং UISearchResultsUpdating প্রোটোকল এবং এর stub পদ্ধতি আপডেটSearchResults প্রয়োগ করুন।
extension HomeVC: UISearchResultsUpdating{
func updateSearchResults(for searchController: UISearchController) {
guard let query = searchController.searchBar.text else{return}
}
}
}
updateSearchResults()
যখনই সার্চ বারে লেখা টেক্সট পরিবর্তিত হয় বা ব্যবহারকারী যখন তাদের কীবোর্ডে সার্চ বোতামে ট্যাপ করে তখন পদ্ধতিটি বলা হবে।
পরবর্তীতে আমাদের সেই কাস্টম UICollectionView সেল তৈরি করতে হবে। MovieCell.swift ফাইলের ভিতরে, নিম্নলিখিত কোড যোগ করুন:
import Foundation
import UIKit
import SDWebImage
class MovieCell: UICollectionViewCell{
static let ID = "MovieCell"
private var MoviePosterImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
// imageView.image = UIImage(systemName: "house")
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(MoviePosterImageView)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension MovieCell{
func configureUI(){
MoviePosterImageView.translatesAutoresizingMaskIntoConstraints = false
MoviePosterImageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
MoviePosterImageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
MoviePosterImageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
MoviePosterImageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
}
func updateCell(posterURL: String?){
if let posterURL = posterURL {
guard let CompleteURL = URL(string: "https://image.tmdb.org/t/p/w500/\(posterURL)") else {return}
self.MoviePosterImageView.sd_setImage(with: CompleteURL)
}
}
}
এখানে আমরা UICollectionView ক্লাসকে সাব-ক্লাস করে এবং init()
বাস্তবায়ন করে আমাদের কাস্টম কালেকশন ভিউ সেল তৈরি করি। ফাংশন
আমরা সিনেমার পোস্টার ইমেজ প্রদর্শন করতে একটি UIImageView তৈরি করি এবং এটির জন্য স্বয়ংক্রিয়-লেআউট সীমাবদ্ধতা সেটআপ করি। তারপরে আমরা একটি ব্যবহারকারী সংজ্ঞায়িত ফাংশন তৈরি করি যা মুভি পোস্টার URL স্ট্রিংকে একটি প্যারামিটার নেয় এবং UI থ্রেড/মেইন থ্রেডকে প্রভাবিত না করেই এটিকে অ্যাসিঙ্ক্রোনাসভাবে ডাউনলোড করে। এটি SD WebImage CocoaPod ব্যবহার করে যা আমরা আগে যোগ করেছি৷
৷কিভাবে আমাদের API সেট আপ করবেন
এগিয়ে যাওয়ার আগে, আপনাকে একটি অ্যাকাউন্ট তৈরি করে TMDB API-এর জন্য আপনার API কী পেতে হবে (এটি বিনামূল্যে)। আমরা API-এর মুভি সার্চ এন্ড পয়েন্ট ব্যবহার করতে যাচ্ছি যা API কী এবং মুভির নামকে প্যারামিটার হিসেবে নেয়।
https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&query=batman
আপনি পোস্টম্যানে এটি চালিয়ে API প্রতিক্রিয়া পরীক্ষা করতে পারেন।
এপিআই প্রতিক্রিয়ার জন্য কীভাবে একটি মডেল তৈরি করবেন
এখন আমরা API থেকে একটি JSON প্রতিক্রিয়া পাই। আমাদের তাদের সুইফটে ডিকোড করতে হবে যা কোডেবল প্রোটোকল প্রয়োগ করে এমন একটি মডেল স্ট্রাকট তৈরি করে আমরা করতে পারি।
আমরা JSON থেকে সুইফট ওয়েবসাইট ব্যবহার করে সহজেই আমাদের JSON প্রতিক্রিয়ার জন্য মডেল স্ট্রাকট তৈরি করতে পারি। এখানে API প্রতিক্রিয়ার জন্য মডেল কোড রয়েছে – আপনি এটিকে Model.swift ফাইলের ভিতরে কপি এবং পেস্ট করতে পারেন:
import Foundation
struct TrendingTitleResponse: Codable {
let results: [Title]
}
struct Title: Codable {
let id: Int
let media_type: String?
let original_name: String?
let original_title: String?
let poster_path: String?
let overview: String?
let vote_count: Int
let release_date: String?
let vote_average: Double
}
struct YoutubeSearchResponse: Codable {
let items: [VideoElement]
}
struct VideoElement: Codable {
let id: IdVideoElement
}
struct IdVideoElement: Codable {
let kind: String
let videoId: String
}
কিভাবে সুইফট ব্যবহার করে HTTP অনুরোধগুলি সম্পাদন করতে হয়
এখন আমাদের HTTP GET অনুরোধগুলি সম্পাদন করার জন্য কিছু সুইফ্ট কোড লিখতে হবে যা API এর JSON প্রতিক্রিয়া প্রদান করে।
Swift একটি URLSession ক্লাস প্রদান করে যা AFNetworking, AlamoFire ইত্যাদির মতো কোনো তৃতীয় পক্ষের লাইব্রেরির প্রয়োজন ছাড়াই নেটওয়ার্কিং কোড লেখা সহজ করে তোলে।
APIService.swift খুলুন এবং নিম্নলিখিত কোড যোগ করুন:
import Foundation
class APIService{
static var shared = APIService()
let session = URLSession(configuration: .default)
func getMovies(for Query: String,completion:@escaping([Title]?,Error?)->Void){
guard let FormatedQuery = Query.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else{return}
guard let SEARCH_URL = URL(string: "https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&query=\(FormatedQuery)") else {print("INVALID")
return}
let task = session.dataTask(with: SEARCH_URL) { data, response, error in
if let error = error {
print(error.localizedDescription)
completion(nil,error)
}
if let data = data {
do{
let decodedData = try JSONDecoder().decode(TrendingTitleResponse.self, from: data)
// print(decodedData)
completion(decodedData.results,nil)
}
catch{
print(error)
}
}
}
task.resume()
}
}
এখানে আমরা সিঙ্গলটন প্যাটার্ন সহ API পরিষেবা নামে একটি ক্লাস তৈরি করেছি তাই আমাদের ক্লাসের স্ট্যাটিক সদস্য হিসাবে এই ক্লাসের জন্য একটি উদাহরণ প্রয়োজন। তারপরে আমরা ডিফল্ট কনফিগারেশন সহ আমাদের নেটওয়ার্কিং টাস্কের জন্য একটি সেশন তৈরি করেছি, একটি ব্যবহারকারীর সংজ্ঞায়িত পদ্ধতি getMovies() অনুসরণ করে।
তারপরে আমরা আমাদের নেটওয়ার্কিং টাস্ক তৈরি করেছি - এই ক্ষেত্রে আমাদের HTTP GET অনুরোধগুলি সম্পাদন করতে হবে যা dataTask()
ব্যবহার করে সম্পাদন করা যেতে পারে URLSession ক্লাসের পদ্ধতি। এটি একটি প্যারামিটার হিসাবে ইউআরএল নেয় এবং একটি সমাপ্তি হ্যান্ডলার দেয় যাতে API থেকে প্রত্যাবর্তিত ডেটা থাকে, কোনো ত্রুটি দেখা দিলে ত্রুটির ডেটা এবং একটি প্রতিক্রিয়া যাতে HTTP প্রতিক্রিয়া তথ্য যেমন স্ট্যাটাস কোড এবং তাদের সংশ্লিষ্ট বার্তা রয়েছে।
যদি কোন ত্রুটি থাকে, তাহলে আমরা ত্রুটির তথ্য দিয়ে এই ফাংশন থেকে পালিয়ে যাই। যদি তা না হয়, তাহলে আমরা আমাদের সুইফ্ট মডেলের উপর ভিত্তি করে আমাদের JSON ডেটা ডিকোড করি এবং ডিকোড করা ডেটা দিয়ে এই ফাংশন থেকে বের হয়ে যাই।
কিভাবে UICollectionView-এ অনুসন্ধান ফলাফল প্রদর্শন করবেন
HomeVC.swift-এ, একটি ব্যক্তিগত সম্পত্তি তৈরি করুন যা টাইটেল অবজেক্টের একটি অ্যারে। এগুলি API দ্বারা প্রত্যাবর্তিত প্রতিটি মুভির তথ্য ধারণ করবে৷
private var Movies = [Title]()
HomeVC.swift-এ HomeVC ক্লাসের জন্য একটি এক্সটেনশন তৈরি করুন এবং UIColletionViewDelegate এবং UICollectionViewDatasource প্রোটোকল প্রয়োগ করুন। তারপর numberOfItemsInSection (যা API দ্বারা প্রত্যাবর্তিত চলচ্চিত্রের সংখ্যার সমান) এবং cellForItemAt (যা প্রকৃতপক্ষে API প্রতিক্রিয়াগুলির সাথে সেলকে পপুলেট করে, যেমন পোস্টার ইমেজ ডাউনলোড এবং সেট করা) প্রয়োগ করুন৷
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Movies.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MovieCell.ID, for: indexPath) as? MovieCell{
// cell.backgroundColor = .systemBackground
cell.updateCell(posterURL: Movies[indexPath.row].poster_path)
return cell
}
return UICollectionViewCell()
}
অবশেষে আমাদের প্রকৃত API কল করতে হবে যা আমরা updateSearchResults()
এর ভিতরে করি প্রতিনিধি পদ্ধতি যা আমরা পূর্বে প্রয়োগ করেছি। সেই পদ্ধতির ভিতরে নিম্নলিখিত কোড যোগ করুন:
func updateSearchResults(for searchController: UISearchController) {
guard let query = searchController.searchBar.text else{return}
APIService.shared.getMovies(for:query.trimmingCharacters(in: .whitespaces)) { titles, error in
if let titles = titles {
self.Movies = titles
DispatchQueue.main.async {
self.MovieCollectionView.reloadData()
}
}
}
}
এখানে, যখনই ব্যবহারকারী সার্চ বারে টাইপ করে বা অনুসন্ধান বোতাম টিপে, আমরা একটি মুভি আনার জন্য একটি HTTP GET অনুরোধ করি (সার্চ বারে প্রবেশ করা নামের উপর ভিত্তি করে)। তারপর আমরা কালেকশন ভিউ রিলোড করি যা মুভির পোস্টারের সাথে সংগ্রহ ভিউ সেল আপডেট করে।
মনে রাখবেন যে আমাদের এটি প্রধান থ্রেড/ইউআই থ্রেডে করতে হবে, কারণ ডিফল্টরূপে iOS স্বয়ংক্রিয়ভাবে ব্যাকগ্রাউন্ড থ্রেডে HTTP অনুরোধ করে। এর মানে হল আমাদের UI উপাদানগুলি আপডেট করতে আমাদের UI/Main থ্রেড ব্যবহার করতে হবে।
ফলাফল দেখতে এখন আপনার অ্যাপটি সিমুলেটরে চালান:
অভিনন্দন! আপনি iOS অ্যাপে UISearchController ব্যবহার করতে শিখেছেন।