web-dev-qa-db-fra.com

UICollectionView paging horizontal avec 3 éléments

Je dois afficher 3 éléments dans une UICollectionView, avec la pagination activée comme ceci

enter image description here

mais je deviens comme ça enter image description here

J'ai créé un flux personnalisé, plus la pagination est activée mais je ne parviens pas à obtenir ce dont j'ai besoin. Comment puis-je y parvenir ou quel délégué dois-je examiner, ou me diriger vers un lien d'où je peux obtenir de l'aide pour ce scénario.

- (void)awakeFromNib
{
    self.itemSize = CGSizeMake(480, 626);
    self.minimumInteritemSpacing = 112;
    self.minimumLineSpacing = 112;
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.sectionInset = UIEdgeInsetsMake(0, 272, 0, 272);
}
36
Raheel Sadiq

Modifier : Lien de démonstration: https://github.com/raheelsadiq/UICollectionView-horizontal-paging-with-3-items =

Après de nombreuses recherches, je l'ai fait, trouvez le point suivant pour faire défiler et désactiver la pagination. Dans scrollviewWillEndDragging, faites défiler jusqu'à la cellule suivante x.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{

    float pageWidth = 480 + 50; // width + space

    float currentOffset = scrollView.contentOffset.x;
    float targetOffset = targetContentOffset->x;
    float newTargetOffset = 0;

    if (targetOffset > currentOffset)
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
    else
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;

    if (newTargetOffset < 0)
        newTargetOffset = 0;
    else if (newTargetOffset > scrollView.contentSize.width)
        newTargetOffset = scrollView.contentSize.width;

    targetContentOffset->x = currentOffset;
    [scrollView setContentOffset:CGPointMake(newTargetOffset, scrollView.contentOffset.y) animated:YES];
}

J'ai aussi dû faire le gauche et le droit petit et le centre grand, donc je l'ai fait avec transform. Le problème était de trouver l'indice, donc c'était très difficile à trouver.

Pour transformer gauche et droite dans cette même méthode, utilisez le newTargetOffset

int index = newTargetOffset / pageWidth;

if (index == 0) { // If first index 
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index  inSection:0]];

    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = CGAffineTransformIdentity;
    }];
    cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index + 1  inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = TRANSFORM_CELL_VALUE;
    }];
}else{
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = CGAffineTransformIdentity;
    }];

    index --; // left
    cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = TRANSFORM_CELL_VALUE;
    }];

    index ++;
    index ++; // right
    cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = TRANSFORM_CELL_VALUE;
    }];
}

Et dans cellForRowAtIndex, ajoutez

if (indexPath.row == 0 && isfirstTimeTransform) { // make a bool and set YES initially, this check will prevent fist load transform
    isfirstTimeTransform = NO;
}else{
    cell.transform = TRANSFORM_CELL_VALUE; // the new cell will always be transform and without animation 
}

Ajoutez également ces deux macros ou comme vous le souhaitez pour gérer les deux

#define TRANSFORM_CELL_VALUE CGAffineTransformMakeScale(0.8, 0.8)
#define ANIMATION_SPEED 0.2

Le résultat final est

enter image description here

68
Raheel Sadiq

La première partie de @ Raheel Sadiq répond dans Swift, sans Transform.

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let pageWidth: Float = Float(self.collectionView.frame.width / 3) //480 + 50
        // width + space
        let currentOffset: Float = Float(scrollView.contentOffset.x)
        let targetOffset: Float = Float(targetContentOffset.pointee.x)
        var newTargetOffset: Float = 0
        if targetOffset > currentOffset {
            newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth
        }
        else {
            newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth
        }
        if newTargetOffset < 0 {
            newTargetOffset = 0
        }
        else if (newTargetOffset > Float(scrollView.contentSize.width)){
            newTargetOffset = Float(Float(scrollView.contentSize.width))
        }

        targetContentOffset.pointee.x = CGFloat(currentOffset)
        scrollView.setContentOffset(CGPoint(x: CGFloat(newTargetOffset), y: scrollView.contentOffset.y), animated: true)

    }
14
Gent Berani

Swift 3.0 Solution complète basée sur Raheel Sadiq

