How to Create a Centered Uicollectionview Like in Spotify's Player

How to create a centered UICollectionView like in Spotify's Player

As you have said in the comment you want that in the Objective-c code, there is a very famous library called iCarousel which can be helpful in completing your requirement.Link: https://github.com/nicklockwood/iCarousel

You may use 'Rotary' or 'Linear' or some other style with little or no modification to implement the custom view

To implement it you have implement only some delegate methods of it and it's working for ex:

//specify the type you want to use in viewDidLoad
_carousel.type = iCarouselTypeRotary;

//Set the following delegate methods
- (NSInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
//return the total number of items in the carousel
return [_items count];
}

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;

//create new view if no view is available for recycling
if (view == nil)
{
//don't do anything specific to the index within
//this `if (view == nil) {...}` statement because the view will be
//recycled and used with other index values later
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)];
((UIImageView *)view).image = [UIImage imageNamed:@"page.png"];
view.contentMode = UIViewContentModeCenter;

label = [[UILabel alloc] initWithFrame:view.bounds];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = [label.font fontWithSize:50];
label.tag = 1;
[view addSubview:label];
}
else
{
//get a reference to the label in the recycled view
label = (UILabel *)[view viewWithTag:1];
}

//set item label
label.text = [_items[index] stringValue];

return view;
}

- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
{
if (option == iCarouselOptionSpacing)
{
return value * 1.1;
}
return value;
}

You can check the full working demo from 'Examples/Basic iOS Example' which is included with the Github repository link

As it is old and popular you can find some related tutorials for it and it will also be much stable than the custom code implementation

Snap to center of a cell when scrolling UICollectionView horizontally

While originally I was using Objective-C, I since switched so Swift and the original accepted answer did not suffice.

I ended up creating a UICollectionViewLayout subclass which provides the best (imo) experience as opposed to the other functions which alter content offset or something similar when the user has stopped scrolling.

class SnappingCollectionViewLayout: UICollectionViewFlowLayout {

override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) }

var offsetAdjustment = CGFloat.greatestFiniteMagnitude
let horizontalOffset = proposedContentOffset.x + collectionView.contentInset.left

let targetRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)

let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)

layoutAttributesArray?.forEach({ (layoutAttributes) in
let itemOffset = layoutAttributes.frame.origin.x
if fabsf(Float(itemOffset - horizontalOffset)) < fabsf(Float(offsetAdjustment)) {
offsetAdjustment = itemOffset - horizontalOffset
}
})

return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
}
}

For the most native feeling deceleration with the current layout subclass, make sure to set the following:

collectionView?.decelerationRate = UIScrollViewDecelerationRateFast

UICollectionViewFlowLayout `targetContentOffset` - how to keep cells centered after orientation change?

As far as I can see, the method is getting called not when you just rotate the device, but when the layout changes. Meaning, if you change from Landscape Left to Landscape Right the delegate method is not called — however if you rotating from any Landscape to Portrait or other way around, it is working fine.

Important!

For maintaining the centered collection view cell, add the targetContentOffset(forProposedContentOffset:) method. This is called after a rotation, and is not the same as your current targetContentOffset(forProposedContentOffset:withScrollingVelocity:) method. In your final code, you should use both of these methods. To summarize:

  • targetContentOffset(forProposedContentOffset:) is called after a layout change
  • targetContentOffset(forProposedContentOffset:withScrollingVelocity:) is called when the user lifts their finger

So, in your PagingFlowLayout class, paste the following code:

private var focusedIndexPath: IndexPath?

override func prepare(forAnimatedBoundsChange oldBounds: CGRect) {
super.prepare(forAnimatedBoundsChange: oldBounds)
focusedIndexPath = collectionView?.indexPathsForVisibleItems.first
}
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
guard
let indexPath = focusedIndexPath,
let attributes = layoutAttributesForItem(at: indexPath),
let collectionView = collectionView
else {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
}
return CGPoint(
x: attributes.frame.origin.x - collectionView.contentInset.left,
y: attributes.frame.origin.y - collectionView.contentInset.top
)
}
override func finalizeAnimatedBoundsChange() {
super.finalizeAnimatedBoundsChange()
focusedIndexPath = nil
}

The code was from: https://stackoverflow.com/a/54868999/11332605.

UICollectionView Horizontal Paging not centered

Remove spaces between items. For horizontal scrolling collection view set minimum line spacing to 0. You can do this with interface builder or with method of UICollectionViewDelegateFlowLayout protocol:

- (CGFloat)collectionView:(UICollectionView *)collectionView 
layout:(UICollectionViewLayout *)collectionViewLayout
minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}

Sample Image

Another way is making your cell's width less than collectionView's width for a value of horizontal space between items. Then add section insets with left and right insets that equal a half of horizontal space between items. For example, your minimum line spacing is 10:

- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 10;
}

- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(collectionView.frame.size.width - 10, collectionView.frame.size.height);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(0, 5, 0, 5);
}

Sample Image

And third way: manipulate collectionView scroll in scrollViewDidEndDecelerating: method:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.collectionView) {
CGPoint currentCellOffset = self.collectionView.contentOffset;
currentCellOffset.x += self.collectionView.frame.size.width / 2;
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:currentCellOffset];
[self.collectionView scrollToItemAtIndexPath:indexPath
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
animated:YES];
}
}

Sample Image

Center First UICollectionViewCell in UICollectionView

To make collection view cell center, your controller must confirm UICollectionViewDelegateFlowLayout protocol. Use below code to make cells center:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width * 0.7, height: 300)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let width = collectionView.frame.width
let margin = width * 0.3
return UIEdgeInsets(top: 10, left: margin / 2, bottom: 10, right: margin / 2)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.width * 0.3 / 2
}

If you return 1 or 2 in numberOfItemsInSection method, It works fine.

UICollectionView Bring Item to center position

SOL 1:

collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .centeredHorizontally, animated: true)

SOL 2:
I suggest you add empty items at the beginning and end. So the user can scroll the first and last visible item to the centre.



Related Topics



Leave a reply



Submit