Paging in Collection View not centering properly - ios

I have a UICollectionView with paging enabled and the individual views that are moving horizontally are not getting centered properly.
I have made sure that the views are the same width as the screen.
Any pointers on how to force the UICollectionView to page the views horizontally?

You have to make sure that your section inset + line spacing + cell width all equals exactly the bounds of the CollectionView. I use the following method on a custom UICollectionViewFlowLayout sublcass:
- (void)adjustSpacingForBounds:(CGRect)newBounds {
NSInteger count = newBounds.size.width / self.itemSize.width - 1;
CGFloat spacing = (newBounds.size.width - (self.itemSize.width * count)) / count;
self.minimumLineSpacing = spacing;
UIEdgeInsets insets = self.sectionInset;
insets.left = spacing/2.0f;
self.sectionInset = insets;
}
You have to remember that UICollectionView is just a sublcass of UIScrollView so it doesn't know how you want your pagination to work. Before UICollectionView you would have to handle all of this math when laying out your subviews of the UIScrollView.

Override the method:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
*targetContentOffset = scrollView.contentOffset; // set acceleration to 0.0
float pageWidth = (float)self.articlesCollectionView.bounds.size.width;
int minSpace = 10;
int cellToSwipe = (scrollView.contentOffset.x)/(pageWidth + minSpace) + 0.5; // cell width + min spacing for lines
if (cellToSwipe < 0) {
cellToSwipe = 0;
} else if (cellToSwipe >= self.articles.count) {
cellToSwipe = self.articles.count - 1;
}
[self.articlesCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:cellToSwipe inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}

Related

How to make a view gone on scrolling in ios

I have a view which i want to scroll up on my tableview scrolling.
this is the view which will scroll up whenever i am scrolling my tableview. but this is what happens when i do that:
As u can see there is a black space in between my targeted view and the tableview.
on my scrollViewDidScroll i did this:
CGPoint offset = scrollView.contentOffset;
self.counter = 0;
CGRect bounds = scrollView.bounds;
CGSize size = scrollView.contentSize;
UIEdgeInsets inset = scrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
float reload_distance = 10;
float scrollViewHeight = scrollView.frame.size.height;
float scrollContentSizeHeight = scrollView.contentSize.height;
float scrollOffset = scrollView.contentOffset.y;
self.tableData.contentInset = UIEdgeInsetsZero;
if (scrollOffset == 0)
{
[self.topView removeFromSuperview];
[[self navigationController] setNavigationBarHidden:NO animated:YES];
[self changeHeight:48];
self.parentOfPhotoview.frame =CGRectMake(0, 0, self.parentOfPhotoview.frame.size.width, self.heightConstraint.constant);
}
else if(scrollOffset>2)
{
//[[self navigationController] setNavigationBarHidden:YES animated:YES];
//[self.view addSubview:_topView];
if(self.heightConstraint.constant<2)
{
[self changeHeight:0];
}
else
{
[self changeHeight:48-(scrollView.contentOffset.y)];
self.parentOfPhotoview.frame = CGRectMake(0, -scrollView.contentOffset.y, self.parentOfPhotoview.frame.size.width, self.heightConstraint.constant);
self.parentOfPhotoview.backgroundColor = [UIColor whiteColor];
NSLog(#"%f",self.heightConstraint.constant);
}
}
else if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
{
// then we are at the end
}
scrolloffset 0 indicates top and the heightConstraint is the height constraint of parentOfPhotoview which is the desired view!!
What is i am missing??
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 0;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 0;
}
Try above line of code. May be your problem will be solved.
You should set your table contentInset with the Top having the same height as the navigationBar. The black is when there is no views at all. This way you do not need to worry about changing height of the tableView etc.
However, I would suggest using AutoLayout.
Also if you want to have parentPhotoView to be floating on the top of the table. You set that in the view for header in section.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
And make sure you set the height of header in section to be the same as the parentPhotoView

Want to fire scrollViewDidScroll before go to bottom

