滚动到collectionView的底部不起作用

Posted

技术标签:

【中文标题】滚动到collectionView的底部不起作用【英文标题】:Scrolling to the bottom of collectionView does not work 【发布时间】:2017-12-29 11:51:25 【问题描述】:

当视图首次加载其项目时,我尝试滚动到 collectionView 的底部。我在 viewDidLoad 中运行这个函数:

private func fetchMessages() 
    guard let r = replyTo else  return 
    let urlString = "\(Config.URL)/api/messages/replyTo/\(r)/offset/\(offset)"
    guard let authToken = Auth.getToken() else  return 
    let headers = ["Authorization": "Bearer \(authToken)"]
    Alamofire.request(urlString, headers: headers).responseJSON  (response) in
        guard let value = response.result.value else  return 
        let json = JSON(value)
        for messagesJSON in json["messages"].arrayValue 
            let message = Message(json: messagesJSON.dictionaryObject ?? [:])
            self.messages.append(message)
        
        self.messages.reverse()
        DispatchQueue.main.async 
            self.collectionView?.reloadData()
            let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
            self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: false)
        
    

我得到最后一个indexPath,然后用scrollToItem滚动到底部,但是它不想滚动到底部。它根本不滚动。除了滚动之外,一切正常。

【问题讨论】:

使用 selectItem 方法。 也许对你有用试试这个:- ***.com/a/48018130/6822622 【参考方案1】:

Taimoor 的回答是正确的(本质上),但我想添加一些背景为什么

viewDidLoad() 在整个视图加载到内存时调用,包括所有子视图。然而此时,视图尚未布局,即所有子视图的框架尚未正确设置,或者换句话说:使用自动布局时,您的约束尚未解决然而当viewDidLoad() 被调用时。

因此,集合视图的框架尚未正确设置 - 它的大小可能仍为 .zero。因此,集合视图无法将任何单元格滚动到其框架中 - 或者至少方法 scrollToItem(at:, at:, animated:) 会出现意外行为。

您可以确定布局已完成的一点是viewDidLayoutSubviews()。但是,请注意,在您的视图控制器的生命周期中,此方法可能会被多次调用 - 每当您的布局中的某些内容发生变化时,都会触发另一个布局传递。发生这种情况时,您可能不希望一次又一次地获取数据,因此您需要以某种方式对此进行跟踪。

如果你想保持简单,viewDidAppear() 也可以工作,因为当视图可见时,它显然已经布局,但用户可能会看到集合视图的滚动/跳跃。

viewWillAppear() 也可能有效,但这里不能保证。

【讨论】:

你的意思是我应该从 viewDidLoad 以外的其他地方调用函数 fetchMessages() 吗?还是只是滚动部分?如果是这样,我在http请求的回调中重新加载collectionView的数据后你会怎么做? 不,我会将fetchMessages() 保留在viewDidLoad() 中,但仅在完成关闭时更新您的数据源(即messages 属性)。然后在该属性的didSet 观察者中,您可以更新集合视图并进行滚动(这涵盖了从您的获取中执行的完成闭包您的视图已经布局之后执行的情况)。对于另一种情况,当您知道布局已完成时,您还需要更新和滚动您的集合视图,例如里面viewDidLayoutSubviews()。这样,您只需执行一次提取,但始终更新 c.v.需要时。 好的,所以我更新了 http 请求完成块中的数据源。然后对消息执行 didSet 并在那里执行 reloadData。然后当 reloadData 完成它的事情时, viewDidLayoutSubviews 将被调用,并且我应该在那里进行滚动?我应该在滚动之前进行逻辑检查吗?例如如果 messages.count > 0 嗯,reloadData() 和 viewDidLayoutSubviews() 是独立的方法。一个不会(必然)触发另一个。但是当 viewDidLayoutSubviews() 被调用时,你肯定知道视图已经被布局并且所有的框架——包括你的集合视图——都被正确设置了。因此,如果在那个时间点您的数据源已经设置好(获取已返回),那么在那里执行重新加载和滚动是有意义的。如果没有,您知道它稍后会返回,在这种情况下,您可以安全地在 fetch 返回时进行重新加载和滚动(didSet() 观察者)。 viewWillLayoutSubviews 为我工作。只需添加一个检查以确保它仅在第一次加载视图时触发滚动到底部,以确保它不会多次调用滚动。【参考方案2】:

viewDidLoad 在元素可见之前调用,因此某些 UI 元素不能很好地操作。诸如在工作中移动按钮但处理子视图通常不会(如滚动 CollectionView)。

当在 viewWillAppear 或 viewDidAppear 中调用时,这些操作中的大多数会发挥最佳效果。因此请尝试在 viewWillAppearviewDidAppear 中执行此操作。

【讨论】:

对我来说同样的问题,但 Taimoor 的回答对我有用,谢谢 ....??

以上是关于滚动到collectionView的底部不起作用的主要内容,如果未能解决你的问题,请参考以下文章

在底部滚动不起作用

UICollectionView,单元格选择不起作用,单元格滚动正常

滚动到 CollectionView 底部时,Collection View Cells 会无意中调整大小

当用户点击 InputAccessoryView 时如何滚动到 CollectionView 的底部

Swift CollectionView 取消选择不起作用

自定义 CollectionView 布局重新加载数据不起作用