web-dev-qa-db-fra.com

UICollectionView pagination horizontale non centrée

J'ai une collection de défilement horizontalVoir avec chaque cellule la taille de la vue. Lorsque je parcourt la collectionView, il ne page pas par cellule. Les cellules ne sont pas au centre de l'écran. J'ai essayé un tas de choses pour essayer de le réparer et je n'ai pas eu de chance. Voici une vidéo du problème: https://www.youtube.com/watch?v=tXsxWelk16w Des idées?

35
mlevi

Supprimez les espaces entre les éléments. Pour une vue de collection à défilement horizontal, définissez un espacement minimal des lignes sur 0. Vous pouvez le faire avec le générateur d'interface ou avec la méthode du protocole UICollectionViewDelegateFlowLayout:

- (CGFloat)collectionView:(UICollectionView *)collectionView 
                   layout:(UICollectionViewLayout *)collectionViewLayout 
        minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return 0;    
}

enter image description here

Une autre façon consiste à réduire la largeur de votre cellule à la largeur de collectionView pour une valeur d'espace horizontal entre les éléments. Ajoutez ensuite des encarts de section avec des encarts gauche et droit qui correspondent à la moitié de l'espace horizontal entre les éléments. Par exemple, votre interligne minimum est de 10:

- (CGFloat)collectionView:(UICollectionView *)collectionView
                   layout:(UICollectionViewLayout *)collectionViewLayout
        minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return 10;
}

- (CGSize)collectionView:(UICollectionView *)collectionView 
                  layout:(UICollectionViewLayout *)collectionViewLayout 
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return CGSizeMake(collectionView.frame.size.width - 10, collectionView.frame.size.height);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView 
                        layout:(UICollectionViewLayout *)collectionViewLayout 
        insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 5, 0, 5);
}

enter image description here

Et troisième façon: manipuler le défilement collectionView dans scrollViewDidEndDecelerating: méthode:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (scrollView == self.collectionView) {
        CGPoint currentCellOffset = self.collectionView.contentOffset;
        currentCellOffset.x += self.collectionView.frame.size.width / 2;
        NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:currentCellOffset];
        [self.collectionView scrollToItemAtIndexPath:indexPath
                                    atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                                            animated:YES];
    }
}

enter image description here

116
Vlad

Démo ici dans Swift 3 : https://github.com/damienromito/CollectionViewCustom

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let pageWidth = Float(itemWidth + itemSpacing)
    let targetXContentOffset = Float(targetContentOffset.pointee.x)
    let contentWidth = Float(collectionView!.contentSize.width  )
    var newPage = Float(self.pageControl.currentPage)

    if velocity.x == 0 {
        newPage = floor( (targetXContentOffset - Float(pageWidth) / 2) / Float(pageWidth)) + 1.0
    } else {
        newPage = Float(velocity.x > 0 ? self.pageControl.currentPage + 1 : self.pageControl.currentPage - 1)
        if newPage < 0 {
            newPage = 0
        }
        if (newPage > contentWidth / pageWidth) {
            newPage = ceil(contentWidth / pageWidth) - 1.0
        }
    }
    self.pageControl.currentPage = Int(newPage)
    let point = CGPoint (x: CGFloat(newPage * pageWidth), y: targetContentOffset.pointee.y)
    targetContentOffset.pointee = point
}

Swift 4 :

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let pageWidth = Float(itemWidth + itemSpacing)
    let targetXContentOffset = Float(targetContentOffset.pointee.x)
    let contentWidth = Float(collectionView!.contentSize.width  )
    var newPage = Float(self.pageControl.currentPage)

    if velocity.x == 0 {
        newPage = floor( (targetXContentOffset - Float(pageWidth) / 2) / Float(pageWidth)) + 1.0
    } else {
        newPage = Float(velocity.x > 0 ? self.pageControl.currentPage + 1 : self.pageControl.currentPage - 1)
        if newPage < 0 {
            newPage = 0
        }
        if (newPage > contentWidth / pageWidth) {
            newPage = ceil(contentWidth / pageWidth) - 1.0
        }
    }

    self.pageControl.currentPage = Int(newPage)
    let point = CGPoint (x: CGFloat(newPage * pageWidth), y: targetContentOffset.pointee.y)
    targetContentOffset.pointee = point
}
31
Paolo Musolino

