UIScrollView Tanpa Batas

gambar

Di banyak aplikasi, Anda mungkin menemukan bergulir yang tidak pernah membungkus ke arah yang berlawanan di akhir konten. Teknik ini telah menjadi standar selama bertahun-tahun di banyak platform. Di sisi lain, ada banyak perpustakaan pihak ketiga untuk mendapatkan efek ini. TETAPI Anda tidak membutuhkan perpustakaan pihak ketiga. Teknik ini memiliki logika yang sangat sederhana.

Dukungan halaman UIScrollView memungkinkan pengguna untuk melihat kontennya halaman demi halaman. UIScrollView mengaktifkan efek ini dengan menyesuaikan offset scrollView ketika pengguna selesai menyeret. Ketika pengguna menggulir ke ujung halaman (di sebelah kanan), scrollview membatasi kelebihan isinya dengan menggerakkan offsetnya ke arah yang berlawanan dengan animasi yang indah.

gambar

Kami ingin scrollview tidak membatasi konten offset ketika pengguna ingin melebihi jumlah mereka. Karena itu, kita perlu menambahkan dua halaman lagi ke UIScrollView. Halaman terakhir akan ditambahkan ke indeks nol, dan halaman pertama akan ditambahkan ke indeks (numberOfItems + 1). Kemudian, jika pengguna melihat halaman “numberOfItems”, konten gulir x offset diatur ke 0. Jika pengguna melihat indeks 0, maka offset konten scrollView x akan diatur ke “pageSize * numberOfItems”.

gambar

Hal pertama yang harus dilakukan adalah membuat kelas baru yang diwarisi dari UIView.

gambar

BannerView harus seperti di bawah ini:

