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

时间:2019-02-26 19:50:08

标签: ios swift uicollectionview uicollectionviewlayout uicollectionviewflowlayout

我有一个简单的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文件中删除主要的故事板条目。

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 个答案:

答案 0 :(得分:1)

摘自文档:

  

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