var isfirstTimeTransform:Bool = true

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {


    let cell : UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCustomViewCell", for: indexPath)

    if (indexPath.row == 0 && isfirstTimeTransform) { 
        isfirstTimeTransform = false
    }else{
        cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 
    }

    return cell
}

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

    return CGSize(width: collectionView.bounds.width/3, height: collectionView.bounds.height)
}

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    // Simulate "Page" Function
    let pageWidth: Float = Float(self.collectionView.frame.width/3 + 20)
    let currentOffset: Float = Float(scrollView.contentOffset.x)
    let targetOffset: Float = Float(targetContentOffset.pointee.x)
    var newTargetOffset: Float = 0
    if targetOffset > currentOffset {
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth
    }
    else {
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth
    }
    if newTargetOffset < 0 {
        newTargetOffset = 0
    }
    else if (newTargetOffset > Float(scrollView.contentSize.width)){
        newTargetOffset = Float(Float(scrollView.contentSize.width))
    }

    targetContentOffset.pointee.x = CGFloat(currentOffset)
    scrollView.setContentOffset(CGPoint(x: CGFloat(newTargetOffset), y: scrollView.contentOffset.y), animated: true)

    // Make Transition Effects for cells
    let duration = 0.2
    var index = newTargetOffset / pageWidth;
    var cell:UICollectionViewCell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0))!
    if (index == 0) { // If first index
        UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
            cell.transform = CGAffineTransform.identity
        }, completion: nil)
        index += 1
        cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0))!
        UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
            cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
        }, completion: nil)
    }else{
        UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
            cell.transform = CGAffineTransform.identity;
        }, completion: nil)

        index -= 1 // left
        if let cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0)) {
            UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8);
            }, completion: nil)
        }

        index += 1
        index += 1 // right
        if let cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0)) {
            UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8);
            }, completion: nil)
        }
    }

}
7
Tomas Cordoba

@ raheel-sadiq est une réponse géniale mais assez difficile à comprendre, je pense. Voici une version très lisible, à mon avis:

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

        //minimumLineSpacing and insetForSection are two constants in my code

        //this cell width is for my case, adapt to yours
        let cellItemWidth = view.frame.width - (insetForSection.left + insetForSection.right)
        let pageWidth = Float(cellItemWidth + minimumLineSpacing)

        let offsetXAfterDragging = Float(scrollView.contentOffset.x)
        let targetOffsetX = Float(targetContentOffset.pointee.x)

        let pagesCountForOffset = pagesCount(forOffset: offsetXAfterDragging, withTargetOffset: targetOffsetX, pageWidth: pageWidth)

        var newTargetOffsetX = pagesCountForOffset * pageWidth

        keepNewTargetInBounds(&newTargetOffsetX, scrollView)

        //ignore target
        targetContentOffset.pointee.x = CGFloat(offsetXAfterDragging)

        let newTargetPoint = CGPoint(x: CGFloat(newTargetOffsetX), y: scrollView.contentOffset.y)
        scrollView.setContentOffset(newTargetPoint, animated: true)

        //if you're using pageControl
        pageControl.currentPage = Int(newTargetOffsetX / pageWidth)

    }

    fileprivate func pagesCount(forOffset offset: Float, withTargetOffset targetOffset: Float, pageWidth: Float) -> Float {
        let isRightDirection = targetOffset > offset
        let roundFunction = isRightDirection ? ceilf : floorf
        let pagesCountForOffset = roundFunction(offset / pageWidth)
        return pagesCountForOffset
    }

    fileprivate func keepNewTargetInBounds(_ newTargetOffsetX: inout Float, _ scrollView: UIScrollView) {
        if newTargetOffsetX < 0 { newTargetOffsetX = 0 }
        let contentSizeWidth = Float(scrollView.contentSize.width)
        if newTargetOffsetX > contentSizeWidth { newTargetOffsetX = contentSizeWidth }
    }
5
Daniel Carlos

vous devrez remplacer targetContentOffsetForProposedContentOffset: withScrollingVelocity: méthode de la disposition du flux. De cette façon, vous prenez le point d'arrêt de la vue de défilement.

-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    CGFloat yOffset = MAXFLOAT;

    CGRect proposedRect;
    proposedRect.Origin = proposedContentOffset;
    proposedRect.size = self.collectionView.bounds.size;
    CGPoint proposedCenterPoint = CGPointMake(CGRectGetMidX(proposedRect), CGRectGetMidY(proposedRect)) ;

    NSArray *array = [super layoutAttributesForElementsInRect:proposedRect];

    for (UICollectionViewLayoutAttributes *attributes in array)
    {
        CGFloat newOffset = attributes.center.y - proposedCenterPoint.y;
        if ( fabsf(newOffset) < fabs(yOffset))
        {
            yOffset = newOffset;
        }
    }

    return CGPointMake(proposedContentOffset.x, proposedContentOffset.y + yOffset);
}

Vous devrez également définir la sectionInset de la disposition de flux pour centrer la première cellule et la dernière cellule. Mon exemple est la hauteur mais facile à changer en largeur.

CGFloat height = (self.collectionView.bounds.size.height / 2.0 ) - (self.itemSize.height / 2.0) ;
self.sectionInset = UIEdgeInsetsMake(height, 30.0, height, 30.0) ;
4
John