import UIKit class BannerView: UIView { override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 

Tidak ada yang aneh di sini. Sekarang kita perlu menambahkan scrollView dan mengatur kode untuk BannerView:

 import UIKit class BannerView: UIView { private let scrollView:UIScrollView = { let sc = UIScrollView(frame: .zero) sc.translatesAutoresizingMaskIntoConstraints = false sc.isPagingEnabled = true return sc }() // BannerView DataSources (1) private var itemAtIndex:((_ bannerView:BannerView , _ index:Int)->(UIView))! private var numberOfItems:Int = 0 override init(frame: CGRect) { super.init(frame: frame) setUpUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setUpUI() { scrollView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height) scrollView.delegate = self self.addSubview(scrollView) scrollView.showsHorizontalScrollIndicator = false } func reloadData(numberOfItems:Int , itemAtIndex:@escaping ((_ bannerView:BannerView , _ index:Int)->(UIView)) ) { self.itemAtIndex = itemAtIndex self.numberOfItems = numberOfItems reloadScrollView() } private func reloadScrollView() { guard self.numberOfItems > 0 else { return } if self.numberOfItems == 1 { let firstItem:UIView = self.itemAtIndex(self , 0) addViewToIndex(view: firstItem, index: 0) scrollView.isScrollEnabled = false return } let firstItem:UIView = self.itemAtIndex(self , 0) addViewToIndex(view: firstItem, index: numberOfItems+1) let lastItem:UIView = self.itemAtIndex(self , numberOfItems-1) addViewToIndex(view: lastItem, index: 0) for index in 0..<self.numberOfItems { let item:UIView = self.itemAtIndex(self , index) addViewToIndex(view: item, index: index+1) } scrollView.contentSize = CGSize(width: CGFloat(numberOfItems+2)*scrollView.frame.size.width, height: scrollView.frame.size.height) scrollView.contentOffset = CGPoint(x: self.scrollView.frame.size.width, y: self.scrollView.contentOffset.y) } private func addViewToIndex(view:UIView, index:Int) { view.translatesAutoresizingMaskIntoConstraints = false scrollView.addSubview(view) view.frame = CGRect(x: CGFloat(index)*scrollView.frame.size.width, y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height) } } 

Saya menggunakan bingkai, bukan tata letak otomatis untuk kesederhanaan. Selain itu, saya menggunakan penutupan bukan delegasi. Ini membantu menghindari kotoran di ViewController. Dengan penutupan, Anda cukup menggunakan bannerView sebagai berikut:

 // ViewController.swift bannerView = BannerView(frame: CGRect(x: 0, y: 64, width: self.view.frame.size.width, height: 200)) self.view.addSubview(bannerView) bannerView.reloadData(numberOfItems: 5) { (bannerView, index) -> (UIView) in let view = UIView() view.backgroundColor = UIColor.red return view } 

Untuk mendelegasikan UIScrollView, saya akan menggunakan scrollViewDidEndDecelerating (_ scrollView: UIScrollView) daripada scrollViewDidScroll (_ scrollView: UIScrollView). Karena kita tidak perlu menghitung posisi swap dengan setiap gerakan scrollView.

 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { let currentPage:Int = Int(scrollView.contentOffset.x / scrollView.frame.size.width) if currentPage == 0 { self.scrollView.contentOffset = CGPoint(x: scrollView.frame.size.width * CGFloat(numberOfItems), y: scrollView.contentOffset.y) } else if currentPage == numberOfItems { self.scrollView.contentOffset = CGPoint(x: 0, y: scrollView.contentOffset.y) } } 

Dan akhirnya, kode kita akan seperti ini untuk BannerView.swift:

 // BannerView.swift import UIKit class BannerView: UIView , UIScrollViewDelegate{ private let scrollView:UIScrollView = { let sc = UIScrollView(frame: .zero) sc.translatesAutoresizingMaskIntoConstraints = false sc.isPagingEnabled = true return sc }() private var itemAtIndex:((_ bannerView:BannerView , _ index:Int)->(UIView))! private var numberOfItems:Int = 0 override init(frame: CGRect) { super.init(frame: frame) setUpUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func reloadData(configuration:BannerViewConfiguration? , numberOfItems:Int , itemAtIndex:@escaping ((_ bannerView:BannerView , _ index:Int)->(UIView)) ) { self.itemAtIndex = itemAtIndex self.numberOfItems = numberOfItems reloadScrollView() } private func reloadScrollView() { guard self.numberOfItems > 0 else { return } if self.numberOfItems == 1 { let firstItem:UIView = self.itemAtIndex(self , 0) addViewToIndex(view: firstItem, index: 0) scrollView.isScrollEnabled = false return } let firstItem:UIView = self.itemAtIndex(self , 0) addViewToIndex(view: firstItem, index: numberOfItems+1) let lastItem:UIView = self.itemAtIndex(self , numberOfItems-1) addViewToIndex(view: lastItem, index: 0) for index in 0..<self.numberOfItems { let item:UIView = self.itemAtIndex(self , index) addViewToIndex(view: item, index: index+1) } scrollView.contentSize = CGSize(width: CGFloat(numberOfItems+2)*scrollView.frame.size.width, height: scrollView.frame.size.height) scrollView.contentOffset = CGPoint(x: self.scrollView.frame.size.width, y: self.scrollView.contentOffset.y) } private func addViewToIndex(view:UIView, index:Int) { view.translatesAutoresizingMaskIntoConstraints = false scrollView.addSubview(view) view.frame = CGRect(x: CGFloat(index)*scrollView.frame.size.width, y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height) } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { let currentPage:Int = Int(scrollView.contentOffset.x / scrollView.frame.size.width) if currentPage == 0 { self.scrollView.contentOffset = CGPoint(x: scrollView.frame.size.width * CGFloat(numberOfItems), y: scrollView.contentOffset.y) } else if currentPage == numberOfItems { self.scrollView.contentOffset = CGPoint(x: 0, y: scrollView.contentOffset.y) } } private func setUpUI() { scrollView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height) scrollView.delegate = self self.addSubview(scrollView) scrollView.showsHorizontalScrollIndicator = false } } 

gambar

Ringkasan


Jadi, kami membuat komponen scrollview yang dapat digunakan kembali dengan sedikit logika. Omong-omong, dengan sejumlah besar data, lebih baik menggunakan UICollectionView, karena memiliki kinerja yang lebih baik dan manajemen memori yang lebih baik daripada UIScrollView. Selain itu, Anda dapat memperluas InfiniteScrollView menggunakan opsi sinkronisasi atau gulir dua arah. Dengan sedikit peningkatan, itu akan menjadi alat yang benar-benar dapat digunakan kembali untuk aplikasi Anda.

→ Kode sumber lengkap dapat ditemukan di GitHub

Source: https://habr.com/ru/post/id453782/


All Articles