UICollectionViewController 页脚在 endEditing 后无法正确重新定位

Posted

技术标签:

【中文标题】UICollectionViewController 页脚在 endEditing 后无法正确重新定位【英文标题】:UICollectionViewController footer not re-positioning itself properly after endEditing 【发布时间】:2019-02-26 19:50:07 【问题描述】:

我有一个简单的 UICollectionViewController,它是一个带有固定到可见边界的页眉和页脚的部分。当我单击页脚中的 textField 时,页脚会自动在键盘上方进行动画处理,并且 collectionView 会滚动以显示否则会如您所料被键盘隐藏的单元格。但是,当我单击键盘外部的一个并通过调用self.view.endEditing(true) 将其关闭时,除非在底部附近滚动collectionView,否则页脚和collectionView 不会做出反应。如果 collectionView 在底部附近滚动,则页脚和 collectionView 会按预期进行动画处理。

如何强制页脚和 collectionView 每次都正确设置动画?

在下图中,页眉为橙色,页脚为绿色,并且有 10 个单元格以红色和蓝色交替显示。

实际行为(当键盘关闭时您未在 collectionView 底部附近滚动时发生)

期望的行为(当您在键盘关闭时在 collectionView 底部或附近滚动时发生)

我知道有一些方法可以在键盘出现和消失时使用通知来实现这一点,但如果可能的话,我宁愿只使用默认的 UICollectionViewController 行为。是否缺少某些配置或设置?

代码: 新的单一视图应用程序。删除 StoryBoard 和 ViewController.swift。从 info plist 文件中删除主情节提要条目。

AppDelegate.swift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate 

var window: UIWindow?

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

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()
    window?.rootViewController = CollectionViewController.init()

    return true


CollectionViewController.swift

import UIKit

private let reuseIdentifier = "Cell"
private let collectionViewHeaderFooterReuseIdentifier = "MyHeaderFooterClass"

class CollectionViewController: UICollectionViewController 

init() 
    let collectionViewFlowLayout = UICollectionViewFlowLayout.init()
    collectionViewFlowLayout.sectionHeadersPinToVisibleBounds = true
    collectionViewFlowLayout.sectionFootersPinToVisibleBounds = true
    super.init(collectionViewLayout: collectionViewFlowLayout)


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


override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)


override func viewWillDisappear(_ animated: Bool) 
    super.viewWillDisappear(animated)


override func viewDidLoad() 
    super.viewDidLoad()
    // Register cell classes
    self.collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
    self.collectionView?.register(MyHeaderFooterClass.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier)
    self.collectionView?.register(MyHeaderFooterClass.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier)

    self.collectionView?.backgroundColor = UIColor.white

    self.collectionView?.dataSource = self
    self.collectionView?.delegate = self



// MARK: UICollectionViewDelegate
extension CollectionViewController 
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    DispatchQueue.main.async 
        self.view.endEditing(true)
        self.collectionViewLayout.invalidateLayout()
        self.collectionView?.layoutIfNeeded()
    



// MARK: - UICollectionViewDataSource
extension CollectionViewController 
override func numberOfSections(in collectionView: UICollectionView) -> Int 
    // #warning Incomplete implementation, return the number of sections
    return 1


override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    // #warning Incomplete implementation, return the number of items
    return 10


override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)

    switch indexPath.row % 2 
    case 0:
        cell.backgroundColor = UIColor.red
    case 1:
        cell.backgroundColor = UIColor.blue
    default:
        cell.backgroundColor = UIColor.gray
    

    return cell


override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView 
    let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier, for: indexPath)

    if kind == UICollectionView.elementKindSectionHeader 
        view.backgroundColor = UIColor.orange
    
    else  //footer
        view.backgroundColor = UIColor.green
    

    return view



// MARK: - Collection View Flow Layout Delegate
extension CollectionViewController: UICollectionViewDelegateFlowLayout 

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    return CGSize(width: collectionView.frame.width, height: 100)


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


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


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize 
    return CGSize(width: collectionView.frame.width, height: 100)


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize 
    return CGSize(width: collectionView.frame.width, height: 100)


MyHeaderFooterClass

import UIKit

class MyHeaderFooterClass: UICollectionReusableView 
let textField: UITextField = 
    let view = UITextField.init()
    view.placeholder = "enter text here"
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
()

override init(frame: CGRect) 
    super.init(frame: frame)

    self.addSubview(textField)
    self.setNeedsUpdateConstraints()


override func updateConstraints() 
    textFieldConstraints()
    super.updateConstraints()


private func textFieldConstraints() 
    NSLayoutConstraint(item: textField, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
    NSLayoutConstraint(item: textField, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0.0).isActive = true
    NSLayoutConstraint(item: textField, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0.0).isActive = true
    NSLayoutConstraint(item: textField, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true


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


【问题讨论】:

你看到我发布的答案了吗? 嘿对不起,是的,我做到了。这确实解决了页脚卡在页面中间的问题,但单元格本身并没有滑回以匹配它。所以无论如何我都必须实施不同的机制。感谢您的帮助,您确实回答了最初的问题,所以我现在会接受您的回答。 【参考方案1】:

来自文档:

函数 layoutIfNeeded()

使用此方法强制视图立即更新其布局

func invalidateLayout()

此方法使集合视图的布局本身无效,并且 立即返回。

解决方案:

查看您的代码,您已经在更新collectionViewLayout。因此,您无需再次强制更新collectionView。因此,仅删除 self.collectionView?.layoutIfNeeded() 即可解决您的问题。

如下修改你的代码:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    DispatchQueue.main.async 
        self.view.endEditing(true)
        self.collectionViewLayout.invalidateLayout()
        //self.collectionView?.layoutIfNeeded()
    

【讨论】:

以上是关于UICollectionViewController 页脚在 endEditing 后无法正确重新定位的主要内容,如果未能解决你的问题,请参考以下文章

UISearchController 像 Instagram 探索选项卡

无法使用自动布局将简单的自行调整大小的集合视图单元格调整为标签大小