UIKit collection view, strange behavior (accessing/scrolling cells) - ios

So I recently implemented a collection view in my app, and I got a bug that I can't seem to solve, searched it and saw no threads about it.
If I have my cursor/finger over the cells i can't scroll through my collection view i need select a "empty" area to scroll.
Second strange Behavior I came across is that I can't directly touch a cell. I need some sort of swipe gesture over it to trigger the code when a cell is selected.
If I go to my collection view on my storyboard and select Delays Content Touches and Cancellable Content Touches in the scrollview section, the collection view scrolls just fine but if I put my finger/cursor over a cell with these option enabled I can't access any cells anymore.
This completely confuses me.
and thank you for reading/considering this thread.

Let's see what your two properties do.
delaysContentTouches: If the value of this property is true, the scroll view delays handling the touch-down gesture until it can determine if scrolling is the intent. If the value is false , the scroll view immediately calls touchesShouldBegin(_:with:in:). The default value is true.
canCancelContentTouches: If the value of this property is true and a view in the content has begun tracking a finger touching it, and if the user drags the finger enough to initiate a scroll, the view receives a touchesCancelled(_:with:) message and the scroll view handles the touch as a scroll. If the value of this property is false, the scroll view does not scroll regardless of finger movement once the content view starts tracking.
First, you set delaysContentTouches to false. So the scrollview immediately calls the content view's touch handling methods, allowing it to handle the touch. Obviously, the scroll view won't start scrolling right away because of this, even if you drag.
Second, you also set canCancelContentTouches to false. But if the scroll view isn't allowed to "take over" touches that the content already handles (by cancelling them), it is never able to start scrolling later on either. So if your touch hits a content view, there is no possible way for the scroll view to start scrolling: it isn't allowed to scroll right away because it isn't allowed to delay the content touches, and it can't start scrolling later because it can't cancel the content touches.
I don't know what happens within your cells, not sure what code you put in there. However, you should probably allow your tableview to both delay touches (that means that your cell won't handle swipes that are cancelled immediately anyway because they were intended to be scroll gestures), and to cancel content touches (that means that when you touch down and don't release, you can still start a scroll gesture after a cell became highlighted).

i had the same problem when touching a cell, the problem was that I'm using more than one UIGesture without adding ".cancelsTouchesInView = false" for each one
so if you're using a UIGesture just add Your_Gesture.cancelsTouchesInView = false
and you should be able to access your cells

Related

Trouble handling interactive state for my UIView

I'll start with my view hierarchy:
ParentView -----> TopBarView (UIView) and CollectionView (UICollectionView)
Scenario:
1) My TopBarView is pinned to the top of ParentView with a height constraint. It is set to handle tap and long-press gestures. Also, my TopBarView's width is equal to that of CollectionView
2) My CollectionView is pinned to the top of the ParentView too! And it has a top padding equal to that of the TopBarView's height. So visually, CollectionView's content starts right below the TopBarView. Also, whenever the CollectionView is scrolled, my TopBarView's constraint is adjusted based on my CollectionView's offset, to visually make it look like it's scrolling along and the inverse is what I'm working on...
My requirement:
I'm trying to make the TopBarView scrollable, so that the CollectionView scrolls along with it too
What I've tried:
To establish scroll behaviour, I'm simply setting the isUserInteractionEnabled property of TopBarView to false, so that the touch events get passed to the underlying CollectionView, and it works as intended!
The Problem:
Since I already support tap gestures and long-press gestures for my TopBarView, I cannot permanently set it to be irresponsive. I have to toggle between the gestures that the user performs i.e: To accept interactions where the user taps or long-presses the view, but deny interactions when user tries to scroll the view(so that the touch events get passed to CollectionView)
All of the above is mostly just for the context. Here's my problem put simply:
My view should respond to taps and long-presses. But when scrolled, it should quit responding and pass the scroll to underlying collection view.
There's got to be something probably trivial that I'm missing out on, and I'm here. TYIA!

Swift: How to override a method for a scrollView?

