当嵌套的 UITextView 开始编辑时,UICollectionView 跳转/滚动

Posted

技术标签:

【中文标题】当嵌套的 UITextView 开始编辑时,UICollectionView 跳转/滚动【英文标题】:UICollectionView jumps/scrolls when a nested UITextView begins editing 【发布时间】:2020-08-03 06:37:17 【问题描述】:

关于 SO 的第一个问题,请耐心等待。我创建了一个 UICollectionViewController,它有一个标题和 1 个单元格。单元格内部是一个表格视图,表格视图内部有多个静态单元格。其中之一有一个水平的 UICollectionView,其中包含具有 UITextViews 的单元格。

问题:点击 UITextView 时,集合视图会滚动/跳转

Problem Illustration

在右侧,您可以看到 y 偏移值。在第一次点击时,它变为 267 - 标题高度。连续点击它会下降到 400 - 最底部。无论我尝试做什么,都会发生这种情况。

注意:在我的整个应用程序中,我都在使用 IQKeyboardManager

我尝试了什么:

完全禁用 IQKeyboardManager 并

    点击文本视图 将其替换为基于旧 SO 答案的自定义键盘管理方法

设置collectionView.shouldIgnoreScrollingAdjustment = true为:

    VC 中的所有可滚动视图 个人可滚动视图

注意:此属性源自 IQKeyboardManager 库,据我了解,它应该禁用滚动调整偏移。

尝试在 viewDidLoad() 以及此 VC 中的所有其他位置完全禁用滚动。我用过:

collectionView.isScrollEnabled = false
collectionView.alwaysBounceVertical = false  

值得注意的是,我尝试在文本 viewDidBeginEditing 以及自定义键盘管理方法中禁用滚动。

我的代码:

主 UICollectionView 及其一个单元格在情节提要中创建。其他一切都是以编程方式完成的。这是决定唯一单元格大小的流布局函数:

extension CardBuilderCollectionViewController: UICollectionViewDelegateFlowLayout 
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: 
UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    let height = view.frame.size.height
    let width = view.frame.size.width
    return CGSize(width: width * cellWidthScale, height: height * cellHeigthScale)
 


另外,collectionView.contentInsetAdjustmentBehavior = .never

该单元格子类中的 TableView 是这样创建的:

let tableView: UITableView = 
    let table = UITableView()
    table.estimatedRowHeight = 300
    table.rowHeight = UITableView.automaticDimension
    return table
()

和:

    override func awakeFromNib() 
       super.awakeFromNib()     
        dataProvider = DataProvider(delegate: delegate)
        addSubview(tableView)
        tableView.fillSuperview() // Anchors to 4 corners of superview
        registerCells()
        tableView.delegate = dataProvider
        tableView.dataSource = dataProvider
    

表格视图中的单元格都是GeneralTableViewCell类的子类,它包含以下确定单元格高度的方法:

var cellHeightScale: CGFloat = 0.2 
    didSet 
        setContraints()
    


private func setContraints() 
    let screen = UIScreen.main.bounds.height
    let heightConstraint = heightAnchor.constraint(equalToConstant: screen*cellHeightScale)
    heightConstraint.priority = UILayoutPriority(999)
    heightConstraint.isActive = true

表格视图中嵌套单元格(带有TextView)的高度使用与主视图中唯一一个单元格相同的方法确定。

最后使用自定义 FlowLayout 创建标题:

class StretchyHeaderLayout: UICollectionViewFlowLayout 
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 
        let layoutAttributes = super.layoutAttributesForElements(in: rect)
        
        layoutAttributes?.forEach( (attribute) in
            if attribute.representedElementKind == UICollectionView.elementKindSectionHeader  && attribute.indexPath.section == 0 
                
                guard let collectionView = collectionView else  return 
                
                attribute.zIndex = -1
                let width = collectionView.frame.width
                let contentOffsetY = collectionView.contentOffset.y
                print(contentOffsetY)
                if contentOffsetY > 0  return 
                let height = attribute.frame.height - contentOffsetY
                attribute.frame = CGRect(x: 0, y: contentOffsetY, width: width, height: height)
            
        )
        return layoutAttributes
    
    
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool 
        return true
    

这是我第一次使用主要是程序化方法设计复杂的布局。因此,我可能错过了一些明显的东西。但是,尽管浏览了许多旧问题,但我无法找到解决方案。任何解决方案或指导表示赞赏。

编辑:

根据要求,这里是自定义键盘方法:

在 viewDidLoad() 中

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

然后:

  var scrollOffset : CGFloat = 0
    var distance : CGFloat = 0
    var activeTextFeild: UITextView?
    var safeArea: CGRect?

    
    @objc func keyboardWillShow(notification: NSNotification) 
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue 
            var safeArea = self.view.frame
            safeArea.size.height += collectionView.contentOffset.y
            safeArea.size.height -= keyboardSize.height + (UIScreen.main.bounds.height*0.04)
            self.safeArea = safeArea
        
    
    
    private func configureScrollView() 
        
        if let activeField = activeTextFeild 
            if safeArea!.contains(CGPoint(x: 0, y: activeField.frame.maxY)) 
                print("No need to Scroll")
                return
             else 
                distance = activeField.frame.maxY - safeArea!.size.height
                scrollOffset = collectionView.contentOffset.y
                self.collectionView.setContentOffset(CGPoint(x: 0, y: scrollOffset + distance), animated: true)
            
        
        // prevent scrolling while typing
        
        collectionView.isScrollEnabled = false
        collectionView.alwaysBounceVertical = false
    
    
    @objc func keyboardWillHide(notification: NSNotification) 
            if distance == 0 
                return
            
            // return to origin scrollOffset
            self.collectionView.setContentOffset(CGPoint(x: 0, y: scrollOffset), animated: true)
            scrollOffset = 0
            distance = 0
            collectionView.isScrollEnabled = true
    

最后:

//MARK: - TextViewDelegate

extension CardBuilderCollectionViewController: UITextViewDelegate 
    
    func textViewDidBeginEditing(_ textView: UITextView) 
        self.activeTextFeild = textView
        configureScrollView()
    

【问题讨论】:

您能向我们展示您的自定义键盘管理吗?你在那里处理滚动? 我已将这些添加到问题的底部 【参考方案1】:

我可以看到的问题是,当您的 textView 聚焦在 textViewDidBeginEditing 时,您调用了 configureScrollView()

distance = activeField.frame.maxY - safeArea!.size.height
scrollOffset = collectionView.contentOffset.y
self.collectionView.setContentOffset(CGPoint(x: 0, y: scrollOffset + distance), animated: true)

你打电话给collectionView.setContentOffset --> 这就是你的收藏视图跳跃的原因。

    请检查您的distance 计算是否正确。另外,您的safeAreakeyboardWillShow 时被修改。 尝试禁用setCOntentOffset

【讨论】:

不幸的是,这不是跳转的原因。即使没有任何这些方法,它也会跳跃。据我所知,它们根本没有效果。我尝试更改上述属性无济于事。收到通知后我也直接调用了configureScrollView(),收效甚微。

以上是关于当嵌套的 UITextView 开始编辑时,UICollectionView 跳转/滚动的主要内容,如果未能解决你的问题,请参考以下文章

UITextView如何将光标保持在键盘上方

UITextView从文本的底部或中间开始

UITextView 开始编辑时,文字没有左上角对齐解决办法

单击另一个 UITextView 时结束编辑

在 UITextView 编辑时移动 UIView

ios uitextview 怎么设置编辑时弹出键盘