键盘打破了 UICollectionViewController 中的布局

Keyboard breaks layout in UICollectionViewController

我有一个水平的 UICollectionViewController,其中每个单元格的底部都包含一个 UITextView。当我在 UITextView 内部点击时,当键盘出现时,CollectionView 的高度减少了 260 点(我注意到这是键盘的高度)然后增加了 130 点,所以最终高度比期望的低 130 点。

你知道框架为什么会以这种方式变化吗?

我在下面包含了最相关的部分,或者您可以在此处找到测试项目:https://github.com/johntiror/testAutomaticPush/tree/master

UIViewController(简单地启动 CollectionViewController):

class ViewController: UIViewController {
  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    let layout = UICollectionViewFlowLayout()
    layout.itemSize = view.bounds.size
    layout.scrollDirection = .horizontal
    layout.minimumLineSpacing = 0
    let fsPicVC = CollectionViewController(collectionViewLayout: layout)
    self.present(fsPicVC, animated: true) { }
  }
}

集合视图控制器:

class CollectionViewController: UICollectionViewController {
  override func viewDidLoad() {
    super.viewDidLoad()

    self.collectionView!.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")                
  }

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

    return cell
  }
}

非常感谢

我发现您的代码不寻常的地方在于,您将文本字段放在容器视图的单元格中。我不确定您还打算在那个容器视图中放入什么...但是让您的文本框放在那里对我来说有点奇怪。

话虽如此...您遇到的问题是:当键盘显示时,系统有两个相互竞争的目标。首先,它试图滚动集合视图的内容,以便文本字段保持可见。这给了很大的跳跃。然后它试图调整集合视图的大小以使其不妨碍键盘,但由于流布局设置,它变得非常混乱。如果您查看日志,您会看到很多:

2017-05-02 23:39:30.671 automaticPush[31629:1628527] The behavior of the UICollectionViewFlowLayout is not defined because: 2017-05-02 23:39:30.671 automaticPush[31629:1628527] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values. 2017-05-02 23:39:30.672 automaticPush[31629:1628527] The relevant UICollectionViewFlowLayout instance is , and it is attached to ; layer = ; contentOffset: {0, 0}; contentSize: {3750, 667}> collection view layout: . 2017-05-02 23:39:30.672 automaticPush[31629:1628527] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.

由于系统拼命尝试协调集合视图的约束与保持文本视图可见的尝试

总而言之,我建议您将文本视图放在集合视图之外。

首先,我要说故事板很棒 :)

对于此用例,您可能不想使用 CollectionViewController。如果您决定使用它,我还发布了另一个答案。这是将 CollectionView 移动到 ViewController 的最快方法。这解决了您的问题,但不考虑自动布局。

1) 替换 ViewController 中的这些行:

let fsPicVC = CollectionViewController(collectionViewLayout: layout)
self.present(fsPicVC, animated: true) { }

let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.dataSource = self
view.addSubview(collectionView)

2) 将此添加到 ViewController 的最底部( ViewController class 之外):

extension ViewController: UICollectionViewDataSource {

  func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
  }

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

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

    // Configure the cell

    return cell
  }
}

最后,您可以删除收藏集ViewController,因为它现在已被替换。

PS 您可能还想 1) 扩展 ViewController 以符合 UICollectionViewDelegateFlowLayout 和 2) 使 collectionView 全局化。

如果你想使用 CollectionViewController,这里有一个可能的解决方案(如果你想切换到通用 ViewController 请参阅我的其他答案)。

将以下内容添加到您的收藏中ViewController class:

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

    // Try commenting out this line to see the animation. It's included so the user doesn't see the fade effect
    collectionView?.backgroundColor = UIColor.orange

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    NotificationCenter.default.removeObserver(self)
}

func keyboardWillChangeFrame(notification: NSNotification) {

    guard let info = notification.userInfo else {return}
    guard let keyboardFrameEndValue = info[UIKeyboardFrameEndUserInfoKey] as? NSValue else {return}
    let keyboardFrameEnd = keyboardFrameEndValue.cgRectValue

    var itemSize = view.bounds.size
    itemSize.height = keyboardFrameEnd.origin.y

    guard let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout else {return}
    layout.itemSize = itemSize

    collectionView?.setCollectionViewLayout(layout, animated: true)
}

演示 link : https://github.com/harshilkotecha/UIScrollViewWhenKeyboardAppearInSwift3

当你有多个 textview 时,它是如此困难,所以最好的解决方案 ->

第 1 步:将 Delegate 交给 UITextFieldDelegate

class ScrollViewController: UIViewController,UITextFieldDelegate {

第 2 步:创建新的 IBOutlet 但不连接故事板中的任何文本字段。

//  get current text box when user Begin editing
    @IBOutlet weak var activeTextField: UITextField?

第 3 步:当用户关注文本字段对象时编写这两个方法传递引用并存储在 activeTextField 中。

// get current text field
    func textFieldDidBeginEditing(_ textField: UITextField)
    {
        activeTextField=textField;
    }
    func textFieldDidEndEditing(_ textField: UITextField)
    {
        activeTextField=nil;
    }

第 4 步:在 viewdidload() setNotificationKeyboard

中设置通知
override func viewWillAppear(_ animated: Bool) {
            // call method for keyboard notification
            self.setNotificationKeyboard()
    }

    // Notification when keyboard show
    func setNotificationKeyboard ()  {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: .UIKeyboardWillHide, object: nil)
    }

第 5 步:键盘出现和消失的两种方法。

func keyboardWasShown(notification: NSNotification)
    {
        var info = notification.userInfo!
        let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
        let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height+10, 0.0)
        self.scrollView.contentInset = contentInsets
        self.scrollView.scrollIndicatorInsets = contentInsets
        var aRect : CGRect = self.view.frame
        aRect.size.height -= keyboardSize!.height
        if let activeField = self.activeTextField
        {
            if (!aRect.contains(activeField.frame.origin))
            {
                self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
            }
        }
    }
// when keyboard hide reduce height of scroll view


 func keyboardWillBeHidden(notification: NSNotification){
        let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0,0.0, 0.0)
        self.scrollView.contentInset = contentInsets
        self.scrollView.scrollIndicatorInsets = contentInsets
        self.view.endEditing(true)
    }

我遇到过这样的情况,我有一个视图控制器,在屏幕底部有一个嵌入式 UICollectionViewController 运行。当键盘出现时,它会更改嵌入式控制器的 contentInset。在我看来没有必要这样做 - 键盘(部分)掩盖它是可以的。我最终做的是在嵌入式 UICollectionViewController 中重置 viewWillLayoutSubviews() 中的 contentInset: (Swift 4)

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    collectionView?.contentInset = UIEdgeInsets.zero
}