I'm using collection view to view my rss feed data. I am using -(void)scrollViewDidScroll:(UIScrollView *)scrollView to detect the view go to bottom & load more rss feeds.
But I want call the scrollViewDidScroll method not in the bottom of my view, but just 2 or 3 index items before the bottom. How can I edit my scrollViewDidScroll method to perform this.
You can do something like this:
-(void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
float reload_distance = -2 * [your item height];
if(y > h + reload_distance) {
[self.delegate loadMoreDataHere];
}
}

Avoid displaying half UICollectionViewCell

As shown in the image, my requirement is to avoid displaying half cell while scrolling....if i scroll more than half a height of cell and released, the cell should be completed scrolled to top..it is somewhat similar to setPagingEnabled concept...I want to implement same concept for each row.. Thanks in advance
Override
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
in UICollectionViewlayout. The return value is the offset you want to use instead of the one the user scrolled to. Assuming your row's height is 100 and the current proposedContentOffset is (0,80) then you would want to return (0,100) which would then scroll your collection view 20 more points. At that point, your previous row's cells won't be visible anymore.
(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGFloat proposedOffset = targetContentOffset->y;
CGFloat heightOfPage =65;
CGFloat heightOfSpacing = 5.0f;
CGFloat numOfPage = lround(proposedOffset / (heightOfPage + heightOfSpacing));
CGFloat newY = numOfPage * (heightOfPage + heightOfSpacing);
// if the calculated y is bigger then the maximum possible y we adjust accordingly
CGFloat contentHeight = _categoryCollectionView.contentSize.height;
CGFloat collectionViewHeight = _categoryCollectionView.bounds.size.height;
CGFloat maxY = contentHeight - collectionViewHeight;
if (newY > maxY)
{
newY = maxY;
}
targetContentOffset->y = (newY);
}

Horizontal UICollectionView with UIRefreshControl

