Can you please share code snippets or direct me to the xamarin docs, how to implement UITableView, scroll to load more functionality.
How to detect if the tableview has scrolled to the bottom?
Thanks in advance!
inside the TableViewSource class override this method
public override void WillDisplay (UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
if (indexPath.Row == tableItems.Count - 1) {
//Reload your data here
}
}
constructor TableViewSource class
isScrolling = false;
inside the TableViewSource class override this method
public override void Scrolled (UIScrollView scrollView)
{
// load more bottom
float height = scrollView.Frame.Size.Height;
float distanceFromBottom = scrollView.ContentSize.Height - scrollView.ContentOffset.Y;
if(distanceFromBottom < height && isScrolling == false)
{
// reload data here
}
}
Related
I have a collection view that needs to have the cells fitting size to content.
I use sizeForItemAtIndexPath: to set size for each cell.
It's fired and works great.
Here is the problem what I am facing.
The data list for collection view is changed dynamically.
collection view has only 1 item to show and shows it in correct cell size(let's say size1)
next time collection view has 1 item to show again but it's different data.
In this case, the collection view doesn't show it in correct cell size. it shows in size1
I want to show next item in its own size but collection view keep original size.
When item count is changed, sizeForItemAtIndexPath: is fired again.
When item count is same, it's not fired.
I want it to be fired everytime.
I share some of my code.
cllSelected.Source = new SelectedItemSource(this, _selectedItems);
Above code will be called whenever there is new _selectedItems
I implemented the UICollectionViewSource as following
public class SelectedItemSource : UICollectionViewSource, IUICollectionViewDelegateFlowLayout
{
CatalogVC _ownerVC;
SelectedCategoryList _selectedItems;
public SelectedItemSource(CatalogVC ownerVC, SelectedCategoryList selectedItems)
{
_ownerVC = ownerVC;
_selectedItems = selectedItems;
}
public override nint NumberOfSections(UICollectionView collectionView)
{
return 1;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
return _selectedItems != null ? _selectedItems.Count : 0;
}
public override bool ShouldHighlightItem(UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
return true;
}
public override void ItemHighlighted(UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
}
public override void ItemUnhighlighted(UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
_ownerVC.SelectedFilter_ItemClick(indexPath.Row);
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
SelectedFilterCollectionCell cell = collectionView.DequeueReusableCell("SelectedFilterCollectionCell", indexPath) as SelectedFilterCollectionCell;
cell.Layer.ShouldRasterize = true;
cell.Layer.RasterizationScale = UIScreen.MainScreen.Scale;
cell.BindDataToCell(_selectedItems[indexPath.Row]);
return cell;
}
[Export("collectionView:layout:sizeForItemAtIndexPath:")]
public CoreGraphics.CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, Foundation.NSIndexPath indexPath)
{
var text = new NSString(_selectedItems[indexPath.Row].Text);
CGSize cellSize = new CGSize(xxx, yyy); // cellSize will be determined according to text
return cellSize;
}
}
Thanks for any solution or suggestion!
Try to call CollectionView.ReloadData() after the data changes.
I think it will invoke the methods included in the delegate.
I want to add a footer view to a UITableView that shows a UIProgresIndicator when the user has reached the end of the list and new data will be loaded, or a UILabel when there are no more data to be fetched.
I have used the code below, but nothing happens:
UITableViewHeaderFooterView footer = new UITableViewHeaderFooterView ();
UILabel futerlabel = new UILabel ();
futerlabel.Text = "Duke u ngarkuar";
footer.AddSubview (futerlabel);
Any way how to achieve this.
If you are in a UITableViewController try this:
TableView.TableFooterView = footer;
UPDATE
After having a think about this I would suggest not using a footer but rather an extra cell at the end of your data, this will get this effect.
Using this method to check if you have scrolled to the last item and to update the table's data:
public override void WillDisplay (UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
// if showing last row of last section, load more
if (indexPath.Section == tableView.NumberOfSections()-1 && indexPath.Row == tableView.DataSource.RowsInSection(tableView, indexPath.Section)-1 && !FullyLoaded) {
var growRowSource = tableView.DataSource as GrowRowTableDataSource;
if (growRowSource != null) {
FullyLoaded = growRowSource.LoadNextPage ();
Task.Delay (5000);
tableView.ReloadData ();
}
}
}
And then in the Delegate checking for the last item and creating a different cell like so:
public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
{
if (indexPath.Row == LoadedItems.Count) {
var loadingCell = tableView.DequeueReusableCell (LoadingCellID, indexPath) as LoadFooterCell;
loadingCell.Loading = !hasNextPage;
return loadingCell;
}
var cell = tableView.DequeueReusableCell (CellID, indexPath) as GrowRowTableCell;
var item = LoadedItems [indexPath.Row];
// Setup
cell.Image = UIImage.FromFile(item.ImageName);
cell.Title = item.Title;
cell.Description = item.Description;
return cell;
}
bool hasNextPage;
I quickly mocked an example from the GrowTable Xamarin sample code here:
https://github.com/b099l3/LoadingTableExample
You could accomplish this in several ways, I will layout a few here:
In your TableViewSource you can do this:
1.) Implement: public override UIView GetViewForFooter(UITableView tableView, nint section) and return your UILabel
2.) Implement public override nfloat GetHeightForFooter(UITableView tableView, nint section) and return a height for your UIView
If your Footer is going to be a simple UILabel, you can replace Step 1 above with this:
Implement public override string TitleForFooter(UITableView tableView, nint section) and return "Duke u ngarkuar".
You will still need to implement public override nfloat GetHeightForFooter(UITableView tableView, nint section) and return a height otherwise your footer wont show up.
Alternatively, UITableView exposes a property called TableFooterViewthat allows you to set a UIView for a footer.
You can size this to your liking.
var footerView = new UIView(new RectangleF(0, 0, 375, 66));
TableView.TableFooterView = footerView;
I want to create a static table with 7 cells that fill the rest of the screen with taking into account the status bar and nav bar. I override the methods bellow but I am not getting the expected result. What I am missing?
override func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath) -> CGFloat {
let tableHeight = (CGFloat(tableView.bounds.height) - CGFloat(64))/7
return tableHeight }
the picture shows that my 7 cells do no occupy all the screen
try this
override func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath) -> CGFloat {
let tableHigh = (CGFloat(UIScreen.mainScreen.bounds.height) - CGFloat(64))/7
return tableHigh }
This is another good solution:
override func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath) -> CGFloat {
let tableHeight = (tableView.bounds.height - tableView.contentInset.top - tableView.contentInset.bottom) / 7
return tableHeight
}
I would like to set the UITableView to match the height for all the contents in the table view.
This is my storyboard
The problem with this is the top and bottom ImageView is always static on the screen.
The there are suppose to be 10 items on the table view but only 7 shows up due to screen size limitation. I would like to show all 10 before user is able to see the bottom ImageView. (btw, all 3 of the views ie. both the image views and tableview is in a uiscrollview)
IDEAL
Some of the other limitations that i have to work with is that the number of items in the table view is dynamic meaning it can be in any amount of usually less than 10 that i will later retrieve from an api. And the cell height is also dynamic depending on the contents.
I have only just started with some simple code
class ExampleViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var items: [String] = [
"Item 01", "Item 02", "Item 03", "Item 04", "Item 05",
"Item 06", "Item 07", "Item 08", "Item 09", "Item 10"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
}
Subclass your UITableView to override the intrinsicContentSize to be its contentSize, like this:
override var intrinsicContentSize: CGSize {
return contentSize
}
Then use automatic row heights for your table, so your exampleViewController's viewDidLoad would have:
tableView.estimatedRowHeight = 44
And the UITableViewDelegate function:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
When you receive data from your API and reload your table, just call:
tableView.invalidateIntrinsicContentSize()
This will tell your table to resize itself to the same size as its contents (because of the override), and move your bottom image as needed.
If your storyboard throws an error saying that your UIScrollView has an ambiguous height because there's no height constraint on the UITableView, select your UITableView and give it a placeholder intrinsic size in the Size Inspector.
The answers using the subclassing technique are incomplete. You should also override layoutSubviews() like this.
public class DynamicSizeTableView: UITableView
{
override public func layoutSubviews() {
super.layoutSubviews()
if bounds.size != intrinsicContentSize {
invalidateIntrinsicContentSize()
}
}
override public var intrinsicContentSize: CGSize {
return contentSize
}
}
This is what I utilize in production apps:
Swift 5, 2021
import UIKit
class DynamicTableView: UITableView {
/// Will assign automatic dimension to the rowHeight variable
/// Will asign the value of this variable to estimated row height.
var dynamicRowHeight: CGFloat = UITableView.automaticDimension {
didSet {
rowHeight = UITableView.automaticDimension
estimatedRowHeight = dynamicRowHeight
}
}
public override var intrinsicContentSize: CGSize { contentSize }
public override func layoutSubviews() {
super.layoutSubviews()
if !bounds.size.equalTo(intrinsicContentSize) {
invalidateIntrinsicContentSize()
}
}
}
You need to set an IBOutlet to the NSLayoutConstraint that sets the tableView height (first you need create the height constraint with any value, doesn't matter) and then ctrl drag it to your class file
Then in your viewWillAppear you have to calculate the tableView height and set it. Like this:
var tableViewHeight:CGFloat = 0;
for (var i = tableView(self.tableView , numberOfRowsInSection: 0) - 1; i>0; i-=1 ){
tableViewHeight = height + tableView(self.tableView, heightForRowAtIndexPath: NSIndexPath(forRow: i, inSection: 0) )
}
tableViewHeightLayout.constant = tableViewHeight
And that's pretty much it. That will give your scrollView content size and shouldn't raise any warnings.
Update Swift 4
this code working be good
self.scrollView.layoutIfNeeded()
self.view.layoutIfNeeded()
self.tableViewHeightConstraint.constant = CGFloat(self.tableView.contentSize.height)
You probably have to implement the table view intrinsic content size. Please check this answer to see if it helps.
I remember having this problem and even created a custom UITableView subclass.
#import "IntrinsicTableView.h"
#implementation IntrinsicTableView
#pragma mark - UIView
- (CGSize)intrinsicContentSize
{
return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}
#pragma mark - UITableView
- (void)endUpdates
{
[super endUpdates];
[self invalidateIntrinsicContentSize];
}
- (void)reloadData
{
[super reloadData];
[self invalidateIntrinsicContentSize];
}
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[super reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation];
[self invalidateIntrinsicContentSize];
}
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
[super reloadSections:sections withRowAnimation:animation];
[self invalidateIntrinsicContentSize];
}
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:animation];
[self invalidateIntrinsicContentSize];
}
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
[super insertSections:sections withRowAnimation:animation];
[self invalidateIntrinsicContentSize];
}
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[super deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation];
[self invalidateIntrinsicContentSize];
}
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
[super deleteSections:sections withRowAnimation:animation];
[self invalidateIntrinsicContentSize];
}
#end
Update for Swift 5. Adding maxHeight so that you can specify how tall you want your tableView to be
class SelfSizingTableView: UITableView {
var maxHeight = CGFloat.infinity
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
setNeedsLayout()
}
}
override var intrinsicContentSize: CGSize {
let height = min(maxHeight, contentSize.height)
return CGSize(width: contentSize.width, height: height)
}
}
In that case, don't make your bottom cell static, make it a part of table view and insert this bottom image in last row using table view delegate method - insertRowAtIndexPath
In this type of case add your bottom imageView(red) in a table footer view.
To add footer view in UITableView you can use:
tableViewObj.tableFooterView = footerViewObj;
Try this also
in ViewDidLoad
self.table.estimatedRowHeight = 44.0 ;
self.table.rowHeight = UITableViewAutomaticDimension;
Height for row at index path
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;}
Easy way here.
Step 1: Set a height constraint for the table view
Step 2: Control drag the constraint
Step 3: Before you return the count of the rows. In numberOfRowsInSection method, do
tableViewHeightConstraint.constant = tableView.rowHeight * CGFloat(someArray.count)
Of course you can edit the height anchor programmatically, the logic here is to adjust the table view height according to the cell height and cell number.
Based on solution #nikans, written in Xamarin
[Register(nameof(DynamicSizeTableView)), DesignTimeVisible(true)]
public class DynamicSizeTableView : UITableView
{
public override void LayoutSubviews()
{
base.LayoutSubviews();
if (Bounds.Size != IntrinsicContentSize)
InvalidateIntrinsicContentSize();
}
public override CGSize IntrinsicContentSize => ContentSize;
public DynamicSizeTableView(CGRect frame) : base(frame) { }
public DynamicSizeTableView(IntPtr handle) : base(handle) { }
}
Here is the simplest Solution
First Give a height to the tableView.
Create outlet of that height in view Controller. let's say tableViewHeight
Then do this in viewDidLoad or where you populate the data after calling tableView.reloadData()
var height = 0.0
for i in 0..<items.count {
let frame = tableView.rectForRow(at: IndexPath(row: i, section: 0))
height += frame.size.height
}
tableViewHeight.constant = height
This Also Works with tableViews that have dynamic cell heights
Based on solution of #rr1g0
Updated for Swift 5 in 2020, and works with TableViews with sections too.
Create height constraint for tableView and create an outlet to it. And in viewDidLayoutSubviews() use the code below:
var tableViewHeight: CGFloat = 0
for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberOfRows(inSection: section) {
tableViewHeight += tableView(tableView, heightForRowAt: IndexPath(row: row, section: section))
}
}
tableViewHeightConstraint.constant = tableViewHeight
I'm using a UICollectionView with a flow layout to show a list of cells, I also have a page control to indicate current page, but there seems to be no way to get current index path, I know I can get visible cells:
UICollectionView current visible cell index
however there can be more than one visible cells, even if each of my cells occupies full width of the screen, if I scroll it to have two halves of two cells, then they are both visible, so is there a way to get only one current visible cell's index?
Thanks
You can get the current index by monitoring contentOffset in scrollViewDidScroll delegate
it will be something like this
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSInteger currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width;
}
Get page via NSIndexPath from center of view.
Works even your page not equal to width of UICollectionView.
func scrollViewDidScroll(scrollView: UIScrollView) {
let xPoint = scrollView.contentOffset.x + scrollView.frame.width / 2
let yPoint = scrollView.frame.height / 2
let center = CGPoint(x: xPoint, y: yPoint)
if let ip = collectionView.indexPathForItemAtPoint(center) {
self.pageControl.currentPage = ip.row
}
}
Definitely you need catch the visible item when the scroll movement is stopped. Use next code to do it.
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let indexPath = myCollectionView.indexPathsForVisibleItems.first {
myPageControl.currentPage = indexPath.row
}
}
Swift 5.1
The easy way and more safety from nil crash
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if collectionView == newsCollectionView {
if newsPager.currentPage == indexPath.row {
guard let visible = newsCollectionView.visibleCells.first else { return }
guard let index = newsCollectionView.indexPath(for: visible)?.row else { return }
newsPager.currentPage = index
}
}
}
Place PageControl in your view or set by Code.
Set UIScrollViewDelegate
In Collectionview-> cellForItemAtIndexPath (Method) add the below
code for calculate the Number of pages,
int pages = floor(ImageCollectionView.contentSize.width/ImageCollectionView.frame.size.width);
[pageControl setNumberOfPages:pages];
Add the ScrollView Delegate method,
#pragma mark - UIScrollViewDelegate for UIPageControl
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat pageWidth = ImageCollectionView.frame.size.width;
float currentPage = ImageCollectionView.contentOffset.x / pageWidth;
if (0.0f != fmodf(currentPage, 1.0f))
{
pageControl.currentPage = currentPage + 1;
}
else
{
pageControl.currentPage = currentPage;
}
NSLog(#"finishPage: %ld", (long)pageControl.currentPage);
}
I had similar situation where my flow layout was set for UICollectionViewScrollDirectionHorizontal and I was using page control to show the current page.
I achieved it using custom flow layout.
/------------------------
Header file (.h) for custom header
------------------------/
/**
* The customViewFlowLayoutDelegate protocol defines methods that let you coordinate with
*location of cell which is centered.
*/
#protocol CustomViewFlowLayoutDelegate <UICollectionViewDelegateFlowLayout>
/** Informs delegate about location of centered cell in grid.
* Delegate should use this location 'indexPath' information to
* adjust it's conten associated with this cell.
* #param indexpath of cell in collection view which is centered.
*/
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout cellCenteredAtIndexPath:(NSIndexPath *)indexPath;
#end
#interface customViewFlowLayout : UICollectionViewFlowLayout
#property (nonatomic, weak) id<CustomViewFlowLayoutDelegate> delegate;
#end
/------------------- Implementation file (.m) for custom header -------------------/
#implementation customViewFlowLayout
- (void)prepareLayout {
[super prepareLayout];
}
static const CGFloat ACTIVE_DISTANCE = 10.0f; //Distance of given cell from center of visible rect
static const CGFloat ITEM_SIZE = 40.0f; // Width/Height of cell.
- (id)init {
if (self = [super init]) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.minimumInteritemSpacing = 60.0f;
self.sectionInset = UIEdgeInsetsZero;
self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
self.minimumLineSpacing = 0;
}
return self;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
for (UICollectionViewLayoutAttributes *attribute in attributes) {
if (CGRectIntersectsRect(attribute.frame, rect)) {
CGFloat distance = CGRectGetMidX(visibleRect) - attribute.center.x;
// Make sure given cell is center
if (ABS(distance) < ACTIVE_DISTANCE) {
[self.delegate collectionView:self.collectionView layout:self cellCenteredAtIndexPath:attribute.indexPath];
}
}
}
return attributes;
}
Your class containing collection view must conform to protocol 'CustomViewFlowLayoutDelegate' I described earlier in custom layout header file. Like:
#interface MyCollectionViewController () <UICollectionViewDataSource, UICollectionViewDelegate, CustomViewFlowLayoutDelegate>
#property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
#property (strong, nonatomic) IBOutlet UIPageControl *pageControl;
....
....
#end
There are two ways to hook your custom layout to collection view, either in xib OR in code like say in viewDidLoad:
customViewFlowLayout *flowLayout = [[customViewFlowLayout alloc]init];
flowLayout.delegate = self;
self.collectionView.collectionViewLayout = flowLayout;
self.collectionView.pagingEnabled = YES; //Matching your situation probably?
Last thing, in MyCollectionViewController implementation file, implement delegate method of 'CustomViewFlowLayoutDelegate'.
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout cellCenteredAtIndexPath:(NSIndexPath *)indexPath {
self.pageControl.currentPage = indexPath.row;
}
I hope this would be helpful. :)
Note - I have found andykkt's answer useful but since it is in obj-c converted it to swift and also implemented logic in another UIScrollView delegate for a smoother effect.
func updatePageNumber() {
// If not case to `Int` will give an error.
let currentPage = Int(ceil(scrollView.contentOffset.x / scrollView.frame.size.width))
pageControl.currentPage = currentPage
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
// This will be call when you scrolls it manually.
updatePageNumber()
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
// This will be call when you scrolls it programmatically.
updatePageNumber()
}
for swift 4.2
#IBOutlet weak var mPageControl: UIPageControl!
#IBOutlet weak var mCollectionSlider: UICollectionView!
private var _currentIndex = 0
private var T1:Timer!
private var _indexPath:IndexPath = [0,0]
private func _GenerateNextPage(){
self._currentIndex = mCollectionSlider.indexPathForItem(at: CGPoint.init(x: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midX, y: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midY))?.item ?? 0
self.mPageControl.currentPage = self._currentIndex
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
_SetTimer(AutoScrollInterval)
_GenerateNextPage()
}
#objc private func _AutoScroll(){
self._indexPath = IndexPath.init(item: self._currentIndex+1, section: 0)
if !(self._indexPath.item < self.numberOfItems){
_indexPath = [0,0]
}
self.mCollectionSlider.scrollToItem(at: self._indexPath, at: .centeredHorizontally, animated: true)
}
private func _SetTimer(_ interval:TimeInterval){
if T1 == nil{
T1 = Timer.scheduledTimer(timeInterval: interval , target:self , selector: #selector(_AutoScroll), userInfo: nil, repeats: true)
}
}
you can skip the function _SetTimer() , thats for auto scroll
With UICollectionViewDelegate methods
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
pageControl.currentPage = indexPath.row
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if pageControl.currentPage == indexPath.row {
pageControl.currentPage = collectionView.indexPath(for: collectionView.visibleCells.first!)!.row
}
}
Swift 5.0
extension youriewControllerName:UIScrollViewDelegate{
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageWidth = self.collectionView.frame.size.width
pageControl.currentPage = Int(self.collectionView.contentOffset.x / pageWidth)
}
}
(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = _cvImagesList.frame.size.width;
float currentPage = _cvImagesList.contentOffset.x / pageWidth;
_pageControl.currentPage = currentPage + 1;
NSLog(#"finishPage: %ld", (long)_pageControl.currentPage);
}