Skip to content

Instantly share code, notes, and snippets.

@hassanvfx
Created September 30, 2019 04:24
Show Gist options
  • Save hassanvfx/931d007d526516215d27f29712b17a50 to your computer and use it in GitHub Desktop.
Save hassanvfx/931d007d526516215d27f29712b17a50 to your computer and use it in GitHub Desktop.
import UIKit
import Delayed
import RxSwift
import RxCocoa
import RxDataSources
protocol MoviesCollectionViewProtocol:AnyObject {
func didSelect(movie : Movie) //todo pass selected index and/or object
}
class MoviesCollectionView: UIView {
let PADDING : Int = 8
let SEARCHBAR_HEIGHT: Int = 60
let disposeBag = DisposeBag()
var page = 1
var isLoading = false
var lastMoviesCount = 0
var collectionView: UICollectionView!
var refreshControl : UIRefreshControl!
var searchbar : UISearchBar!
var searchString : String? = nil
weak var delegate :MoviesCollectionViewProtocol?
var movies: [Movie] = []
var sections: [MoviesSection] = []
var dataSource : RxCollectionViewSectionedAnimatedDataSource<MoviesSection>!
var dataSubject : PublishSubject<[MoviesSection]>!
func dataFiltered()->[MoviesSection] {
let sectionTitle = "Trending"
let uniqueMovies = uniq(movies)
guard let searchString = searchString else {
return [MoviesSection(header: sectionTitle, items: uniqueMovies)]
}
guard searchString.count > 2 else{
return [MoviesSection(header: sectionTitle, items: uniqueMovies)]
}
let string = searchString.lowercased()
let moviesFiltered = uniqueMovies.filter({ (movie) -> Bool in
return movie.title.lowercased().contains(string) || movie.description.lowercased().contains(string)
})
return [MoviesSection(header: sectionTitle, items: moviesFiltered)]
}
}
extension MoviesCollectionView{
func setupRx(){
let dataSubject = PublishSubject<[MoviesSection]>()
let dataSource = RxCollectionViewSectionedAnimatedDataSource<MoviesSection>(
configureCell:{ dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withReuseIdentifier: self.cellName(), for: indexPath) as! MovieViewCell
cell.setupWithMovie(item)
return cell
})
dataSource.canMoveItemAtIndexPath = { dataSource, indexPath in
return true
}
dataSubject
.bind(to: collectionView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
self.dataSubject = dataSubject
self.dataSource = dataSource
}
}
extension MoviesCollectionView : UISearchBarDelegate{
func setupSearch(){
let searchBar = UISearchBar(frame: .zero)
searchBar.searchBarStyle = .prominent
searchBar.tintColor = .white
searchBar.barTintColor = .white
searchBar.delegate = self
searchBar.placeholder = "Filter..."
searchBar.isTranslucent = true
searchBar.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.75)
searchBar.backgroundImage = UIImage()
if let txfSearchField = searchBar .value(forKey: "_searchField") as? UITextField {
txfSearchField.backgroundColor = .clear
}
addSubview(searchBar)
searchBar.snp_makeConstraints { (make) in
make.top.equalTo(snp_topMargin)
make.bottom.lessThanOrEqualTo(snp_topMargin).offset(SEARCHBAR_HEIGHT)
make.left.equalTo(0)
make.width.equalToSuperview()
}
self.searchbar = searchBar
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText == "" {
cancelSearching()
endEditing(true)
}
syncCollectionView()
scrollToTop()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
endEditing(true)
}
func cancelSearching(){
DispatchQueue.main.async {
self.searchbar.resignFirstResponder()
self.searchbar.text = ""
}
}
}
extension MoviesCollectionView {
func setup(){
setupCollectionView()
setupRx()
setupPullToRefresh()
setupSearch()
}
func setupCollectionView(){
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = .clear
collectionView.rx.setDelegate(self).disposed(by: disposeBag)
collectionView.register(MovieViewCell.self, forCellWithReuseIdentifier: cellName())
let top = CGFloat(SEARCHBAR_HEIGHT + PADDING)
collectionView.contentInset = UIEdgeInsets(top: top , left: CGFloat(0), bottom: CGFloat(0), right: CGFloat(0))
addSubview(collectionView)
collectionView.snp_makeConstraints { (make) in
make.edges.equalToSuperview()
make.center.equalToSuperview()
}
self.collectionView = collectionView
}
func setupPullToRefresh(){
refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action:
#selector(self.handleRefresh),for: .valueChanged)
collectionView.addSubview(refreshControl)
}
}
extension MoviesCollectionView {
@objc func handleRefresh(_ refreshControl: UIRefreshControl) {
loadMovies()
}
@objc func loadMovies(){
guard !isLoading else {
return
}
isLoading = true
page+=1
Services.api.getMoviesList(from : page) { [weak self] (movies) in
self?.isLoading = false
self?.refreshControl.endRefreshing()
self?.movies.append(contentsOf: movies)
self?.syncCollectionView()
}
}
@objc func isInfiniteFeed()->Bool{
return true
}
@objc func cellName()->String{
return "MovieCell"
}
func syncCollectionView() {
Kron.debounceLast(timeOut: 0.25, resetKey: self) { (key, ctx) in
self._syncCollectionView()
}
}
func _syncCollectionView(){
searchString = searchbar.text
dataSubject.onNext(dataFiltered())
}
func scrollToTop(){
if dataFiltered()[0].items.count > 1 {
collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .top, animated: false)
}
}
}
extension MoviesCollectionView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let movie = dataSource[indexPath]
delegate?.didSelect(movie: movie)
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if isInfiniteFeed() && indexPath.row == movies.count - 2 {
loadMovies()
}
}
}
extension MoviesCollectionView: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = Int(collectionView.bounds.size.width) - ( PADDING * 2 )
let height = Services.theme.CELLS_HEIGHT
return CGSize( width: width , height: height )
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat(PADDING)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets.init(top: 8, left: 8, bottom: 8, right: 8)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment