简体   繁体   English

UICollectionView 滚动到不使用水平方向的项目

[英]UICollectionView scroll to item not working with horizontal direction

I have a UICollectionView within a UIViewController with paging enabled.我在启用了分页的UICollectionView中有一个UIViewController For some strange reason collectionView.scrollToItem works when the direction of the collectionview is vertical but doesn't when direction is horizontal .由于某些奇怪的原因, collectionView.scrollToItemcollectionview的方向为vertical时有效,但在方向为horizontal时无效。 Is this there something I'm doing wrong or is this supposed to happen?这是我做错了什么还是应该发生?

  //Test scrollToItem
  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let i = IndexPath(item: 3, section: 0)
    collectionView.reloadData()
    collectionView.scrollToItem(at: i, at: .top, animated: true)
    print("Selected")
  }

For iOS 14对于 iOS 14

Apparently there is a new bug in UICollectionView that is causing scrollToItem to not work when paging is enabled.显然UICollectionView中存在一个新错误,导致在启用分页时scrollToItem无法工作。 The work around is to disable paging before calling scrollToItem , then re-enabling it afterwards:解决方法是在调用scrollToItem之前禁用分页,然后再重新启用它:

collectionView.isPagingEnabled = false
collectionView.scrollToItem(
    at: IndexPath(item: value, section: 0),
    at: .centeredHorizontally,
    animated: true
)
collectionView.isPagingEnabled = true

Source: https://developer.apple.com/forums/thread/663156来源: https : //developer.apple.com/forums/thread/663156

For this part:对于这部分:

collectionView.scrollToItem(at: i, at: .top, animated: true)

When the scroll direction is horizontal you need to use at: left , at: right or at: centeredHorizontally .当滚动方向为horizontal您需要使用at: leftat: rightat: centeredHorizontally at: top is for vertical direction. at: topvertical方向。

I had trouble implementing this in a flow layout with entered paging per item.我无法在每个项目输入分页的流布局中实现这一点。 The .centeredHorizontally just wouldn't work for me so i use scroll to rect and Check there is data before scrolling: .centralHorizo​​ntally 对我不起作用,所以我使用滚动来 rect 并在滚动前检查是否有数据:

    if self.collectionView?.dataSource?.collectionView(self.collectionView!, cellForItemAt: IndexPath(row: 0, section: 0)) != nil {
        let rect = self.collectionView.layoutAttributesForItem(at: IndexPath(item: data[index], section: 0))?.frame
        self.collectionView.scrollRectToVisible(rect!, animated: false)
    }

Swift 5.1, Xcode 11.4斯威夫特 5.1,Xcode 11.4

collectionView.scrollToItem(at: IndexPath(item: pageNumber , section: 0), at: .centeredHorizontally, animated: true)
self.collectionView.setNeedsLayout() // **Without this effect wont be visible**

For me, I had to scroll collection view on main thread like:对我来说,我必须在主线程上滚动集合视图,例如:

    DispatchQueue.main.async {
        self.collectionView.scrollToItem(at: IndexPath(item: index, section: 0), at: .centeredHorizontally, animated: true)
    }

After adding items to collectionView and reloadData() , scrollToItem was not working because reloading data has not finished yet.将项目添加到 collectionView 和reloadData()scrollToItem无法正常工作,因为重新加载数据尚未完成。 Because of this I added performBatchUpdates like this :因此,我像这样添加了 performBatchUpdates :

self.dataSource.append("Test")
self.collectionView.performBatchUpdates ({
    self.collectionView.reloadData()
}, completion: { _ in 
    self.collectionView.scrollToItem(at: IndexPath(item: 3, section: 0),
     at: .centeredHorizontally,
     animated: false)
})

I know it's not about this question but it will be helpful for this title.我知道这不是关于这个问题,但它会对这个标题有所帮助。

I have this issue: when button tapped (for horizontal collection scroll to next item) it always returns to first item with UI bug.我有这个问题:当点击按钮(水平集合滚动到下一个项目)时,它总是返回到第一个带有 UI 错误的项目。

The reason was in this parameter: myCollection.isPagingEnabled = true原因在这个参数中: myCollection.isPagingEnabled = true

Solution: just disable paging before scroll to next item, and enable it after scroll.解决方案:只需在滚动到下一项之前禁用分页,并在滚动后启用它。

For iOS 14+适用于 iOS 14+

It's so stupid but it works.这太愚蠢了,但它有效。

 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
      self?.collectionView.scrollToItem(at: i, at: .top, animated: true)
 }

I try a lot of things and fail.我尝试了很多东西,但都失败了。 This saves my day.这节省了我的一天。

     func scrollToIndex(index:Int) {
       let rect = self.collectionView.layoutAttributesForItem(at: IndexPath(row: index, section: 0))?.frame
       self.collectionView.scrollRectToVisible(rect!, animated: true)
     }

Reference: https://stackoverflow.com/a/41413575/9047324参考: https : //stackoverflow.com/a/41413575/9047324

