UICollectionView align logic missing in horizontal paging scrollview

前端 未结 16 1165
天命终不由人
天命终不由人 2020-12-07 06:49

I\'ve got a UICollectionView, which works ok, until I start scrolling. Here some pics first: \"enter

相关标签:
16条回答
  • 2020-12-07 07:29

    This is the same problem that I was experiencing and i posted my solution on another post, so I'll post it again here.

    I found a solution to it, and it involved subclassing the UICollectionViewFlowLayout.

    My CollectionViewCell size is 302 X 457 and i set my minimum line spacing to be 18 (9pix for each cell)

    When you extend from that class there are a few methods that need to be over-ridden. One of them is

    • (CGSize)collectionViewContentSize

    In this method, I needed to add up the total width of what was in the UICollectionView. That includes the ([datasource count] * widthOfCollectionViewCell) + ([datasource count] * 18)

    Here is my custom UICollectionViewFlowLayout methods....

    -(id)init
    {
        if((self = [super init])){
    
           self.itemSize = CGSizeMake(302, 457);
           self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
           self.minimumInteritemSpacing = 0.0f;
           self.minimumLineSpacing = 18.0f;
           [self setScrollDirection:UICollectionViewScrollDirectionHorizontal];
       }
        return self;
    }
    
    
    
    -(CGSize)collectionViewContentSize{
       return CGSizeMake((numCellsCount * 302)+(numCellsCount * 18), 457);
    }
    

    This worked for me, so I hope someone else finds it useful!

    0 讨论(0)
  • 2020-12-07 07:30

    This answer is way late, but I have just been playing with this problem and found that the cause of the drift is the line spacing. If you want the UICollectionView/FlowLayout to page at exact multiples of your cells width, you must set:

    UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout;
    flowLayout.minimumLineSpacing = 0.0;
    

    You wouldn't think the line spacing comes into play in horizontal scrolling, but apparently it does.

    In my case I was experimenting with paging left to right, one cell at a time, with no space between cells. Every turn of the page introduced a tiny bit of drift from the desired position, and it seemed to accumulate linearly. ~10.0 pts per turn. I realized 10.0 is the default value of minimumLineSpacing in the flow layout. When I set it to 0.0, no drift, when I set it to half the bounds width, each page drifted an extra half of the bounds.

    Changing the minimumInteritemSpacing had no effect.

    edit -- from the documentation for UICollectionViewFlowLayout:

    @property (nonatomic) CGFloat minimumLineSpacing;

    Discussion

    ...

    For a vertically scrolling grid, this value represents the minimum spacing between successive rows. For a horizontally scrolling grid, this value represents the minimum spacing between successive columns. This spacing is not applied to the space between the header and the first line or between the last line and the footer.

    The default value of this property is 10.0.

    0 讨论(0)
  • 2020-12-07 07:30

    Do you roll your own UICollectionViewFlowLayout?

    If so, adding -(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity will help you to calculate where the scrollview should stop.

    This might work (NB: UNTESTED!):

    -(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
                                 withScrollingVelocity:(CGPoint)velocity
    {
      CGFloat offsetAdjustment = MAXFLOAT;
      CGFloat targetX = proposedContentOffset.x + self.minimumInteritemSpacing + self.sectionInset.left;
    
      CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
    
      NSArray *array = [super layoutAttributesForElementsInRect:targetRect];
      for(UICollectionViewLayoutAttributes *layoutAttributes in array) {
    
        if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
          CGFloat itemX = layoutAttributes.frame.origin.x;
    
          if (ABS(itemX - targetX) < ABS(offsetAdjustment)) {
            offsetAdjustment = itemX - targetX;
          }
        }
      }
    
      return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
    }
    
    0 讨论(0)
  • 2020-12-07 07:31

    My answer is based on answer https://stackoverflow.com/a/27242179/440168 but is more simple.

    enter image description here

    You should place UIScrollView above UICollectionView and give them equal sizes:

    @property (nonatomic, weak) IBOutlet UICollectionView *collectionView;
    @property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
    

    Then configure contentInset of collection view, for example:

    CGFloat inset = self.view.bounds.size.width*2/9;
    self.collectionView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset);
    

    And contentSize of scroll view:

    self.scrollView.contentSize = CGSizeMake(self.placesCollectionView.bounds.size.width*[self.collectionView numberOfItemsInSection:0],0);
    

    Do not forget to set delegate of scroll view:

    self.scrollView.delegate = self;
    

    And implement main magic:

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        if (scrollView == self.scrollView) {
            CGFloat inset = self.view.bounds.size.width*2/9;
            CGFloat scale = (self.placesCollectionView.bounds.size.width-2*inset)/scrollView.bounds.size.width;
            self.collectionView.contentOffset = CGPointMake(scrollView.contentOffset.x*scale - inset, 0);
        }
    }
    
    0 讨论(0)
  • 2020-12-07 07:33

    You could disable paging on UICollectionView and implement a custom horizontal scrolling/paging mechanism with a custom page width/offset like this:

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
    {
        float pageWidth = 210;
    
        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, 0) animated:YES];
    }
    
    0 讨论(0)
  • 2020-12-07 07:33

    your UICollectionView's width should be an exact multiplication of the cell size width + the left and right insets. In your example, if the cell width is 96, then the UICollectionView's width should be (96 + 5 + 5) * 3 = 318. Or, if you wish to keep UICollectionView's 320 width, your cell size width should be 320 / 3 - 5 - 5 = 96.666.

    If this does not help, your UICollectionView's width might be different than what is set in the xib file, when the application runs. To check this - add an NSLog statement to printout the view's size in runtime:

    NSLog(@"%@", NSStringFromCGRect(uiContentViewController.view.frame));
    
    0 讨论(0)
提交回复
热议问题