Version rapide de @ vlad-che réponse acceptée:

extension GoodsViewController: UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {

        return 10
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let frameSize = collectionView.frame.size
        return CGSize(width: frameSize.width - 10, height: frameSize.height)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

        return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    }
}
12
Aleksey Shevchenko

Solution Swift 4 pour supprimer l'espacement des lignes pour garder les cellules centrées:

public func collectionView(_ collectionView: UICollectionView, layout 
collectionViewLayout: UICollectionViewLayout, 
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}
4
TejAces

La possibilité d'avoir des cellules plus petites dans le cadre collectionView avec un espace entre les cellules permet d'indiquer à l'utilisateur qu'il y a d'autres cellules de chaque côté pour faire défiler, ce qui est une grande victoire pour UX. Mais le centrage des pages ne fonctionne pas comme prévu, chaque cellule devenant progressivement plus décalée au fur et à mesure du défilement de l'utilisateur. J'ai trouvé que ce qui suit fonctionnait bien. L'animation de centrage/accrochage sur chaque cellule est presque invisible pour l'utilisateur car elle ne fait que peaufiner où le défilement collectionView se terminerait naturellement plutôt que de secouer le collectionView pour défiler rapidement vers une autre indexPath. Il est toujours important que la propriété sectionInset soit suffisamment grande pour permettre à la cellule de ne pas coller aux bords du cadre contenant. De plus, comme il y a des espaces entre les cellules, la cible pourrait atterrir sur un indexPath de nil, ce qui ferait défiler le collectionView jusqu'au début. J'ai corrigé un peu ce décalage, puis réessayé, mais différentes approches pourraient être adoptées ici.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                 withVelocity:(CGPoint)velocity
          targetContentOffset:(inout CGPoint *)targetContentOffset
{
    //Ensure the scrollview is the collectionview we care about
    if (scrollView == self.collectionView) {

        // Find cell closest to the frame centre with reference from the targetContentOffset.
        CGPoint frameCentre = self.collectionView.center;
        CGPoint targetOffsetToCentre = CGPointMake((* targetContentOffset).x + frameCentre.x, (* targetContentOffset).y + frameCentre.y);

        NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:targetOffsetToCentre];

            //Check for "edgecase" that the target will land between cells and then find a close neighbour to prevent scrolling to index {0,0}.
        while (!indexPath) {
            targetOffsetToCentre.x += ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).minimumInteritemSpacing;
            indexPath = [self.collectionView indexPathForItemAtPoint:targetOffsetToCentre];
        }

        // Find the centre of the target cell
        CGPoint centreCellPoint = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath].center;

        // Calculate the desired scrollview offset with reference to desired target cell centre.
        CGPoint desiredOffset = CGPointMake(centreCellPoint.x - frameCentre.x, centreCellPoint.y - frameCentre.y);
        *targetContentOffset = desiredOffset;
    }
}
3
Dallas Johnson

Swift 3

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if scrollView == self.collectionView {
            var currentCellOffset = self.collectionView.contentOffset
            currentCellOffset.x += self.collectionView.frame.width / 2
            if let indexPath = self.collectionView.indexPathForItem(at: currentCellOffset) {
              self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
            }
        }
    }
2
Umesh Verma

Le code que je viens de voir de Apple Guides officiels et exemple de code:

AssetViewController.Swift:

self.collectionView?.isPagingEnabled = true
self.collectionView?.frame = view.frame.insetBy(dx: -20.0, dy: 0.0)
1
lastcc

Après avoir rencontré un problème similaire, j'ai corrigé le mien en réalisant que lors de l'utilisation du défilement horizontal, la hauteur est maintenant la largeur et la largeur est maintenant la hauteur car la valeur par défaut est définie pour le défilement vertical. Essayez de changer les valeurs et voyez si cela aide. https://developer.Apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html

0
user6520705

Swift 3.0 définissez votre propre UICollectionViewFlowLayout

let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let width = UIScreen.main.bounds.width
layout.itemSize = CGSize(width: width, height: 154)
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.scrollDirection = .horizontal
collectionView?.collectionViewLayout = layout
0
santos