I'm trying to setup what should be a simple horizontal layout using UICollectionView, going around in circles without achieving the desired result so any pointer or examples would be gratefully appreciated.
Probably little point in me pasting code as changed so often without success.
The image shows two rows, the first is a single item and is the correct size and aligned correctly in the centre. The second row has 3 items, the first should be the same size as the item above, with the next item edge being just visible indicating another item. When the item is swiped to the left their should be 3 items visible — the main item fully visible in the centre and items 1 & 3 just visible at the left and right edges.
I have tried setting paging on, changing insetForSectionAtIndex, minimumLineSpacingForSectionAtIndex, minimumInteritemSpacingForSectionAtIndex.
Edit - 16th December
I haven't explained or illustrated this very well, so have better illustrated below. I know how to create the collection view, set the item size etc but can not get the desired layout.
This is a single row horizontal UICollectionView with one item always in full view and its neighbouring items in partial view indicating to the user there are more items to the left or right depending on the scroll position and item count.
when Item 1 is in full view, item 2 is in partial view
when Item 2 is in full view, item 1 and 3 are in partial view (left and right)
when Item 3 is in full view, item 2 is in partial view (left)
Ok, I've done this as follows;
sticking with an item size of 288x180 for my case
1/. Set the item size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(288, 180);
}
2/. Set insetForSectionAtIndex
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
//top, left, bottom, right
return UIEdgeInsetsMake(0, 16, 0, 16);
}
3/. Set minimumInteritemSpacingForSectionAtIndex to ensure spacing
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 5;
}
4/. When creating the cell id did the following;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell* cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([UICollectionViewCell class]) forIndexPath:indexPath];
if (!cell)
{
cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([UICollectionViewCell class]) forIndexPath:indexPath];
cell.contentView.width = 288;
cell.contentView.backgroundColor = [UIColor whiteColor];
}
return cell;
}
5/. To achieve the paging effect correctly ensure collectionView.paging = NO and subclass UICollectionViewFlowLayout to use targetContentOffsetForProposedContentOffset for correctly setting the scrollview offset
Not sure if this is the best way but it has given me the desired result..
Create a collection view and set the height according to the height of the items in YOUR SECOND ROW and then set the scroll direction to Horizontal. Also
Here is an image attached, check the settings of the collection view accordingly.
The following method will set the cell width according to your second row. The first cell would be displayed in 3/4th of the screen and second cell 1/4th part.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake( (self.view.frame.size.width / 4) * 3, "YOUR SECOND ROW ITEM HEIGHT");
}
I have done this for 5 cells on the screen
Attaching the code below.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake( self.view.frame.size.width / 5, 70);
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row > pos)
{
if (indexPath.row - pos == 2) {
pos = pos + 1;
}
[self changeDate];
}
else if (indexPath.row == pos)
{
NSLog(#"Do Nothing");
}
else
{
if (indexPath.row + 2 == pos) {
pos = pos - 1;
}
[self changeDate1];
}
//NSLog(#"%#",[arrDate objectAtIndex:indexPath.row]);
}
-(void)changeDate
{
if (pos<(arrDate.count - 2)) {
pos=pos+1;
[self.tpCollectionView selectItemAtIndexPath:[NSIndexPath indexPathForRow:pos inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
NSLog(#"%#",[arrDate objectAtIndex:pos]);
self.lblMovieName.text =[arrDate objectAtIndex:pos];
}
}
-(void)changeDate1
{
if(pos>2)
{
pos=pos-1;
[self.tpCollectionView selectItemAtIndexPath:[NSIndexPath indexPathForRow:pos inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
NSLog(#"%#",[arrDate objectAtIndex:pos]);
self.lblMovieName.text =[arrDate objectAtIndex:pos];
}
else{
}
}
Related
There's this bug happening when I drag a UICollectionViewCell outside of the frame of the collection: the collection's contentOffset is reset to 0, I suppose to scroll to top, even when dragging the cell over below the collection. The problem is the contentOffset has to be manually put back to where it was before and that's visually delayed.
I've tried locking the scroll while dragging, such as the following
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath {
collectionView.scrollEnabled = NO;
}
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath {
collectionView.scrollEnabled = YES;
}
and it didn't do anything, contentOffset still changes. Also did the following
- (CGPoint)collectionView:(UICollectionView *)collectionView targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset {
if (proposedContentOffset.y > -10.0f) { // Minimum scroll from top is -10
return CGPointMake(proposedContentOffset.x, -10.0f);
}
return proposedContentOffset;
}
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView.contentOffset.y > -10.0f) { // Minimum scroll from top is -10
[collectionView setContentOffset:CGPointMake(collectionView.contentOffset.x, -10.0f)];
}
}
to reset the contentOffset whenever it changes and this works fine, but the contentOffset is changed while dragging and the reset only happens when the user releases the cell, so there's a delay. Can I, somehow, lock the contentOffset while dragging?
I think it's worth mentioning my view structure is currently
UIScrollView
UICollectionView
UICollectionView (the one that has drag-drop enabled)
The parent ScrollView unifies both scrolls of the collections inside, so that could be a problem. When the collection is scrolled to top by the contentOffset, it slightly invades the collection above it.
I realised the project was using the LXReorderableCollectionViewFlowLayout framework for drag and drop on UICollectionViews, so I inspected that source code and found that the method that treats dragging out of the collection is
- (void)handleScroll:(NSTimer *)timer
so I added a little check on case LXScrollingDirectionUp to have a maximum edge offset, that I set as a property of that class, like the following
// distance calculated above: how much to scroll based on drag action
if (distance + contentOffset.y > _maxScrollingEdgeOffset.top) {
distance = 0;
}
And that fixed it!
I've got a UICollectionView, and have a custom UICollectionTableViewCell with a UIImageViewInside.
I've got the spacings as followed:
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// TODO: Select Item
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
// TODO: Deselect item
}
#pragma mark – UICollectionViewDelegateFlowLayout
- (UICollectionViewFlowLayout *)collectionViewFlowLayout
{
UICollectionViewFlowLayout *flowLayout = [UICollectionViewFlowLayout new];
flowLayout.sectionInset = UIEdgeInsetsZero;
flowLayout.minimumInteritemSpacing = 0.0f;
flowLayout.minimumLineSpacing = 0.0f;
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
return flowLayout;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat widthOfTheScreen =self.view.frame.size.width;
CGSize retval = CGSizeMake(widthOfTheScreen , widthOfTheScreen *1.5);
return retval;
}
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsZero;
}
Now, when I run it, it works perfectly for my purpose. It shows the image in the centre of the view (I've got my collectionview under a label), and it shows it almost exactly how I wanted it (don't worry about the shortage of bottom space bit).
The image below shows the first item (without scrolling):
However, when I scroll right, I start to get slight gaps. Also, this gap gets bigger with every scroll (I have the background red so you can see).
This is the first scroll:
This is the second scroll:
And this is the third scroll:
And so on goes the pattern. I've got a feeling that it's something to do with the insets, but not sure how to remove it.
OR is it something to do with some outset? I've never heard of such, but that could also be the problem
Thanks a lot in advance for your advice.
Right off the bat, your UI feels and looks pretty strange. I certainly hope the imageview is not nested inside another scrollview inside the collection view. If you care to put the project up we can run it to see what the issue is.
In my project i have collection ,in which horizontal pagination is enabled, Every thing working fine but, when i scroll,particular cell item not fit entire cell,It show half image of previous cell item and half current Items, i need One item to fit the entire screen ??? Any suggestion are welcome
Here is the code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; {
SingleItemCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"SingleItemCollectionViewCell" forIndexPath:indexPath];
[cell ConfigaureCollectionViewItem:self.totalCellItems[indexPath.row]]; return cell;
}
-(CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize size = CGSizeMake(self.bounds.size.width - 20, self.bounds.size.height - 40);
return size;
}
Perhaps you should be using UIPageViewController in stead of UICollectionView. From your question it seems that you are trying to mimic the behaviour of the former.
How can I set the collection cell view to dynamically stretch to iphone screen width (e.g. iphone 5s, iphone 6 plus)?
I tried:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
(ResultCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellClass
forIndexPath:indexPath];
cell.bounds = CGRectMake(0,0, self.view.bounds.size.width, 150);
return cell;
}
That does not work. I don't see the content get stretch to the right side of the screen.
I have tried adding this delegate method:
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGSize cellSize;
cellSize.width = self.view.bounds.size.width;
// body view height
cellSize.height = 150;
return cellSize;
}
I have set breakpoints in the method, but that method never get called?
I had the same issue with the same use case. I finally ended up doing
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(collectionView.bounds.size.width, 150);
}
This changes the size of each item (cell) of the collection view. Hence you could change the size of each cell using this. Here i need the cell width to be same as of my UICollectionView so i passed the UICollectionview's width and specific height that i desired. Hope this would help.
Also there is no need to set the cell bounds in - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath as the size gets set via - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
Also make sure that you have attached the desired Delegates & DataSources to the UICollectionView
The size of the cells are specified by the layout object of the collection view. Are you using a UICollectionViewFlowLayout? Try either setting the itemSize property of the layout object or implementing the delegate method: collectionView:layout:sizeForItemAtIndexPath:
You will be able to specify the size of the item (cell) relative to the collection view's bounds there.
Keep in mind that your collection view also needs to be set up so that it resizes with your view controller. If you're using a UICollectionViewController that should be set up automatically. Otherwise, you'll have to constrain your collection view to the bounds of its superview.
Update:
You can set the itemSize property of the layout directly on the layout:
self.layout.itemSize = CGSizeMake(CGRectGetWidth(self.collectionView.bounds), 100);
or, (better if you're handling rotation or resizing), implement the appropriate delegate call back:
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(CGRectGetWidth(collectionView.bounds), 100);
}
I'd recommend reading the appropriate UICollectionView docs to get more familiar with how collection views and their layout's work:
UICollectionViewDelegateFlowLayout
UICollectionViewFlowLayout
UICollectionView Programming Guide
I have a UICollectionView with 2 different types of UICollectionViewCells. There is a tab that prompts the showing of each type of cell and only one type of cell can be visible at a time.
For some reason, 1 type of cell aligns left in the UICollectionView, but the other cell horizontally aligns in the center. I'd like both types of cells to align left.
Edge insets are:
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(10,10,10,10);
}
Any help would be much appreciated.
The cells will layout differently depending on their size, so you need to use different insets for each of your cell types. I think it should be doable like this:
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section];
if ([cell isKindOfClass:[MyClass1 class]]){
return UIEdgeInsetsMake(10,10,10,10);
}else{
return UIEdgeInsetsMake(20,10,10,10);
}
Substitute your correct class name for MyClass1 and experiment with the edge insets until you get the behavior you want.