5

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.

nikmin
  • 1,803
  • 3
  • 28
  • 46

3 Answers3

4

Drag TableView to return it to the min height and then tap on a cell, nothing is happening because:

  • When you drag to expand the table view to full screen, scrollView.isDecelerating is true. So the code inside scrollViewDidScroll method will run.

  • But when you drag TableView to return it to the min height, scrollViewDidScroll is false. So the code inside scrollViewDidScroll method won't run. It's make the first tap do nothing.

Simply remove guard !scrollView.isDecelerating else { return } from scrollViewDidScroll. You will tap cell normally after drag TableView down.

But you need change logic a little, animation will go wrong after remove above line.

Hope it can help you ;)

trungduc
  • 11,926
  • 4
  • 31
  • 55
  • As far as I could test, the problem is not in `scrollViewDidScroll` because it's not called at all after I release the dragging gesture. So when dragging down to collapse the view, `scrollViewWillEndDragging` is the last call after I lift my finger off the screen and `scrollViewDidScroll` is not called at all after it. The `guard !scrollView.isDecelerating` is needed just in case of normal table view scroll when the height constraint is not being changed. – nikmin Nov 07 '17 at 08:23
  • @nikmin you just need to comment `guard !scrollView.isDecelerating else { return }` and try to click cell after drag down. Let try first. I'm sure you can click cell. – trungduc Nov 07 '17 at 08:25
  • Why do you think I haven't tried? :) I'm banging my head around this for too long and I've tried everything I could think of. Dragging down works only if you drag it all the way down beneath the min height and then release. But if you swipe and release before it reaches the min height, but it continues moving to reach the min height (the animation in `scrollViewWillEndDragging`), it's the same thing. And I tried this with commenting the `guard` and changing some things in the logic in `scrollViewDidScroll`. But nothing helps :( – nikmin Nov 07 '17 at 08:38
  • So sorry @nikmin, my mistake. Can't remember why it works when i post the answer. I will try to investigate more to help you. – trungduc Nov 07 '17 at 09:40
  • thank you very much for your help, but as I posted in my answer below, we decided to change the behaviour a bit to avoid this bug. Spend too much time trying to find a solution :) If you happen to run into a solution for this thing, I would be really happy to hear and try it – nikmin Nov 07 '17 at 09:58
1

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

nikmin
  • 1,803
  • 3
  • 28
  • 46
0

It looks like incorrectly set content offset. The first touch cancels incorrect position(unscroll), this is why it is not registered. It might be better if we got an access to the full code to check it, because I can't tell you where exactly the problem lies, but I guess it is in method scrollViewDidScroll.

Luzo
  • 1,336
  • 10
  • 15
  • The whole code of `scrollViewDidScroll` and everything else connected to the table view is in the code snippet in the question. But I will try to create a small repo with just this thing. If you have anything else on mind please share, I'm willing to try anything :) – nikmin Nov 03 '17 at 09:58
  • I added the repo, please have a look – nikmin Nov 03 '17 at 14:10
  • Unfortunately, I only came to this... didHighlight is called if it helps you. if nobody will find a solution to this, be you I would try to implement it with custom gesture recognizer, that is worth a shot – Luzo Nov 03 '17 at 20:24