Swift 3:斯威夫特 3:

This worked for me on horizontal collection view.这在水平集合视图上对我有用。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

//Show 14th row as first row
self.activityCollectionView?.scrollToItem(at: IndexPath(row: 14, section: 0), at: UICollectionViewScrollPosition.right, animated: true)
}

You can choose whether the desired cell at index to be on right or left on scroll position.您可以选择索引处的所需单元格是在滚动位置的右侧还是左侧。

UICollectionViewScrollPosition.right or UICollectionViewScrollPosition.left.

For scrolling the item, there is no need to reloadData. 对于滚动项目,不需要reloadData。 Remove below line from your code and try. 从代码中删除以下行,然后尝试。

collectionView.reloadData()

Swift 5 if with animation horizontally Swift 5如果水平动画

func scrollToIndex(index:Int) {
 self.collectionView?.scrollToItem(at: IndexPath(item: index, section: 0), at: .centeredHorizontally, animated: true)
}

Your example:你的例子:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  
   collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
   print("Selected: \(indexPath.row)")
   collectionView.reloadData()
  }

sometimes you can set有时你可以设置

collectionView.collectionlayout.invalidate()
collectionView.scrollToItem(row:2,section:0)

If you have networking which can make disturb main thread, you have to avoid or after finish that, have to apply this code.如果您的网络可以干扰主线程,则必须避免或完成后,必须应用此代码。 for example例如

self.group.enter()
some thread
self.group.leave()

This will not work if you call scrollToItem before viewDidLayoutSubviews .如果您在viewDidLayoutSubviews之前调用scrollToItem ,这将不起作用。 Using scrollToItem in viewDidLoad will not work.viewDidLoad使用scrollToItem将不起作用。 So call this function after viewDidLayoutSubviews() is completed.所以在viewDidLayoutSubviews()完成后调用这个函数。

As @Dheeraj Agarwal points out, none of the 'ScrollTo' functionality of UICollectionView OR UIScrollView will seem to work properly if it is called before the view has been laid out.正如@Dheeraj Agarwal 指出的那样,如果在布局视图之前调用UICollectionViewUIScrollView的“ScrollTo”功能似乎都不会正常工作。 To be accurate, I think they WORK, but the effects are immediately nullified because laying out a UICollectionView causes it to reset to its minimum bounds, probably because of all the cell layout functions that will trigger, and the fact its content size may change.准确地说,我认为它们可以工作,但效果会立即失效,因为布置UICollectionView会导致它重置为最小边界,可能是因为所有单元格布局功能都会触发,而且它的内容大小可能会改变。

The solution is to make sure this function is called after layout occurs, but it's not that simple.解决方案是确保在布局发生后调用此 function,但这并不是那么简单。 It's entirely likely that a collection view may be told to layout its content again and again in response to various changes - setting delegates, the contents updating, the view controller being added to a parent and therefore changing size.集合视图很可能会被告知一次又一次地布局其内容以响应各种更改 - 设置代表,内容更新,视图 controller 被添加到父级并因此改变大小。 Each time this happens it'll reset to 0:0 offset.每次发生这种情况时,它都会重置为 0:0 偏移量。

You'll therefore have to keep a reference to the desired offset / cell index / frame until such a time as you are CERTAIN there will be no more unexpected layout updates.因此,您必须保留对所需偏移量/单元格索引/框架的引用,直到您确定不会有更多意外的布局更新。 You can't just nil it out immediately as your collection view's layout might change multiple times before the view appears.您不能立即取消它,因为您的集合视图的布局可能会在视图出现之前多次更改。 I'm currently storing a frame in an attribute and calling the function in layoutFrames every time (my collection view's parent is a custom view, not a view controller).我目前正在将一个框架存储在一个属性中,并且每次都在layoutFrames中调用 function (我的集合视图的父级是自定义视图,而不是视图控制器)。 Although this has the slightly annoying feature of scrolling back again if the user rotates their phone, I consider it acceptable since this is a custom keyboard and most users will work with it in one orientation or the other, they won't keep flipping their phone around just to select a single value.虽然如果用户旋转他们的手机,这有一个有点烦人的再次滚动的功能,但我认为这是可以接受的,因为这是一个自定义键盘,大多数用户会在一个方向或另一个方向上使用它,他们不会一直翻转他们的手机大约只是到 select 单个值。

Solutions like calling DispatchQueue.main.asyncAfter are fragile.像调用DispatchQueue.main.asyncAfter这样的解决方案是脆弱的。 They work because the function call gets delayed until after the first layout occurs, but this may not always-and-forever solve the problem.它们之所以起作用,是因为 function 调用被延迟到第一个布局发生之后,但这可能不会永远解决问题。

I guess the 'Scroll To' functions were only ever intended to be used in response to direct user input after the collection view is already populated.我猜“滚动到”功能仅用于在集合视图已经填充后响应直接用户输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM