UICollectionView перескакивает / прокручивается, когда вложенный UITextView начинает редактирование - PullRequest
0 голосов
/ 03 августа 2020

Мой первый вопрос о ТАК, так что потерпите меня. Я создал UICollectionViewController с заголовком и 1 ячейкой. Внутри ячейки находится табличное представление, внутри табличного представления имеется несколько ячеек stati c. Один из них имеет горизонтальный UICollectionView с ячейками, которые имеют UITextViews.

Проблема: При нажатии на UITextView представление коллекции прокручивается / перескакивает

Иллюстрация проблемы

Справа вы можете увидеть значения смещения по оси Y. При первом нажатии он меняется на 267 - высота заголовка. При последовательном нажатии опускается до 400 - самый низ. Это происходит независимо от того, что я пытался сделать.

Примечание: В моем приложении я использую IQKeyboardManager

Что я пробовал:

Полное отключение IQKeyboardManager и

  1. Нажатие на текстовое представление
  2. Замена его на собственные методы управления клавиатурой на основе старых ответов SO

Установите collectionView.shouldIgnoreScrollingAdjustment = true для:

  1. всех прокручиваемых представлений в V C
  2. Прокручиваемых представлений отдельных лиц

Примечание. Это свойство происходит из библиотеки IQKeyboardManager. и, насколько я понимаю, предполагается отключить смещение регулировки прокрутки.

Пытался полностью отключить прокрутку в viewDidLoad (), а также во всех других местах в этом V C. Я использовал:

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

Это мой первый раз, когда я проектирую сложную компоновку, используя в основном программный подход. Следовательно, возможно, я упустил что-то очевидное. Однако, несмотря на просмотр множества старых вопросов, я не смог найти решения. Приветствуются любые решения или рекомендации.

Изменить:

По запросу здесь приведены настраиваемые методы клавиатуры:

In 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 Ответ

0 голосов
/ 03 августа 2020

Проблема, которую я вижу, заключается в том, что вы вызываете configureScrollView(), когда ваш textView сфокусирован на textViewDidBeginEditing .

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. Также ваш safeArea был изменен при keyboardWillShow.
  2. Попробуйте отключить setCOntentOffset?
...