I have a custom horizontal collection view that has 1 row and I want to add pull to refresh functionality which, by default, appears above my row of cells. I would like the user to be able to pull the collection view from left to right to activate the UIRefreshControl. Any ideas?
Thanks in advance.
Basically the response above tells you how to do in Objective-C a load more in a UICollectionView. However, I believe the question was how to do pull to refresh horizontally on that component.
I don't think you can add a UIRefreshControl horizontally but taking into consideration the previous code and making a conversion to Swift I came up with the following one
DON'T FORGET TO SET YOUR UICollectionView bounce property TO TRUE
func scrollViewDidScroll(scrollView: UIScrollView) {
let offset = scrollView.contentOffset
let inset = scrollView.contentInset
let y: CGFloat = offset.x - inset.left
let reload_distance: CGFloat = -75
if y < reload_distance{
scrollView.bounces = false
UIView.animateWithDuration(0.3, animations: { () -> Void in
scrollView.setContentOffset(CGPointMake(0, 0), animated: false)
}, completion: { (Bool) -> Void in
scrollView.bounces = true
})
}
}
Also and in order to avoid issues with the continuous pulling I added some code to remove the bouncing temporarily, animate de scroll back to the right and then enabling the bouncing again. That will give you the same effect as the UIRefreshControl.
Finally, if you want to have a loading icon my suggestion is to add it behind the controller so when you pull you can see it behind
Just adding the Obj-C version of Julio Bailon's answer, which works for pulling the collectionView from its Top i.e. Left to Right
CGPoint offset = scrollView.contentOffset;
CGRect bounds = scrollView.bounds;
CGSize size = scrollView.contentSize;
UIEdgeInsets inset = scrollView.contentInset;
float y = offset.x - inset.left;
float h = size.width;
float reload_distance = -75; //distance for which you want to load more
if(y < reload_distance) {
// write your code getting the more data
NSLog(#"load more rows");
}
For this you need to implement the UIScrollViewDelegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = scrollView.contentOffset;
CGRect bounds = scrollView.bounds;
CGSize size = scrollView.contentSize;
UIEdgeInsets inset = scrollView.contentInset;
float y = offset.x + bounds.size.width - inset.right;
float h = size.width;
float reload_distance = 75; //distance for which you want to load more
if(y > h + reload_distance) {
// write your code getting the more data
NSLog(#"load more rows");
}
}

How to determine margin of a grouped UITableView (or better, how to set it)?

The grouped UITableView places a margin between the edge of the view and the table cells. Annoyingly (for me) this margin is some function of the width of the view.
In my application I have two UITableViews of different widths that I am looking to align the cell edges of.
Is it possible to get this margin? Or better is it possible to set this margin?
cheers,
--Ben
Grouped TableView Width in Relation to Cell Margin
TBWidth (0 to 20)
Left Margin = TBWidth - 10
TBWidth (20 to 400)
Left Margin = 10
TBWidth (401 to 546)
Left Margin = 31
TBWidth (547 to 716)
Left Margin = apx 6% of tableView
TBWidth (717 to 1024)
Left Margin = 45
- (float) groupedCellMarginWithTableWidth:(float)tableViewWidth
{
float marginWidth;
if(tableViewWidth > 20)
{
if(tableViewWidth < 400)
{
marginWidth = 10;
}
else
{
marginWidth = MAX(31, MIN(45, tableViewWidth*0.06));
}
}
else
{
marginWidth = tableViewWidth - 10;
}
return marginWidth;
}
By creating (and using!) a subclass of UITableViewCell, you can possibly achieve what you're looking for. Just move the contentview and backgroundview around in layoutsubviews, as in the code sample below which shifts the visible parts of the cell 20pt to the right.
- (void) layoutSubviews
{
NSLog (#"Cell/contentview/backgroundview before:\n%#\n%#\n%#", self, self.contentView, self.backgroundView);
[super layoutSubviews];
CGRect frame = self.backgroundView.frame;
frame.origin.x += 20;
frame.size.width -= 20;
self.backgroundView.frame = frame;
frame = self.contentView.frame;
frame.origin.x += 20;
frame.size.width -= 20;
self.contentView.frame = frame;
NSLog (#"Cell/contentview/backgroundview after:\n%#\n%#\n%#", self, self.contentView, self.backgroundView);
}
I was using Matthew Thomas implementation but it's unreliable (it's broken if you use autorotation in your controllers) and has a lot of hardcoded code... I realized that there is a very simple solution, my implementation is a single line of code:
- (float)cellMargins
{
return self.backgroundView.frame.origin.x * 2;
}
This is far better IMO :)
EDIT: my implementation was unreliable too (it does not work properly when switching to/from editing mode), this is my final implementation (I handled right and left margins separately):
- (float)leftMargin
{
return self.contentView.frame.origin.x;
}
- (float)rightMargin
{
CGRect frame = self.contentView.frame;
float containerWidth = frame.size.width;
float margin = self.frame.size.width - (containerWidth + frame.origin.x);
return margin;
}
- (float)cellMargins
{
return ([self leftMargin] + [self rightMargin]);
}
Updated Matthew Thomas's code for cell margins calculation.
Created via UITableView's category.
This can be useful, when you need to determine text height in cell, and you dont' know width of that cell.
So, actual cell can be calculated like
cell.width = tableView.width - tableView.cellsMargin * 2;
An here's the code
#implementation UITableView (CellsMargins)
// This is black magic
// from
// http://stackoverflow.com/questions/4708085/how-to-determine-margin-of-a-grouped-uitableview-or-better-how-to-set-it
- (CGFloat)cellsMargin {
// No margins for plain table views
if (self.style == UITableViewStylePlain) {
return 0;
}
// iPhone always have 10 pixels margin
if (! isIPad()) {
return 10;
}
CGFloat tableWidth = self.frame.size.width;
// Really small table
if (tableWidth <= 20) {
return tableWidth - 10;
}
// Average table size
if (tableWidth < 400) {
return 10;
}
// Big tables have complex margin's logic
// Around 6% of table width,
// 31 <= tableWidth * 0.06 <= 45
CGFloat marginWidth = tableWidth * 0.06;
marginWidth = MAX(31, MIN(45, marginWidth));
return marginWidth;
}
#end
This is now accessible through a property of UITableViewCell:
#property(nonatomic) UIEdgeInsets separatorInset
Apple Docs - UITableViewCell - seperatorInset
More accurate left margin for the 'stretchy' zone instead of the 'apx. 6% calc.'
Side note: the iPad implements additional section top and bottom padding in addition to the left margin padding, it's 10.0f below 400.0f table width and 31.0f otherwise.
-(CGFloat)leftMarginForTableView:(UITableView*)tableView
{
if (tableView.style != UITableViewStyleGrouped) return 0;
CGFloat widthTable = tableView.bounds.size.width;
if (isPhone) return (10.0f);
if (widthTable <= 400.0f) return (10.0f);
if (widthTable <= 546.0f) return (31.0f);
if (widthTable >= 720.0f) return (45.0f);
return (31.0f + ceilf((widthTable - 547.0f)/13.0f));
}
I would like to supplement Matthew's answer with a code to set-up fixed margin.
Subclass UITableViewCell.
Implement setFrame:
- (void)setFrame:(CGRect)frame
{
CGFloat inset = 15.0;
CGFloat tableWidth = 400.0;
frame.origin.x -= [self groupedCellMarginWithTableWidth:tableWidth] - inset;
frame.size.width = frame.size.width + 2 * ([self groupedCellMarginWithTableWidth:tableWidth] - inset);
[super setFrame:frame];
}
This will set left and right margin for 15.0 while table width may vary.
Just in case anyone needs this, the function for the section title left margin when obtained from tableView:titleForHeaderInSection: seems to be:
- (float)marginForSectionTitleOnGroupedTableView {
float width = self.width;
if (width <= 400) return 19;
else if (width >= 401 && width < 547) return 40;
else if (width >= 547 && width < 560) return 41;
else if (width >= 560 && width < 573) return 42;
else if (width >= 573 && width < 586) return 43;
else if (width >= 586 && width < 599) return 44;
else if (width >= 599 && width < 612) return 45;
else if (width >= 612 && width < 625) return 46;
else if (width >= 625 && width < 639) return 47;
else if (width >= 639 && width < 652) return 48;
else if (width >= 652 && width < 665) return 49;
else if (width >= 665 && width < 678) return 50;
else if (width >= 678 && width < 691) return 51;
else if (width >= 691 && width < 704) return 52;
else if (width >= 704 && width < 717) return 53;
// if (width >= 717)
return 54;
}
For widths greater than 401, the margin seems to be around 7.5% of the table view width; but when trying to align some other view with the section title, the alignment didn't work as expected; so the verbose approach seems to work better.
I have tried a different approach. But not sure whether it always work. I had a grouped UITableView and needed to make customized Header View. But as you know, when implementing viewForHeader inSection method we can not determine the margins for our created view.
So, what I have done is firstly add a CGRect property to ViewController .m file:
#property(nonatomic)CGRect groupedCellRectangle;
and in UITableViewDelegate:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
static BOOL groupFrameInitialized = NO;
if(!groupFrameInitialized){
groupFrameInitialized = YES;
self.groupedCellRectangle = cell.contentView.frame;
}
}
after then, in my viewForHeader inSection method:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(self.groupedCellRectangle.origin.x, 0, self.groupedCellRectangle.size.width, 28)];
[v setBackgroundColor:[UIColor clearColor]];
UILabel *label = [[UILabel alloc] initWithFrame:v.frame];
label.text = #"KAF";
[v addSubview:label];
return v;
}
the result is as I assumed. groupedCellRectangle had successfully stored CGRect value with margin.
The idea behind this approach was UITableView always call viewForHeader method after willDisplay call.
Hope that helps...

Resources