Je dois afficher 3 éléments dans une UICollectionView, avec la pagination activée comme ceci
mais je deviens comme ça
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);
}
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
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)
}
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)
}
}
}
@ 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 }
}
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) ;