UIPageViewController detecting pan gestures - ios

Is there a way to determine the panning location of a UIPageViewController while sliding left/right? I have been trying to accomplish this but its not working. I have a UIPageViewController added as a subview and i can slide it horizontally left/right to switch between pages however i need to determine the x,y coordinates of where I am panning on the screen.

I figured out how to do this. Basically a UIPageViewController uses UIScrollViews as its subviews. I created a loop and set all the subviews that are UIScrollViews and assigned their delegates to my ViewController.
/**
* Set the UIScrollViews that are part of the UIPageViewController to delegate to this class,
* that way we can know when the user is panning left/right
*/
-(void)initializeScrollViewDelegates
{
UIScrollView *pageScrollView;
for (UIView* view in self.pageViewController.view.subviews){
if([view isKindOfClass:[UIScrollView class]])
{
pageScrollView = (UIScrollView *)view;
pageScrollView.delegate = self;
}
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(#"Im scrolling, yay!");
}

My personal preference is not to rely too much on the internal structure of the PageViewController because it can be changed later which will break your code, unbeknownst to you.
My solution is to use a pan gesture recogniser. Inside viewDidLoad, add the following:
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handler))
gestureRecognizer.delegate = yourDelegate
view.addGestureRecognizer(gestureRecognizer)
Inside your yourDelegate's definition, you should implement the following method to allow your gesture recogniser to process the touches
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Now, you should be able to access the X/Y location of the user's touches:
func handler(_ sender: UIPanGestureRecognizer) {
let totalTranslation = sender.translation(in: view)
//...
}

Related

Disable Horizontal UICollectionViewCell Pan Gesture On Vertical UICollectionView Scroll

I have a vertically scrollable UICollectionView with cells that have an added horizontal pan gesture.
Is there an advised way to disable the horizontal pan gesture while scrolling the UICollectionView? I ask because whenever I do scroll vertically, the slightest movements left or right also trigger the pan gesture and move the cell right or left.
I tried enabling/disabling the UICollectionView when the state of the gesture changed but it was difficult to scroll since a pan gesture would be read in almost every touch instance. I also tried doing something I found online in assessing the x/y velocity and determining whether to fulfill the pan gesture. This seemed to prevent any horizontal panning though..
And finally, I need to use pan versus swipe because I need to include conditional statements on a translation threshold when someone pans (i.e. didn't go x distance, return cell back to original position). If there is a way with swipe, I'm all ears!
Relevant code below:
func onPan(_ sender: UIPanGestureRecognizer) {
//Attempt 1
theCollectionView.isScrollEnabled = false
let cellView = sender.view!
let point = sender.translation(in: cellView)
cellView.center = CGPoint(x: parentView.center.x + point.x, y: cellView.center.y)
if sender.state == .ended {
//Attempt 1
homeCollectionView.isScrollEnabled = true
UIView.animate(withDuration: 0.2, animations: {
cellView.center = CGPoint(x: self.parentView.center.x, y: cellView.center.y)
})
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
//Attempt 2
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return abs((pan.velocity(in: pan.view)).x) > abs((pan.velocity(in: pan.view)).y)
}

Recognize swipe gesture in UIScrollView only when scrolling content reach the edge

I have UIScrollView with vertical scroll active. What I am trying to do is to add a swipe gesture with direction .down which will be recognized when the user cannot scroll the content anymore because it reaches the edge.
I was trying with require(toFail:) but it doesn't work properly.
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
swipeDown.direction = UISwipeGestureRecognizerDirection.down
swipeDown.require(toFail: self.scrollView.panGestureRecognizer)
self.scrollView.addGestureRecognizer(swipeDown)
I have also added UIGestureRecognizerDelegate method to recognize simultaneously:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
How to give always priority to scrolling content inside scrollView and when it is not possible anymore to detect swipe?
OK, the way I have managed that issue was simply check if the contentOffset reaches point 0.0, and if yes then disable scrolling and activate additional gesture. For my case that was enough.
if scrollView.contentOffset.y == 0.0 {
print("content on top")
// use delegate method here to manage the gestures
}

Custom Interactive Transition With Table View

I create custom push/pop interactive transition, but I can't understand how to handle this transition and table view scroll simultaneously.
For handle custom pop animation I add pan gesture
let pan = UIPanGestureRecognizer(
target: self,
action: #selector(didPan))
pan.delegate = self
view.addGestureRecognizer(pan)
Next I handle this pan
if recognizer.state == .began {
animator.interactive = true
navigationController!.popViewController(animated: true)
}
animator.handlePan(recognizer: recognizer)
Then inside animator conformed to UIPercentDrivenInteractiveTransition and UIViewControllerAnimatedTransitioning I do transform animation.
I try to pop navigation from VC_2 to VC_1 by pan gesture.
The problem is after adding pan gesture table view is not scrolling. I think it's because 2 gesture recognizers (my own and UIKit table view's recognizer). Actually I want my own pan interactively pop my VC_2 when table is scrolled up.
The only one idea was
func gestureRecognizer(
_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return tableView.contentOffset.y <= 0
}
But on pop table view offset changed strange: I mean table is scroll too much as pan gesture moved (move for 20px, offset changed for 500px some kind of that).
What is the best way to handle this? You can see such animation in Inbox app from Google - pop from e-mail to list.

Disable UIScrollView inertia (scrollViewDidEndDecelerating) Swift

Setup: I have a Scroll View that works great horizontally as well as a UISwipeGestureRecognizer that triggers a segue to another view when I swipe down.
Problem: If I am scrolling horizontally and I begin to swipe down (vertical scrolling disabled) WHILE the scrolling is decelerating, the swipe down action (segue) does not execute. It only works once the scrolling deceleration is complete.
Is there a way to disable the scroll deceleration (aka inertia) so that my swipe down gesture can be detected instantly? Perhaps there's a workaround to force UISwipeGestureRecognizer to be detected first?
Solutions in Swift are highly appreciated!
UIScrollView has a pinchGestureRecognizer and a panGestureRecognizer.If you have a UISwipeGestureRecognizer,the gesture will most likely be recognized as a UIPanGestureRecognizer.
You can add a dependence to solve the problem:
scrollView.panGestureRecognizer.requireGestureRecognizerToFail(swipeGesture)
Swift 3:
To disable decelerating scroll you can try:
func scrollViewWillEndDragging (scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.memory = scrollView.contentOffset
}
or you change this value:
scrollView.decelerationRate = UIScrollViewDecelerationRateNormal
// UIScrollViewDecelerationRateFast is the other param
About your gestureRecognizer interferences you can call this method to exclude touching from:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
// insert here views to exclude from gesture
if touch.view!.isDescendantOfView(self.excludeThisView) {
return false
}
return true
}
did you look here?
Deactivate UIScrollView decelerating
the answer
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
[scrollView setContentOffset:scrollView.contentOffset animated:YES];
}
converted to swift is
func scrollViewWillBeginDecelerating(scrollView: UIScrollView) {
scrollView.setContentOffset(scrollView.contentOffset, animated: true)
}

