在endEditing之后,UICollectionViewController页脚没有正确地重新定位自己

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在endEditing之后,UICollectionViewController页脚没有正确地重新定位自己相关的知识,希望对你有一定的参考价值。

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

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

在下面的图像中,标题是橙色,页脚是绿色,有10个单元格交替红色和蓝色。

实际行为(当键盘解散时未在collectionView底部附近滚动时发生)actual behavior

期望的行为(当您在键盘解散时在collectionView的底部或附近滚动时发生)desired behavior

我知道当键盘出现并消失时,有一些方法可以通过Notifications实现这一点,但我更愿意只使用默认的UICollectionViewController行为。我缺少一些配置或设置吗?

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

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")
}
}
答案

来自文档:

func 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()
    }
}

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

在 UIView 上调用 endEditing

使用 tableviewheader 中的 UITextField 在 self.view.endEditing 上崩溃

在表格视图中使用带有文本字段的 endEditing 时崩溃

resignFirstResponder/endEditing 导致访问错误

使用“endEditing”时出现“NSInvalidArgumentException”错误

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