2012-11-21 11:12:55 8 Comments
I've got a very simple collectionView in my app (just a single row of square thumbnail images).
I'd like to intercept the scrolling so that the offset always leaves a full image at the left side. At the moment it scrolls to wherever and will leave cut off images.
Anyway, I know I need to use the function
- (CGPoint)targetContentOffsetForProposedContentOffset:withScrollingVelocity
to do this but I'm just using a standard UICollectionViewFlowLayout
. I'm not subclassing it.
Is there any way of intercepting this without subclassing UICollectionViewFlowLayout
?
Thanks
Related Questions
Sponsored Content
11 Answered Questions
[SOLVED] UICollectionView flowLayout not wrapping cells correctly
- 2012-10-17 04:18:06
- lindon fox
- 40999 View
- 76 Score
- 11 Answer
- Tags: ios ios6 uicollectionview uicollectionviewcell
4 Answered Questions
2 Answered Questions
[SOLVED] UICollectionViewFlowLayout minimumInteritemSpacing bug?
- 2013-01-30 13:23:45
- Teo Sartori
- 3340 View
- 1 Score
- 2 Answer
- Tags: ios uicollectionviewlayout
0 Answered Questions
Customising layout change animation on UICollectionViewFlowLayout
- 2017-05-02 14:22:27
- TRG
- 124 View
- 1 Score
- 0 Answer
- Tags: ios swift swift3 uicollectionview uicollectionviewlayout
3 Answered Questions
[SOLVED] How to adjust UICollectionView contentSize.height based on number of rows in swift
- 2015-05-26 06:48:33
- Victor --------
- 6714 View
- 2 Score
- 3 Answer
- Tags: ios swift uicollectionview
1 Answered Questions
[SOLVED] UICollectionView items disappear when scrolling with my own UICollectionViewFlowLayout subclass
- 2015-04-21 17:32:48
- Aron K.
- 4290 View
- 7 Score
- 1 Answer
- Tags: ios objective-c iphone swift
1 Answered Questions
CGContext - scale and then crop image at point
- 2014-12-16 21:55:48
- user636066
- 631 View
- 0 Score
- 1 Answer
- Tags: ios objective-c uiscrollview uiimageview crop
1 Answered Questions
UICollectionViewFlowLayout performance for Text Grid
- 2014-09-05 13:07:48
- Darren Wheatley
- 293 View
- 0 Score
- 1 Answer
- Tags: ios objective-c uicollectionview uicollectionviewlayout
1 Answered Questions
[SOLVED] UICollectionView layout malfunction
- 2013-06-14 20:43:45
- Matt Mc
- 3358 View
- 0 Score
- 1 Answer
- Tags: ios uicollectionview
16 comments
@JoniVR 2018-04-02 19:08:15
Here's my implementation in Swift 4.2 for vertical cell-based paging:
Some notes:
itemSize
actually matches the size of the item as that's often a problem.self.collectionView.decelerationRate = UIScollViewDecelerationRateFast
.Here's a horizontal version (haven't tested it thoroughly so please forgive any mistakes):
@denis631 2018-08-22 16:23:06
You are the life saviour! Important to note to SET PAGING TO FALSE!!! Lost like 2 hours of my life fixing your function, which already works ...
@JoniVR 2018-08-22 19:04:51
@denis631 I'm so sorry! I should've added that, I'll edit the post to reflect this! Glad it worked :)
@Oliver Pearmain 2018-03-26 15:27:49
For anyone looking for a solution that...
collectionView.contentInset
(and safeArea on iPhone X) into considerationthen please see below...
@Steven.B 2018-04-12 11:25:26
Thanks! just copied&pasted, working perfectly.. way better as expected.
@LinusGeffarth 2018-12-09 18:51:08
One and only solution that actually works. Nice job! Thanks!
@LinusGeffarth 2018-12-09 18:57:08
I suggest to also put
- sectionInset.left
when calculating thecandidateOffsets
to also respect the defined section insets.@Utku Dalmaz 2018-12-24 02:23:52
return cellLayoutAttributes.frame.origin.x - collectionView.contentInset.left - collectionView.safeAreaInsets.left candidateOffsets - sectionInset.left there is a problem in this line
@Oliver Pearmain 2018-12-24 08:25:14
@Dalmaz thanks for notifying me. I have fixed the issue now.
@André Abreu 2016-03-04 14:14:25
Swift version of the accepted answer.
Valid for Swift 3.
@Chris 2016-03-17 14:43:42
This version works great, and it works well for Y axis too if you swap the code around.
@Christian A. Strømmen 2016-03-21 12:04:03
Mostly works great here. But if I stop scrolling, and lift my finger (carefully), it won't scroll to any page and just stop there.
@André Abreu 2016-03-22 14:21:11
@ChristianA.Strømmen Weird, It works just fine with my app.
@iOS Calendar View OnMyProfile 2016-04-23 22:10:17
@AndréAbreu where do i place this function?
@André Abreu 2016-04-25 17:52:56
@Jay You need to subclass UICollectionViewLayout or any class that already subclass it (e.g. UICollectionViewFlowLayout).
@Niels 2017-09-23 11:44:43
A shorter solution (assuming you're caching your layout attributes):
To put this in context:
@lobstah 2017-09-20 21:35:11
Swift 4
The easiest solution for collection view with cells of one size (horizontal scroll):
@Cruz 2017-01-31 10:43:29
@André Abreu's Code
Swift3 version
@G Clovs 2017-06-15 09:51:36
Thanks For that! The best behaviour expected Thanks it's Help a lot!
@iDevAmit 2017-07-02 09:31:45
You could edit there in @Andre answer itself.
@Husein Kareem 2016-09-13 15:54:56
For those looking for a solution in Swift:
@DJSK 2016-07-28 23:58:10
Here is my Swift solution on a horizontally scrolling collection view. It's simple, sweet and avoids any flickering.
@Kwnstantinos Natsios 2016-08-10 17:39:15
what is
itemSize
??@DJSK 2016-08-10 19:50:59
Its the size of the collection cells. These function are used when subclassing UICollectionViewFlowLayout.
@DJSK 2016-08-10 19:51:18
developer.apple.com/library/ios/documentation/UIKit/Reference/…
@blwinters 2018-04-17 20:59:49
I like this solution, but I have a couple comments.
pageWidth()
should useminimumLineSpacing
since it scrolls horizontally. And in my case, I have acontentInset
for the collection view so that the first and last cell can be centered, so I uselet xOffset = pageWidth() * index - collectionView.contentInset.left
.@Pion 2014-06-04 09:08:46
After long testing I found solution to snap to center with custom cell width (each cell has diff. width) which fixes the flickering. Feel free to improve the script.
@sridvijay 2014-07-28 19:35:10
Best solution of them all, thanks! Also to any future readers, you must turn off paging in order for this to work.
@CyberMew 2014-09-04 06:27:29
If one were to want to align it from the left, instead of the cell aligned right in the center, how would we go about changing it?
@Pion 2014-09-04 09:42:34
No sure if I understand correctly, but if you want to start the items in center, and align it to center, you need to change the contentInset. I use this this: gist.github.com/pionl/432fc8059dee3b540e38
@Pion 2014-09-04 09:45:23
To align in the X position of the cell to middle of the view, just remove + (layoutAttributes.frame.size.width / 2) in velocity section.
@TomSawyer 2014-10-06 21:57:30
Cannot assign to 'x' in 'proposedContentOffset' in swift
@Pion 2014-10-08 17:10:36
@TomSawyer i think in Swift you need to use X and Y in caps.
@TomSawyer 2014-10-09 07:57:19
no, it's not. proposedContentOffset.x only can get, not set. so couldnot set value for it.
@Pion 2014-10-09 08:50:36
@TomSawyer Then save the new X offset in new variable and in return create new CGPoint from the variables.
@trdavidson 2015-06-02 07:18:30
@Pion great work here - only problem I'm having is that it does not pick up swiping movements, such that a swipe will make the collectionview act like there is no paging at all. Any ideas how to resolve this?
@Pion 2015-06-02 18:38:54
@trdavidson I'm not experiencing this issue (by some testing I dont see it). I use custom cell size for several cells, that could be why. I have special code to fix the offset when rotating, probably you can use scrollview delegate wait for finish and call thy fix offset. You can get value from targetContentOffsetForProposedContentOffset by passing current contentOffset. My working example is found in Glogster app (on AppleStore).
@iOS Calendar View OnMyProfile 2016-04-23 22:10:46
@Pion where do i place this function?
@Pion 2016-04-25 16:13:16
@Jay Hi, just create a custom Flow delegate and add this code to it. Don't forget to set the custom layout in nib or code.
@katopz 2015-03-02 15:22:17
refer to this answer by Dan Abramov here's Swift version
or gist here https://gist.github.com/katopz/8b04c783387f0c345cd9
@mstubna 2016-09-26 11:17:43
Updated version of this for Swift 3: gist.github.com/mstubna/beed10327e00310d05f12bf4747266a4
@VaporwareWolf 2016-12-07 23:29:49
Dang it @mstubna, I went ahead and copied the above, updated it to swift 3, started making an updated gist, and came back here to collect notes/title at which point i noticed that you had already made a swift 3 gist. Thanks! Too bad I missed it.
@Anton Gaenko 2015-02-13 15:07:19
I prefer to allow user flicking through several pages. So here is my version of
targetContentOffsetForProposedContentOffset
(which based on DarthMike answer) for vertical layout.@DarthMike 2014-03-06 23:28:18
Dan's solution is flawed. It does not handle user flicking well. The cases when user flicks fast and scroll did not move so much, have animation glitches.
My proposed alternative implementation has the same pagination as proposed before, but handles user flicking between pages.
@Rajiev Timal 2014-05-28 18:11:01
Thank you! This worked like a charm. A little hard to understand but getting there.
@TomSawyer 2014-09-29 11:09:36
I'm having this error: Cannot assign to 'x' in 'proposedContentOffset' ? Using swift? how can i assign to x value?
@DarthMike 2014-09-29 12:58:29
@TomSawyer Params are 'let' by default. Try to declare function as this in Swift (using var before param): override func targetContentOffsetForProposedContentOffset(var proposedContentOffset: CGPoint) -> CGPoint
@TomSawyer 2014-09-29 17:32:40
@DarthMike i already did. But could not set x for proposedContentOffset. And i tried return one specific value return CGPointMake(350,proposedContentOffset.y) but it didn't work
@Plot 2014-10-24 09:09:43
You can't use CGPointMake in swift. I personally used this: "var targetContentOffset: CGPoint if pannedLessThanAPage && flicked { targetContentOffset = CGPoint(x: nextPage * pageWidth(), y: proposedContentOffset.y); } else { targetContentOffset = CGPoint(x: round(rawPageValue) * pageWidth(), y: proposedContentOffset.y); } return proposedContentOffset"
@taber 2014-11-08 10:52:46
I think this sets a wrong value for
nextPage
if/when the velocity is 0.0, for example if the user drags over a little, then just lets go of their finger. I'm seeing it get set to 0 instead of 1.@taber 2014-11-08 11:06:08
Recalculating
nextPage
in the else block seems to fix it:nextPage = proposedContentOffset.x / self.pageWidth;
@DarthMike 2014-11-09 11:10:00
@taber Interesting. What do you want to fix exactly? If user does not flick, and didn't move more than half page, then nextpage should be the same as original. Can you describe in UI what is wrong in that case?
@abagmut 2015-02-02 14:18:06
great, work flawless
@Mark Leonard 2016-06-21 12:09:24
Worked for me, but I had trouble at first since I had
pagingEnabled
set to true.@khunshan 2016-11-09 16:09:25
It should be the selected answer.
@Paul T. 2017-01-10 06:25:42
yes, it works better than selected answer
@Mr. Bean 2017-02-24 07:16:38
Mind blowing man, seriously how did you do it, you are my god :)
@keisar 2013-12-26 09:53:37
a small issue I encountered while using targetContentOffsetForProposedContentOffset is a problem with the last cell not adjusting according to the new point I returned.
I found out that the CGPoint I returned had a Y value bigger then allowed so i used the following code at the end of my targetContentOffsetForProposedContentOffset implementation:
just to make it clearer this is my full layout implementation which just imitates vertical paging behavior:
hopefully this will save someone some time and a headache
@Mike M 2014-03-17 11:17:14
Same problem, seems like the collection view will ignore invalid values instead of rounding them off to its bounds.
@Ajaxharg 2013-01-05 20:12:29
Fogmeisters answer worked for me unless I scrolled to the end of the row. My cells don't fit neatly on the screen so it would scroll to the end and jump back with a jerk so that the last cell always overlapped the right edge of the screen.
To prevent this add the following line of code at the start of the targetcontentoffset method
@Au Ris 2017-09-19 15:47:18
I suppose 320 is your collection view width :)
@Ajaxharg 2017-09-20 17:23:42
Got to love looking back at old code. I guess that magic number was that.
@Dan Abramov 2013-01-12 07:15:39
While this answer has been a great help to me, there is a noticeable flicker when you swipe fast on a small distance. It's much easier to reproduce it on the device.
I found that this always happens when
collectionView.contentOffset.x - proposedContentOffset.x
andvelocity.x
have different sings.My solution was to ensure that
proposedContentOffset
is more thancontentOffset.x
if velocity is positive, and less if it is negative. It's in C# but should be fairly simple to translate to Objective C:This code is using
MinContentOffset
,MaxContentOffset
andSnapStep
which should be trivial for you to define. In my case they turned out to be@Rob Keniger 2013-11-28 04:34:36
This works really well. I converted it to Objective-C for those interested: gist.github.com/rkeniger/7687301
@André Henrique 2017-01-02 13:22:51
Just, need to comment this! This is just amazing!!! Worked very well!!
@Fogmeister 2012-11-21 12:52:10
OK, answer is no, there is no way to do this without subclassing UICollectionViewFlowLayout.
However, subclassing it is incredibly easy for anyone who is reading this in the future.
First I set up the subclass call
MyCollectionViewFlowLayout
and then in interface builder I changed the collection view layout to Custom and selected my flow layout subclass.Because you're doing it this way you can't specify items sizes, etc... in IB so in MyCollectionViewFlowLayout.m I have this...
This sets up all the sizes for me and the scroll direction.
Then ...
This ensures that the scrolling ends with a margin of 5.0 on the left hand edge.
That's all I needed to do. I didn't need to set the flow layout in code at all.
@nicktones 2013-01-29 16:10:49
Thanks for your question and answer. Totally helped me out, starting to love UICollectionViews!
@Fogmeister 2013-01-29 16:13:41
It is really powerful when used properly. Have you watched the Collection View sessions from WWDC 2012? They are really worth watching. Some incredible stuff.
@nicktones 2013-01-29 16:19:38
I have yeah great stuff. Your solution works great, except for the first and last cells, which can get 'stick' half off screen. Any ideas on how to solve that? I have a horizontal collection view, much like the app listings in the App Store.
@Fogmeister 2013-01-29 16:37:09
I think Ajaxharg's answer will help solve your problem.
@nicktones 2013-01-29 16:50:51
Cheers I'll give it a try.
@nicktones 2013-01-29 16:56:28
Got it sorted now. I wasn't getting the same problem as Ajaxharg, so that didn't help. However setting the left and right sectionInset to be the same as the scrolling end margin fixed it. All cells now snap including the first and last. Great QnA Fogmeister!
@Fogmeister 2013-01-29 17:19:25
Thanks for the feedback! Glad you got it sorted :D
@Scott Marchant 2013-05-08 16:13:38
If you don't want to set the collection view flow layout in Interface Builder to custom, I think an alternative way is to set the class of the Collection View Flow Layout object for your collection view in Interface Builder. I could be wrong, though.
@fatuhoku 2014-05-21 15:05:45
targetContentOffsetForProposedContentOffset:withVelocity
isn't being called for me when I scroll. What's going on?@TomSawyer 2014-10-07 18:36:12
when i scroll too fast, it will skip one cell, how can it exactly stop at next / prev cell?
@Clay Ellis 2015-12-25 06:35:54
@TomSawyer set the UICollectionView's declaration rate to UIScrollViewDecelerationRateFast.
@chrs 2016-04-14 17:32:56
@fatuhoku make sure that your collectionView's paginEnabled property is set to false
@Dylan Bettermann 2016-05-23 19:52:37
@nicktones can you elaborate on "However setting the left and right sectionInset to be the same as the scrolling end margin fixed it."?
@Annjawn 2018-01-26 22:28:30
Holy Moly, I had to scroll down like a million miles to see this answer. :)
@talshahar 2018-03-27 12:09:01
Thanks for a great working solution !