UIRefreshControl endRefresh 在启用大标题时跳转

Posted

技术标签:

【中文标题】UIRefreshControl endRefresh 在启用大标题时跳转【英文标题】:UIRefreshControl endRefresh jumps when used with Large Title enabled 【发布时间】:2019-06-14 17:03:06 【问题描述】:

我正在尝试使用UIRefreshControl,但是当我调用endRefreshing() 时,它会跳过UINavigationBar。只有当我使用UIRefreshControl 和大标题时才会出现问题。 看了这里报告的一些类似的问题(UIRefreshControl glitching in combination with custom TableViewCell),我尝试在拖动结束后才刷新,但是仍然出现该错误。也试过用

self.navigationController?.navigationBar.isTranslucent = falseself.extendedLayoutIncludesOpaqueBars = true

但是,在其他问题上找到的解决方案似乎都没有解决问题,仍然不顺利。

正在发生的事情的视频:

https://www.youtube.com/watch?v=2BBRnZ444bE

应用委托

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate 
    var window: UIWindow?

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool 

        let window = UIWindow(frame: UIScreen.main.bounds)
        window.makeKeyAndVisible()
        let nav = UINavigationController()
        nav.title = "My Nav"
        nav.navigationBar.prefersLargeTitles = true
        nav.viewControllers = [ViewController()]
        window.rootViewController = nav

        self.window = window
        return true
    


注意我使用的是大标题:

        let nav = UINavigationController()
        nav.title = "My Nav"
        nav.navigationBar.prefersLargeTitles = true

ViewController:

import UIKit
import Foundation

final class ViewController: UICollectionViewController 
    let randomHeight = Int.random(in: 100..<300)

    init() 
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.estimatedItemSize = CGSize(width: 20, height: 20)
        super.init(collectionViewLayout: layout)
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    override func viewDidLoad() 
        super.viewDidLoad()
        navigationItem.title = "Try to refresh"

        self.navigationController?.navigationBar.isTranslucent = false
        self.extendedLayoutIncludesOpaqueBars = true

        collectionView.backgroundColor = .white
        registerCells()
        setupRefreshControl()
    

    private func registerCells() 
        self.collectionView.register(
            Cell.self,
            forCellWithReuseIdentifier: "Cell"
        )
    

    private func setupRefreshControl() 
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(
            self,
            action: #selector(refreshControlDidFire),
            for: .valueChanged
        )
        self.collectionView.refreshControl = refreshControl
    

    @objc private func refreshControlDidFire(_ sender: Any?) 
        if let sender = sender as? UIRefreshControl, sender.isRefreshing 
            refresh()
        
    

    override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) 
        if collectionView.refreshControl!.isRefreshing 
            refresh()
        
    

    private func refresh() 
        if !collectionView.isDragging 
            collectionView.refreshControl!.endRefreshing()
            collectionView.perform(#selector(collectionView.reloadData), with: nil, afterDelay: 0.05)
        
    


extension ViewController 
    override func numberOfSections(in collectionView: UICollectionView) -> Int 
        return 1
    

    override func collectionView(
        _ collectionView: UICollectionView,
        numberOfItemsInSection section: Int
        ) -> Int 
        return 10
    

    override func collectionView(_ collectionView: UICollectionView,
                                 cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        guard let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: "Cell", for: indexPath
            ) as? Cell else 
                return UICollectionViewCell()
        

        cell.label.text = "Text number \(indexPath.row), with height \(randomHeight)"
        cell.heightAnchorConstraint.constant = CGFloat(randomHeight)
        return cell
    


extension ViewController: UICollectionViewDelegateFlowLayout 

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets 
        return UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
    


final class Cell: UICollectionViewCell 
    private let shadowView = UIView()
    private let containerView = UIView()
    private let content = UIView()
    let label = UILabel()
    var heightAnchorConstraint: NSLayoutConstraint!

    override init(frame: CGRect = .zero) 
        super.init(frame: frame)
        setupViews()
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    private func setupViews() 
        insertSubview(shadowView, at: 0)
        addSubview(containerView)
        containerView.addSubview(label)
        containerView.addSubview(content)
        activateConstraints()
    

    private func activateConstraints() 
        self.translatesAutoresizingMaskIntoConstraints = false
        shadowView.translatesAutoresizingMaskIntoConstraints = false
        containerView.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false
        content.translatesAutoresizingMaskIntoConstraints = false

        shadowView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        shadowView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        shadowView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
        shadowView.bottomAnchor
            .constraint(equalTo: self.bottomAnchor).isActive = true

        containerView.backgroundColor = .white
        containerView.layer.cornerRadius = 14

        containerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
        let widthAnchorConstraint = containerView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20)
        widthAnchorConstraint.identifier = "Width ContainerView"
        widthAnchorConstraint.priority = .defaultHigh
        widthAnchorConstraint.isActive = true

        label.numberOfLines = 0
        label.textAlignment = .center
        label.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
        label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
        label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true

        content.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20).isActive = true
        content.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10).isActive = true
        content.bottomAnchor.constraint(lessThanOrEqualTo: containerView.bottomAnchor, constant: -10).isActive = true
        heightAnchorConstraint = content.heightAnchor.constraint(greaterThanOrEqualToConstant: 220)
        heightAnchorConstraint.identifier = "Height Content"
        heightAnchorConstraint.priority = .defaultHigh
        heightAnchorConstraint.isActive = true
        content.widthAnchor.constraint(equalToConstant: 40).isActive = true
        content.backgroundColor = .red

    

    override func layoutSubviews() 
        super.layoutSubviews()
        applyShadow(width: 0.20, height: -0.064)
    

    private func applyShadow(width: CGFloat, height: CGFloat) 
        let shadowPath = UIBezierPath(roundedRect: shadowView.bounds, cornerRadius: 14.0)
        shadowView.layer.masksToBounds = false
        shadowView.layer.shadowRadius = 8.0
        shadowView.layer.shadowColor = UIColor.black.cgColor
        shadowView.layer.shadowOffset = CGSize(width: width, height: height)
        shadowView.layer.shadowOpacity = 0.3
        shadowView.layer.shadowPath = shadowPath.cgPath
    

