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

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

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

Problem Illustration

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

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

我尝试了什么:

完全禁用 IQKeyboardManager 并

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

设置collectionView.shouldIgnoreScrollingAdjustment = true为:

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

注意:此属性源自 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()
    }
}

【问题讨论】:

  • 您能向我们展示您的自定义键盘管理吗?你在那里处理滚动?
  • 我已将这些添加到问题的底部

标签: ios swift uicollectionview uikit uikeyboard


【解决方案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 --> 这就是你的收藏视图跳跃的原因。

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

【讨论】:

  • 不幸的是,这不是跳转的原因。即使没有任何这些方法,它也会跳跃。据我所知,它们根本没有效果。我尝试更改上述属性无济于事。收到通知后我也直接调用了configureScrollView(),收效甚微。
猜你喜欢
  • 2020-05-06
  • 2012-05-14
  • 1970-01-01
  • 2011-10-03
  • 2017-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多