Search code examples
iosswiftuitableviewuiscrollviewuiscrollviewdelegate

UITableView tap not working first time after constraints change


IMPORTANT: My problem is not that I'm implementing didDeelectRowAt instead of didSelectRowAt. Already checked that :)

I have a UITableView that is shown on part of the screen in a modally presented view controller. When the user is dragging it resizes to full screen and back to some defined min height. I'm doing this by implementing the following methods from the UIScrollViewDelegate:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    guard !scrollView.isDecelerating else { return }

    let contentOffset = scrollView.contentOffset.y
    if tableViewHeightConstraint.constant < view.frame.height && contentOffset > 0.0 {
        tableViewHeightConstraint.constant = min(contentOffset + tableViewHeightConstraint.constant, view.frame.height)
        scrollView.contentOffset.y = 0.0
        return
    }

    if tableViewHeightConstraint.constant > minViewHeight && contentOffset < 0.0 {
        tableViewHeightConstraint.constant = max(tableViewHeightConstraint.constant + contentOffset, minViewHeight)
        scrollView.contentOffset.y = 0.0
    }
}

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    // Here I have some calculations if depending the dragging end position and the velocity the end size should be full screen or `minViewHeight`
    // After calculating what the end size should be I'm animating the size change
    heightConstraint.constant = newConstraintHeight
    UIView.animate(withDuration: TimeInterval(calculatedAnimationDuration), delay: 0.0, options: .curveEaseOut, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)
}

Everything about the resizing and the scrolling works fine, but there is a problem that I cannot figure out why it's happening. It's the following:

  • When the view controller with the table view is shown for the first time with the min height and I tap on a cell it works fine.
  • If I drag to expand the table view to full screen height and tap on a cell, again it works fine.
  • If I drag to expand the table view to full screen height and then drag again to return it to the min height and then tap on a cell, nothing is happening, no UIScrollViewDelegate or UITableViewDelegate method is called at all. If I tap once more on a cell everything works fine.

One thing that I noticed is that after dragging the table view back to the min height the scroll indicator does not hide. On the first tap it hides, and on the second tap the didSelectRowAt is called.

UPDATE:

Here is a test repo for the problem: https://github.com/nikmin/DragTest

Please don't mind if the dragging doesn't work perfectly, I just put something so anyone can try it out, but I think the problem is easily reproducible.

Also one more thing... If you drag from full size all the way to the bottom so the table view reaches min height and you continue dragging so the content offset is < 0 and the you release, the problem is not happening.


Solution

  • After trying to figure out a solution to this without result, we (me and a UX designer) decided to change the behaviour a bit.

    So in the real scenario in the app I'm implementing this in, the table view is inside another view that has also a title label and some other views above the table view. We decided to add a pan gesture recognizer to this root view and disable the scrolling of the table view when the view has the min size. This way the pan gesture recognizer will take over whenever the user tries to drag anywhere inside the view (including the table view), so the expanding of the view works. And the tap in the cell still works.

    When the view has the max height the table view scroll is enabled so the user can scroll. The downside of this approach is that when the user scrolls to the top of the table view and continues scrolling the view will not decrease the size. But he still has the option to drag it down by dragging any of the views above the table view. When dragging down in this way, only the size of the table view changes, and the content offset isn't, which is the root of the problem (changing both at the same time).