Ios UICollectionView单元格滚动到中间
我正在UIViewController中使用UICollectionView 我的collectionview属性设置如下 现在我想细胞是在屏幕上的中心后滚动! 备选案文1: 备选案文2: 要实现选项2,我必须做些什么 更新: 最后我用了下面的代码作为滚动其他答案是不顺利的Ios UICollectionView单元格滚动到中间,ios,objective-c,uicollectionviewcell,uicollectionviewlayout,Ios,Objective C,Uicollectionviewcell,Uicollectionviewlayout,我正在UIViewController中使用UICollectionView 我的collectionview属性设置如下 现在我想细胞是在屏幕上的中心后滚动! 备选案文1: 备选案文2: 要实现选项2,我必须做些什么 更新: 最后我用了下面的代码作为滚动其他答案是不顺利的 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{ CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);
CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
NSArray* array = [super layoutAttributesForElementsInRect:targetRect];
for (UICollectionViewLayoutAttributes* layoutAttributes in array) {
CGFloat itemHorizontalCenter = layoutAttributes.center.x;
if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
offsetAdjustment = itemHorizontalCenter - horizontalCenter;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
-(CGPoint)targetContentOffset for proposedContentOffset:(CGPoint)proposedContentOffset与CrollingVelocity:(CGPoint)速度
{CGFloat offsetAdjustment=MAXFLOAT;
CGFloat horizontalCenter=proposedContentOffset.x+(CGRectGetWidth(self.collectionView.bounds)/2.0);
CGRect targetRect=CGRectMake(proposedContentOffset.x,0.0,self.collectionView.bounds.size.width,self.collectionView.bounds.size.height);
NSArray*数组=[super-layouttributesforementsinrect:targetRect];
用于(UICollectionViewLayoutAttribute*数组中的LayoutAttribute){
CGFloat itemHorizontalCenter=布局属性.center.x;
if(ABS(项目水平中心-水平中心)
您可以在UICollectionViewLayout
子类中重写targetContentOffsetForProposedContentOffset:with CrollingVelocity:
方法,并在其中按如下方式计算偏移量:
@property (nonatomic, assign) CGFloat previousOffset;
@property (nonatomic, assign) NSInteger currentPage;
...
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
NSInteger itemsCount = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];
// Imitating paging behaviour
// Check previous offset and scroll direction
if ((self.previousOffset > self.collectionView.contentOffset.x) && (velocity.x < 0.0f)) {
self.currentPage = MAX(self.currentPage - 1, 0);
} else if ((self.previousOffset < self.collectionView.contentOffset.x) && (velocity.x > 0.0f)) {
self.currentPage = MIN(self.currentPage + 1, itemsCount - 1);
}
// Update offset by using item size + spacing
CGFloat updatedOffset = (self.itemSize.width + self.minimumInteritemSpacing) * self.currentPage;
self.previousOffset = updatedOffset;
return CGPointMake(updatedOffset, proposedContentOffset.y);
}
更新:附加Swift 4.2版本
...
collectionView.isPagingEnabled = false
...
class YourCollectionLayoutSubclass: UICollectionViewFlowLayout {
private var previousOffset: CGFloat = 0
private var currentPage: Int = 0
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
}
let itemsCount = collectionView.numberOfItems(inSection: 0)
// Imitating paging behaviour
// Check previous offset and scroll direction
if previousOffset > collectionView.contentOffset.x && velocity.x < 0 {
currentPage = max(currentPage - 1, 0)
} else if previousOffset < collectionView.contentOffset.x && velocity.x > 0 {
currentPage = min(currentPage + 1, itemsCount - 1)
}
// Update offset by using item size + spacing
let updatedOffset = (itemSize.width + minimumInteritemSpacing) * CGFloat(currentPage)
previousOffset = updatedOffset
return CGPoint(x: updatedOffset, y: proposedContentOffset.y)
}
}
。。。
collectionView.isPaginEnabled=false
...
类YourCollectionLayoutSubclass:UICollectionViewFlowLayout{
private var previousOffset:CGFloat=0
私有变量当前页:Int=0
覆盖func targetContentOffset(forProposedContentOffset proposedContentOffset:CGPoint,使用CrollingVelocity:CGPoint)->CGPoint{
guard let collectionView=collectionView else{
返回super.targetContentOffset(forProposedContentOffset:proposedContentOffset,带CrollingVelocity:velocity)
}
让itemsCount=collectionView.numberOfItems(第0节)
//模仿分页行为
//检查上一个偏移和滚动方向
如果previousOffset>collectionView.contentOffset.x&&velocity.x<0{
currentPage=最大值(currentPage-1,0)
}否则,如果previousOffset0{
currentPage=min(currentPage+1,ItemScont-1)
}
//使用项目大小+间距更新偏移量
让UpdateOffset=(itemSize.width+MinimumItemSpacing)*CGFloat(currentPage)
previousOffset=UpdateOffset
返回CGPoint(x:updateOffset,y:proposedContentOffset.y)
}
}
您可以使用代码
self.collectionView.scrollToItem(at:indepath,at:.centered水平,动画:true)
我不知道为什么每个人的答案都这么复杂,我只需在Interface Builder中启用分页功能,它就能完美工作 这是@dmitry zhukov的Swift 3版本(谢谢你的提醒!)
class PagedCollectionLayout:UICollectionViewFlowLayout{
var previousOffset:CGFloat=0
var currentPage:CGFloat=0
覆盖func targetContentOffset(forProposedContentOffset proposedContentOffset:CGPoint,使用CrollingVelocity:CGPoint)->CGPoint{
设sup=super.targetContentOffset(forProposedContentOffset:proposedContentOffset,带CrollingVelocity:velocity)
警卫
让validCollection=collectionView,
让dataSource=validCollection.dataSource
else{return sup}
让itemsCount=dataSource.collectionView(validCollection,numberOfItemsInSection:0)
//模仿分页行为
//检查上一个偏移和滚动方向
如果(previousOffset>validCollection.contentOffset.x)&(velocity.x<0){
currentPage=最大值(currentPage-1,0)
}
否则如果(previousOffset0){
currentPage=min(currentPage+1,CGFloat(itemscont-1))
}
//使用项目大小+间距更新偏移量
让UpdateOffset=((itemSize.width+MinimumItemSpacing)*currentPage)
self.previousOffset=updateoffset
设updatedPoint=CGPoint(x:UpdateOffset,y:proposedContentOffset.y)
返回更新点
}
}
我找到了很多信息和解决方案
现在,我用这个
在UICollectionViewFlowLayout替代上:
override public func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
if display != .inline {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
}
guard let collectionView = collectionView else {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
}
let willtoNextX: CGFloat
if proposedContentOffset.x <= 0 || collectionView.contentOffset == proposedContentOffset {
willtoNextX = proposedContentOffset.x
} else {
let width = collectionView.bounds.size.width
willtoNextX = collectionView.contentOffset.x + (velocity.x > 0 ? width : -width)
}
let targetRect = CGRect(x: willtoNextX, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)
var offsetAdjustCoefficient = CGFloat.greatestFiniteMagnitude
let horizontalOffset = proposedContentOffset.x + collectionView.contentInset.left
let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)
layoutAttributesArray?.forEach({ (layoutAttributes) in
let itemOffset = layoutAttributes.frame.origin.x
if fabsf(Float(itemOffset - horizontalOffset)) < fabsf(Float(offsetAdjustCoefficient)) {
offsetAdjustCoefficient = itemOffset - horizontalOffset
}
})
return CGPoint(x: proposedContentOffset.x + offsetAdjustCoefficient, y: proposedContentOffset.y)
}
现在,细胞在中心
在设置正确的itemSize
、left
和right
插入后,我更喜欢这样做,而不是将布局子类化
//Setting decelerationRate to fast gives a nice experience
collectionView.decelerationRate = .fast
//Add this to your view anywhere
func centerCell () {
let centerPoint = CGPoint(x: collectionView.contentOffset.x + collectionView.frame.midX, y: 100)
if let path = collectionView.indexPathForItem(at: centerPoint) {
collectionView.scrollToItem(at: path, at: .centeredHorizontally, animated: true)
}
}
//Set collectionView.delegate = self then add below funcs
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
centerCell()
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
centerCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
centerCell()
}
}
我的解决方案是像分页一样滚动
//
collectionview.isPaging = false
//
class CollectionLayoutSubclass: UICollectionViewFlowLayout {
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
var point = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
guard let collectionView = collectionView else {
return point
}
let cells = collectionView.visibleCells
let centerPoint = collectionView.center
var cellFrame: CGRect = CGRect.zero
for cell in cells {
cellFrame = collectionView.convert(cell.frame, to: collectionView.superview)
var newCenterPoint: CGPoint = centerPoint
if velocity.x > 0 {
newCenterPoint = CGPoint(x: centerPoint.x * 1.5, y: centerPoint.y)
} else if velocity.x < 0 {
newCenterPoint = CGPoint(x: centerPoint.x * 0.5, y: centerPoint.y)
}
guard cellFrame.contains(newCenterPoint) else {
continue
}
let x = collectionView.frame.width / 2 - cell.frame.width / 2
point.x = cell.frame.origin.x - x
break
}
return point
}
}
//
collectionview.isPaging=false
//
类CollectionLayoutSubclass:UICollectionViewFlowLayout{
覆盖func targetContentOffset(forProposedContentOffset proposedContentOffset:CGPoint,使用CrollingVelocity:CGPoint)->CGPoint{
var point=super.targetContentOffset(forProposedContentOffset:proposedContentOffset,带CrollingVelocity:velocity)
guard let collectionView=collectionView else{
返回点
}
let cells=collectionView.visibleCells
让centerPoint=collectionView.center
var cellFrame:CGRect=CGRect.zero
细胞中的细胞{
cellFrame=collectionView.convert(cell.frame,to:collectionView.superview)
var newCenterPoint:CGPoint=centerPoint
如果速度.x>0{
newCenterPoint=CGPoint(x:centerPoint.x*1.5,y:centerPoint.y)
}否则,如果速度.x<0{
newCenterPoint=CGPoint(x:centerPoint.x*0.5,y:centerPoint.y)
}
collectionView.decelerationRate = .fast
collectionView.isPagingEnabled = false
collectionView.contentInset = UIEdgeInsets.init(top: 0, left: 16, bottom: 0, right: 16)
//Setting decelerationRate to fast gives a nice experience
collectionView.decelerationRate = .fast
//Add this to your view anywhere
func centerCell () {
let centerPoint = CGPoint(x: collectionView.contentOffset.x + collectionView.frame.midX, y: 100)
if let path = collectionView.indexPathForItem(at: centerPoint) {
collectionView.scrollToItem(at: path, at: .centeredHorizontally, animated: true)
}
}
//Set collectionView.delegate = self then add below funcs
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
centerCell()
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
centerCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
centerCell()
}
}
//
collectionview.isPaging = false
//
class CollectionLayoutSubclass: UICollectionViewFlowLayout {
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
var point = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
guard let collectionView = collectionView else {
return point
}
let cells = collectionView.visibleCells
let centerPoint = collectionView.center
var cellFrame: CGRect = CGRect.zero
for cell in cells {
cellFrame = collectionView.convert(cell.frame, to: collectionView.superview)
var newCenterPoint: CGPoint = centerPoint
if velocity.x > 0 {
newCenterPoint = CGPoint(x: centerPoint.x * 1.5, y: centerPoint.y)
} else if velocity.x < 0 {
newCenterPoint = CGPoint(x: centerPoint.x * 0.5, y: centerPoint.y)
}
guard cellFrame.contains(newCenterPoint) else {
continue
}
let x = collectionView.frame.width / 2 - cell.frame.width / 2
point.x = cell.frame.origin.x - x
break
}
return point
}
}