【问题讨论】:

【参考方案1】:

问题与layout.estimatedItemSize = CGSize(width: 20, height: 20)有关

当我们使用AutoLayout 调整单元格大小时,它会创建一个带有UIRefreshControl 和导航栏大标题的错误。因此,如果您使用的 layout.estimatedItemSize 的大小与我们预期的相同或更大。所以bug不会发生,故障也不会发生。

基本上,问题是当我们调用updateData 但单元格比我们预期的要大,UICollectinView 的每个单元格都会调整到更大的大小,然后UICollectionViewController 就会出现故障。

【讨论】:

【参考方案2】:

所以,我尝试了您的代码,一切正常。

但我在 iPhone 7 上运行它 - 那里没有大标题。

我想,这是largeTitle 的问题。

你可以试试用这个代码sn-p:

self.navigationController?.navigationBar.prefersLargeTitles = false
self.navigationController?.navigationBar.prefersLargeTitles = true

【讨论】:

谢谢!但是,我想知道如何将UIRefreshControllargeTitle 一起使用。我知道这个问题与大标题有关,但我想知道如何解决它。 哦。太遗憾了:( 我也用导航控制器更新了问题,显示我正在使用navigationController.navigationBar.prefersLargeTitles = true 我在这里发现了问题并回答:D 它与单元格的自动调整大小有关。 @Davydov 丹尼斯

以上是关于UIRefreshControl endRefresh 在启用大标题时跳转的主要内容,如果未能解决你的问题,请参考以下文章

下面的 UIRefreshControl 块视图

使用 UIRefreshControl

UIRefreshControl 重复出现

UIRefreshControl 自动启动

完全禁用 UIRefreshControl - iOS

将目标添加到 UIRefreshControl()