键盘在UICollectionViewController中打破布局

时间:2017-04-30 08:36:13

标签: ios swift uicollectionview uicollectionviewlayout

我有一个水平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) { }
  }
}

CollectionViewController:

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

enter image description here

非常感谢

5 个答案:

答案 0 :(得分:3)

首先,我必须给出我的两分钱,故事板很棒:)

对于此用例,您可能不想使用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类):

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

最后,您可以删除CollectionViewController,因为它已被替换。

PS你也可能想要1)扩展ViewController以符合UICollectionViewDelegateFlowLayout和2)使collectionView全局。

答案 1 :(得分:1)

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

将以下内容添加到CollectionViewController类:

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

答案 2 :(得分:1)

演示链接:https://github.com/harshilkotecha/UIScrollViewWhenKeyboardAppearInSwift3

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

第1步:将代理人提交给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)
    }

答案 3 :(得分:0)

我有一种情况,我有一个视图控制器,该控制器带有在屏幕底部运行的嵌入式UICollectionViewController。当键盘出现时,它将更改嵌入式控制器的contentInset。我不需要这样做-键盘可以(部分)掩盖它。我最终要做的是重置嵌入式UICollectionViewController内部的viewWillLayoutSubviews()中的contentInset :(快速4)

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

答案 4 :(得分:-1)

我发现您的代码异常之处在于您将文本字段放在容器视图中的单元格内。我不确定你打算在容器视图中添加什么...但是你的文本框对我来说有点奇怪。

现在已经说过......你遇到的问题是:当键盘显示时,系统有两个相互竞争的目标。首先,它尝试滚动集合视图的内容,以便文本字段保持可见。这给了大跳。然后它试图调整集合视图的大小以使其脱离键盘的方式,但由于流布局设置它真的很混乱。如果你查看日志,你会看到很多:

  

2017-05-02 23:39:30.671 automaticPush [31629:1628527]的行为   UICollectionViewFlowLayout未定义,因为:2017-05-02   23:39:30.671 automaticPush [31629:1628527]项目高度必须小于   比UICollectionView的高度减去section insets top   和底部值,减去内容插入顶部和底部值。   2017-05-02 23:39:30.672 automaticPush [31629:1628527]相关   UICollectionViewFlowLayout实例是,它附加到;   layer =; contentOffset:{0,0}; contentSize:   {3750,667}>集合视图布局:。 2017-05-02 23:39:30.672 automaticPush [31629:1628527]   做一个符号断点   UICollectionViewFlowLayoutBreakForInvalidSizes来捕获它   调试器。

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

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