I'm trying to override the touchesShouldCancel method for a subclass of a scrollview.
This is my subclass code:
class MyScrollView: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool {
print("Works")
return false
}
}
I have made a scrollview of this class and it scrolls it just doesn't ever print "works". I would like to add some functionality to this but it's never being called. According to the apple documentation:
'The scroll view calls this method just after it starts sending tracking messages to the content view.'
This makes me believe the method should be called whenever I am scrolling and I can't figure out why it's not. Cheers if anyone has any ideas.
From the docs:
Because a scroll view has no scroll bars, it must know whether a touch signals an intent to scroll versus an intent to track a subview in the content. To make this determination, it temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement. If the timer fires without a significant change in position, the scroll view sends tracking events to the touched subview of the content view. If the user then drags their finger far enough before the timer elapses, the scroll view cancels any tracking in the subview and performs the scrolling itself. Subclasses can override the touchesShouldBegin(_:with:in:), isPagingEnabled, and touchesShouldCancel(in:) methods (which are called by the scroll view) to affect how the scroll view handles scrolling gestures.
Do this experiment.
Add a subview to your scrollview that intercepts touches (think UIButton, not UILabel).
Make sure your content is actually scrollable
Press down on the button and wait for just a moment, then start sliding your finger.
When you tap down on the scroll view and start dragging right away, it assumes you want to scroll the scroll view and not interact with the button.
However when you do that slight pause, it wants to determine if you're trying to interact with the scroll view or with the button.
touchesShouldCancel will then fire.

UIScrollView that can scroll, but not from user interaction

I want to have a scrollview that has 2 views on it. Both views fill up the screen, and when you push a button on one view it uses scrollRectToVisible to move to the other view.
I want the user to not be able to scroll it with their touch though, so disabling scrolling doesn't work. Obviously i could have scroll become re-enabled when the user touches the button than lock it again afterwards, but then the user could grab or interact while it's unlocked in the scroll animation.
Any ideas?
Woops. As it turns out, against some/lots of logic, you can actually disable scroll on the scrollview, but scrollRectToVisible still works, it'll only stop it working if you disable user interaction - i'me guessing because the view with the button inherits from the scollview, so the button wasn't actually working at all.

Collection view not scrolling when interacting on a cell

My collection view controller is not responding when swiping on a cell(touch begin in a point on the cell). But it interacts when swipe began a point such as spaces between the cells.
It was previously scrolling vertically, not touch gestures(there was once but i deleted it and nothing really changed) and recognizers in collection view and cell. The interesting part is collection view's didSelectCellAtRow works perfectly fine when interact on cell but not responsive to scrolls.
You can only scroll when swiping from the spaces between the cells or any point in collection view that is not containing cell.
Any thoughts?
Are you sure the touch-down event is passed to the table view (scroll view) correctly?
Make sure the cells don't have a custom touch-down event (e.g. tap gesture recognizer, or touchesBegan method without calling the super inside) and also make sure the collection view delays content touches, it's a common mistake to uncheck it and forget it, and then try to understand why a view is not handling touches properly, but its children are.

ios move touch event between two uiscrollview

I'm building an iOS layout which consists of a UITableView and a UIScrollView. The UIScrollView is inside a table cell of the UITableView and can be scrolled both horizontally and vertically. The diagram below shows this situation. If the user begins scrolling down/up on the UIScrollView the scrolling event should trigger setContentOffset of the table view, and not setContentOffset for the scroll view while the top of the scroll view will be on the dotted line (it's constant height). Then a scrolling touch event should trigger setContentOffset for the scroll view, not for the table view.
In another case: When the user starts scrolling on the table view, it should trigger setContentOffset for the table view, until the scroll view reaches the dotted line. Then the scroll view should handle setContentOffset.
My problem is how to transfer touch events between the table view and the scroll view during one sliding action.
This sounds like one of those cases where you want something quite specific and custom. So trying to do something clever with the gesture recognizers won't be enough.
The main problem is that the ways you can control gesture recognizers such as with gestureRecognizer:shouldReceiveTouch: and gestureRecognizerShouldBegin: only affect the start of the gesture (or for new touches, not ongoing ones), but you want a single ongoing gesture to transition between controlling each view. So for this reason I think you will need to place a large transparent view over your entire screen with a pan gesture recognizer on it and in your handlePan method decide which view you want to adjust and then call setContentOffset directly on that view. You can use the translation of the pan recognizer and the existing content offset to calculate the new one. I know this isn't very elegant, but I can't think of another way to achieve the effect you want.
I'm not sure if this is going to work, but you could try doing something like this:
Option
self.scrollView.panGestureRecognizer = self.tableView.panGestureRecognizer;
Option
[self.scrollView addGestureRecognizer:self.tableView.panGestureRecognizer];
Option
[self.tableView.panGestureRecognizer requireGestureRecognizerToFail:self.scrollView.panGestureRecognizer];

Resources