Detect touches under a UIScrollView - ios

I built a project that has a mapView (Google Maps), tableView and a UIScrollView.
The tableView is on the bottom and when the app starts you see a really small part of it and the most of it is out of the screen (on the bottom). When the user scrolls the tableView is revealed and going up from the bottom until it covers all of the screen. It's all working but I have one small problem: I want that the user will be able to touch the map (zoom, move, etc.) but the UIScrollView is on top of the map and when the user tries to interact with the map the scrollView detects a scroll and the map doesn't get a touch event. Is there any way that the UIScrollView wouldn't detect the touch on the area of the map? The scrollView should cover all of the screen for the tableView detection (or maybe not and there is another way?) and I don't want the map to be a subclass of the scrollView because it will go up with the table when the user scrolls (I want the tableView to go on top of the map and not with it).
Thanks!

Going off of my comment. You will need to set the top space to superview constraint on the scroll view. Create an IBOutlet for the constraint and a float variable for the value of the constraint and also connect it to your top space to superview constraint that you will create in interface builder on the scrollview.
in your .h:
IBOutlet NSLayoutConstraint *topSpace;
float topSpaceValue;
Then update that constraint using this:
topSpace.constant = topSpaceValue;
You can detect scrolling in the scrollview with this method and set the topSpace constraint:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
topSpace.constant = topSpaceValue;
}
Now, the actual logic of detecting how fast the scrollview is moving and what value to set for topSpaceValue is up to you to figure out. Also you will need to detect what direction the scroll view is moving to increase or decrease the value of topSpaceValue.
This looks like the link you need for detecting the speed: iPhone UIScrollView Speed Check
So according to the speed, set the constraint. And you will probably want to check for a minimum value for the constraint, like 0, so the scrollview doesn't scroll off the top of the screen.

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!

How to create up/down swipeable UITableView?

