1

I am working to a brief which involves building a horizontally scrolling view which contains a number of workouts and exercises.

I have gone down the route of using a UICollectionView within a UIView as this sounded like it would suit my needs.

I have got as far as having a horizontally scrolled view which shows my custom cell - my issue is that when the page scrolls it seems to under-scroll - ie it will scroll less than the page width - so if I have 6 pages by the time I get midway through my view is out of sync (off center) -

enter image description here

my second problem is that although my view scrolls horizontally - I cant scroll vertically to see all cells. - how can I get this to work?

My code is as follows -

WO_SessionVC.H

#import <UIKit/UIKit.h>
#import "BG_Blur_ViewController.h"

@interface WO_SessionVC : BG_Blur_ViewController <UICollectionViewDelegate,
UICollectionViewDataSource,
UICollectionViewDelegateFlowLayout>
@property (weak, nonatomic) IBOutlet UICollectionView *colView;
@property (weak, nonatomic) IBOutlet UIPageControl *pager;
@end

WO_SessionVC.M

#import "WO_SessionVC.h"
#import "WOColView.h"
@interface WO_SessionVC ()
@end

@implementation WO_SessionVC

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
    // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
     [super viewDidLoad];
    // Do any additional setup after loading the view.
    _pager.numberOfPages = 9;
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (NSInteger)collectionView:(UICollectionView *)collectionView
 numberOfItemsInSection:(NSInteger)section
 {
     return 9;
 }

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
 {
    CGFloat pageWidth = _colView.frame.size.width;
    _pager.currentPage = _colView.contentOffset.x / pageWidth;
 }

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
              cellForItemAtIndexPath:(NSIndexPath *)indexPath
 {
     // Dequeue a prototype cell and set the label to indicate the page
    WOColView *cell = [collectionView
                   dequeueReusableCellWithReuseIdentifier:@"CellWO"
                   forIndexPath:indexPath
                   ];

    return cell;
}

Storyboard settings screenshot -

enter image description here

Vincent Guerci
  • 14,094
  • 4
  • 47
  • 55
Dancer
  • 15,237
  • 35
  • 119
  • 194

2 Answers2

2

Your UICollectionView is using the default 'flow' layout, which only supports scrolling in a single direction - you can't use the flow layout and scroll in both the horizontal and the vertical.

To get scrolling in both directions working you'll need to change your layout. Luckily, there's a number of existing StackOverflow questions along those lines to get you going:

Scrolling horizontal and vertical both direction

UICollectionView scrolling in both directions

Community
  • 1
  • 1
lxt
  • 30,690
  • 5
  • 76
  • 83
0

For your first problem:

Your pages size are a bit bigger than your scrollview contentsize.width. UISCrollView (UICollectionView is a UIScrollView) scrolls pages of whole contentsize.width. In your case whole width of iPhone screen. Your page size is bigger than that, it's the phone screen size + iteritem spacing.

For that kind of pagination, you need to disable native UIScrollView pagination ('Paging Enabled' in storyboard) and tell the scrollview where to stop scrolling programatically. There is a method for that in scrollviewDelegate and layout object in collectionView. I suggest using a custom layout object inheriting from UICollectionViewFlowLayout. I have done that before in a custom layout object as here:

- (CGFloat)pageWidth {
    return self.itemSize.width + self.minimumLineSpacing;
}

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {

    if (self.paginationEnabled) {
        CGFloat rawPageValue = self.collectionView.contentOffset.x / self.pageWidth;
        CGFloat currentPage = (velocity.x > 0.0) ? floor(rawPageValue) : ceil(rawPageValue);
        CGFloat nextPage = (velocity.x > 0.0) ? ceil(rawPageValue) : floor(rawPageValue);

        BOOL pannedLessThanAPage = fabs(1 + currentPage - rawPageValue) > 0.5;
        BOOL flicked = fabs(velocity.x) > [self flickVelocity];
        if (pannedLessThanAPage && flicked) {
            proposedContentOffset.x = nextPage * self.pageWidth;
        } else {
            proposedContentOffset.x = round(rawPageValue) * self.pageWidth;
        }

        return proposedContentOffset;
    }

    return proposedContentOffset;
}

- (CGFloat)flickVelocity {
    return 0.3;
}

Please note this has been asked before in StackOverflow: targetContentOffsetForProposedContentOffset:withScrollingVelocity without subclassing UICollectionViewFlowLayout

but the solution is flawed, because it doesn't handle flicks well. My solution does.

Community
  • 1
  • 1
DarthMike
  • 3,246
  • 1
  • 19
  • 18