Dragging cell outside of UICollectionView frame changes its contentOffset - ios

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!

Related

Unwanted space between UICollectionViewCell

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.

Horizontal collection view has space above cell. How to remove it?

I have collection view with cells I'm presenting horizontally + paging enabled. However I have above cell space that I cant remove (green on screenshot - collection view background). Size of cell is same as containers frame. For better understanding what I mean check screenshot. Any ideas? Thank you very much.
automaticallyAdjustsScrollViewInsets doesn't works for me
[self.calendarView setContentInset:UIEdgeInsetsMake(-64, 0, 0, 0)]; works but I don't know how to get these -64 programatically.
Did you try to implement these methods below and set zero insets and spacings?
You also need to check if the size of the cell is appropriate, may be the way you see the top spacing is the only way layout engine can put the cell. Please, check what size is set as expected
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
return UIEdgeInsetsZero;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 0.0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 0.0;
}
I've came across similar issue many times.
When a UIScrollView or its subclass, e.g. UITableView and UICollectionView, is the first child of viewController.view, it will get a unexpected contentInset.
My solution:
If there are other siblings of UIScrollView, make one of
non-UIScrollView as first child.
If there is no other siblings, add a dummy UIView as the first child, and either hide the UIView or set the height to 0

Horizontal UICollectionView single row layout

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{
}
}

UICollectionViewCell size and rotation

In my UICollectionView I have a cell that should take the entire width of the device. Here is how I set the size for the item:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(self.view.bounds.size.width, 120);
}
I have found that on rotation, the width of the cell does not change. I can resolve this using [collectionView.collectionViewLayout invalidateLayout] in didRotateFromInterfaceOrientation: but this isn't satisfactory. What if the user rotates while in another screen in my app? I will need to add the same to viewWillAppear. What about if the rotation occurs while the app is backgrounded? Now I need to add it for the UIApplicationDidBecomeActiveNotification notification.
What confuses me most is that this is not required for my custom headers in the same collection view. The following supplementary views correctly re-size on orientation change:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return CGSizeMake(self.view.bounds.size.width, 40);
}
Where am I going wrong? What should I do to have the cells re-size automatically to fill the width of the collection view?
A bit late I know but could you solve this by subclassing flow layout and adding?
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return TRUE;
}

Collection View items do not appear when user is on a call

I have a collection view. When I toggle the in-call status bar, my collection view items disappear when the green "you are on a call" status bar is visible. They return when I dismiss the bar.
The log spits out this: "The behavior of the UICollectionViewFlowLayout is not defined because the item height must be less that the height of the UICollectionView minus the section insets top and bottom values."
Has anyone encountered this, and how have you gotten around it? I am using sizeForItemAtIndexPath as follows:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}
Should I be using something else to specify the size of each item?
For anyone else that experiences this. This is the right way to define the item size:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}
You could probably also set itemSize on the collectionViewLayout, but the key to making the items stick around, and not disappear when on a phone call, is to reload the collection view in viewDidLayoutSubviews:
[self.collectionView reloadData];

Resources