I have a UITableView in the bottom of UIViewController and tableview height is 100 point now.
The tableview has 20 cells and tableview's header view is 100 point. And I've added a up UISwipeGestureRecognizer and a down UISwipeGestureRecognizer in table view header.
Now I want to change the tableview height constraint constant to 400 in up gesture action and change the tableview height constraint constant to 100 in down gesture action.
Now the problem is gesture recognizer isnt working in tableview
header when tableview scroll is enabled.
If tableview scroll is disabled then the gesture recognizer is
working. But unable to view all cells once the tableview height is
changed.
Here's a different approach. Don't use swipe gesture recognizers at all.
Instead, make the table always the full 400 points tall. Set its contentInset.top to 300. This will allow the table view to scroll so that only its top 100 points of content are visible at the bottom of the screen. Specifically, the table view will allow its contentOffset.y (which is its vertical scrolling position) to go down to -300 (instead of only down to 0). The table's content always starts at y = 0, so when the table's contentOffset.y is -300, only the top 100 points of its content are visible. As the contentOffset.y increases, more of its content becomes visible.
Then, override the table view's point(inside:withEvent:) method to return true only for points with y >= 0. This means the table will ignore (pass through) touches above its content when its content is scrolled so only the top 100 points are visible.
This is the final effect for a small table:
or for a big table:
You can find a detailed explanation (in Objective-C) and a link to the full test project (also in Objective-C) in this answer.
As it may help others, here is the simplest possible solution to this type of problem.
Extremely simple solution -
no tricks, nothing fancy -
don't have a "table view header".
Just make a new UIViewController
class TableWithRedTop: UIViewController
that has ...
[ red area .. swipe detection ]
[ main area - the table view]
Then simply put 'TableWithRedTop' inside a container view.
(Container view tutorial if you need one.)
In your MainView just have a call
func toggleTableHeight
When 'TableWithRedTop' gets a swipe, have one line of code to call toggleTableHeight in MainView
Note that in toggleTableHeight you can easily animate the height change, tilt it on an angle, duplicate it or do anything you want, as you're using a container view in MainView.
I'm going to make another suggestion that may solve the issue for you.
It could be what you want is an:
"self-expanding" table...
First implement the following with no animations, for simplicity.
You have two heights for the table, small and large.
Start the table on height small.
Remarkably, you only have to implement these two rules: just two simple lines of code:
Any time the user is scrolling upwards - in fact, change to "height large".
Any time the user is scrolling downwards, and, you are at the top position of the table (i.e. you can see cell #1) in fact change to "height small".
It's one of those things that is "so simple, it's hard to believe it works!"
It's sometimes referred to as a "pull-up table" I think.
(Note. If you're not familiar with detecting when the user is scrolling, fortunately it is trivial - code shown here for example.)
Set both swipe gesture’s cancelsTouchesInView to true and make sure that the gestures are added directly to the header and not the tableView.
That should do the trick, but so should adding a view with the gesture recognizes to the container view and setting it’s constraints to match the tableView’s top, left, and right constraints and setting its height to 100.

Animate objects when UIScrollView scrolls

I have a UIScrollView which has two pages and only scroll horizontally.
The scrolling and paging is controlled using a UIPageControl. I have placed a UIImageView on the scrollView which contains an image of an iPhone (shown in red in the image below) that says Hello inside page 1.
I wanted to animate the UIImageView to rotate and change its position as shown in the image when the user scrolls from page-1 to page-2. Also the animation should ideally rotate back when the user is scrolling back from page-2 to page-1.
The animation or movement of the UIImageView is based on how much the user is scrolling horizontally and not based on time.
How can I rotate the UIImageView back and forth based on the scroll position of the UIScrollView?
Set a delegate for your scroll view. Probably you want your view controller to be the delegate. You need to add UIScrollViewDelegate to its list of protocols.
Then, in the delegate, implement scrollViewDidScroll:. In scrollViewDidScroll:, look at the scroll view's contentOffset. Based on the contentOffset, set the image view's transform and center to rotate and move it where you want.
To find by how much the scroll view has scrolled, you can check UIScrollView's contentOffset property:
contentOffset - The point at which the origin of the content view is
offset from the origin of the scroll view.
Then to rotate that image view, you could do this:
self.imageview.transform = CGAffineTransformMakeRotation(M_PI/2);
As far as the animation goes, I personally don't have much experience with it. But you could have a look at Simple Animation Using UIImageView.

How can you pin a header overtop of a UIScrollView that only scrolls in the x dimension?

I have a UIScrollView that is displaying some graphical data and it has a x axis at the top of the screen. Currently when you scroll down the x axis disappears because it is just a subview of UIScrollView but I want it pinned to the top of the screen at all times so the user can see the x axis values.
Typically I would just put this axis ontop of the scroll view, so that it doesn't scroll, but I still need it to scroll along with the UIScrollView whenever a horizontal scroll takes place because the x axis does not entirely fit in the view.
I'm not sure how to go about doing this. I've thought about a few approaches.
I could remove the x axis from the ScrollView and then put it in its own UIView and then use the scroll view delegate methods to sync the x axis with the scrollviews new position after and during a horizontal scroll.
I could somehow duplicate horizontal scroll events and pass them to the x axis view (which would be another UIScrollView in this case).
I'm hesitant to try number one because I think the animations would be jerky or at the very least out of sync with the horizontal scroll of the scroll view, I would like them to be completely in sync.
I'm hesitant to try number two because I'm not sure if that is even possible to do.
I'm looking for advice on how to approach this problem (preferably from somebody who has done something similar before).
Thanks in Advance!
I believe that your second approach will work. Make your view controller a UIScrollViewDelegate for the 2-way scrolling view and use scrollViewDidScroll: to synchronize the positioning by adjusting the scrolling header.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = [scrollView contentOffset];
CGPoint headOffset = CGPointMake(offset.x, 0.0f);
[self.headScroller setContentOffset:headOffset animated:NO];
}
This assumes that the 'headScroller' has user interaction turned off and has no delegate. It only scrolls under program control.

Update view relative to position of verticle UIScrollView

I was wondering how one would update items such as a UILabel when a scroll view reaches a certain point - I have a picker app in which the user scrolls a UIScrollView and the contents of the screen update relative to the position of the UIScrollView however I would like to know how to actually update the contents of a view, again, things like a UILabel relative to the position of the UIScrollView.
You can use scrollView.contentOffset to get current content position inside scrollView.

Resources