uiscrollview won't scroll when dragging on ios-charts subview

I have a ios-chart as a subview that takes up half the screen. When I pan up on any other subview it scrolls. But not when I pan on the chart.
I tried setting:
[self.chart setDefaultTouchEventsEnabled:YES];
//and
[self.chart setScaleEnabled:NO];
It says in the documentation
defaultTouchEventsEnabled
enables/disables default touch events to be handled. When disable, touches are not passed to parent views so scrolling inside a UIScrollView won’t work.
What can I do to enable scrolling when panning/dragging on the chart?
I was struggling with this as well. Since I needed the chart to be scrollable, #Entrabiter's solution didn't work for me. The only solution that worked for me, was assigning the delegate of the chart view's UIPanGestureRecognizer to my ViewController and implement UIGestureRecognizerDelegate.
This solution is in Swift, but it should also work fine in Objective-C.
class MyViewController: UIViewController, UIGestureRecognizerDelegate {
// MARK: Outlets
#IBOutlet weak var contentView: UIScrollView!
#IBOutlet weak var myChart: LineChartView!
override func viewDidLoad() {
super.viewDidLoad()
if let gestureRecognizers = myChart.gestureRecognizers {
for gestureRecongnizer in gestureRecognizers {
if gestureRecongnizer is UIPanGestureRecognizer {
gestureRecongnizer.delegate = self
}
}
}
}
}
The important part is to tell the gestureRecognizer to recognize both the scrollview's panGestureRecognizer as well as the chartview's panGestureRecognizer.
// MARK: UIGestureRecognizerDelegate
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if otherGestureRecognizer == contentView.panGestureRecognizer {
return true
}
return false
}
Set userInteractionEnabled on the chart view to NO.
I think I figured it out...
I removed all the UIPanGestureRecognizers from the ios-charts subview.
This allowed for the scrollview to handle all the pan events.
for(UIGestureRecognizer *rec in self.chart.gestureRecognizers){
if([rec isKindOfClass:[UIPanGestureRecognizer class]]){
[self.chart removeGestureRecognizer:rec];
}
}

Resources