Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to drag down the screen with horizontal Collection View? #214

Closed
wiencheck opened this issue Jan 19, 2018 · 35 comments
Closed

How to drag down the screen with horizontal Collection View? #214

wiencheck opened this issue Jan 19, 2018 · 35 comments

Comments

@wiencheck
Copy link

I have a horizontal Collection View in my View Controller that is presented by LNPopupController and I can't find a way to register dragging down the controller if the touches are in the Collection View's area. If I disable scrolling of Collection View it works great but disabling it is out of question, also subclassing UICollectionView and using UIGestureDelegate's method gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) doesn't make any difference.

@LeoNatan
Copy link
Owner

Could you please post an example project? This should already work automatically; when you scroll to the top and pull the scroll view more, it should close the popup.

@wiencheck
Copy link
Author

Please take a look at this project:
https://github.com/wiencheck/FootballPlayers

It seems that drag-down gesture is not working when collectionView is not moving, if you start dragging collectionView you can drag down entire view.
I have also tried disabling the drag-down gesture when collectionView is moving, because it is too easy to close the screen accidentaly, but with no success. Is there a way to do it?

@LeoNatan
Copy link
Owner

Are you using a UICollectionViewController? I don't seem to remember right now if I support "rogue" table views and collection views, but I think I do. I'll look soon when I have time.

@LeoNatan
Copy link
Owner

Ohhh, you have a horizontal collection. Sorry, I missed that.
I see in my head how there can be an issue. I will look for a solution for you. In general, the delegate methods should be enough, but I could be wrong.

@DantePuglisi
Copy link

I'm having the exact same issue and playing around with gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) doesn't seem to make a difference.

Another project where that's reproducible in case it helps:
TestPopupController.zip

@DantePuglisi
Copy link

@LeoNatan any news? Maybe point me in the right direction and I could try to fix it and create a PR but everything I've tried so far hasn't worked

@LeoNatan
Copy link
Owner

LeoNatan commented Oct 8, 2020

Hello,

The solution, I imagine, would be to play around with the delegate. Take a look at:

https://github.com/LeoNatan/LNPopupController/blob/master/LNPopupController/LNPopupController/Private/LNPopupInteractionPanGestureRecognizer.m

My guess is you'll have to determine the interaction type ("vertical pan") and the scroll view type ("horizontal scroll view") and return YES somewhere where it returns otherwise right now.

@DantePuglisi
Copy link

The weird thing is that when I do this

self.popupContentView.popupInteractionGestureRecognizer.delegate = self.exerciseVC!
self.presentPopupBar(withContentViewController: self.exerciseVC!, openPopup: true, animated: true, completion: nil)

And I do this inside exerciseVC

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
	print("EQUAL: \(otherGestureRecognizer === scrollView.panGestureRecognizer)")
	return true
}

I'm getting EQUAL: true printed when moving the horizontal scrollView but I'm not getting a single print when moving the top bar up and down. I think that could be the base of the issue, maybe some overwriting of the delegate?

@DantePuglisi
Copy link

DantePuglisi commented Oct 9, 2020

Looking at the library's code, it's only forwarding shouldRecognizeSimultaneouslyWithGestureRecognizer if LNPopupPresentationStateOpen is false.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
	if([NSStringFromClass(otherGestureRecognizer.view.class) containsString:@"DropShadow"])
	{
		otherGestureRecognizer.state = UIGestureRecognizerStateFailed;
		return YES;
	}
	
	if([NSStringFromClass(otherGestureRecognizer.class) containsString:@"Reveal"])
	{
		return NO;
	}
	
	if(_popupController.popupControllerInternalState != LNPopupPresentationStateOpen)
	{
		if([self.forwardedDelegate respondsToSelector:_cmd])
		{
			return [self.forwardedDelegate gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
		}
		
		return YES;
	}
	
	return NO;
}

@DantePuglisi
Copy link

I can't figure it out, as far as I understand it should work with a combination of gestureRecognizerShouldBegin and shouldRequireFailureOf. I've tried a lot but what I'm almost sure should work is:

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
	guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer else { return true }
	let velocity = panGesture.velocity(in: view)
	if gestureRecognizer === scrollView.panGestureRecognizer {
		/// If the gesture is from scrollView and it's scrolling horizontally
		if abs(velocity.x) > abs(velocity.y) {
			return true
		} else {
			return false
		}
	} else {
		return true
	}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
	if gestureRecognizer !== scrollView.panGestureRecognizer {
		return true
	}
	
	return false
}

@LeoNatan
Copy link
Owner

LeoNatan commented Oct 9, 2020

It might not be forwarded to the delegate. Try doing that in the actual delegate (not the forwarded user delegate), just to see if it works.

@DantePuglisi
Copy link

I tried doing this inside the library with no luck (it's never calling return NO; but I tried to mimic what I built before)

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[LNPopupInteractionPanGestureRecognizer class]]) {
        return YES;
    } else {
        return NO;
    }
    ...
}

@LeoNatan
Copy link
Owner

I tried running your example, and it works as expected. I also made a horizontal scrolling content view controller, and it seems to me to work as expected.

@LeoNatan
Copy link
Owner

If you can, please record a short video of your problem so I can understand how to test. Thanks

@DantePuglisi
Copy link

The thing is we can't drag down from where the horizontal scrollView is (in the example represented by the blue and yellow views as its content). Also when returning true is shouldRecognizeSimultaneouslyWith the issue is what can be seen at the end where dragging down to dismiss actually works when starting inside the scrollView but only when the scrollView is horizontally scrolling which is something that shouldn't happen.

TestVideo

@DantePuglisi
Copy link

@LeoNatan Does that gif show the issue?

@LeoNatan
Copy link
Owner

I'm not sure. It's not what I saw when I rand your project. I will look again during the weekend. Thanks

@DantePuglisi
Copy link

Great! If it's worth something this happens both with LNPopupInteractionStyle as .drag and as .snap

@DantePuglisi
Copy link

@LeoNatan sorry for insisting but could you check?

@LeoNatan
Copy link
Owner

Sorry, no time at the moment
Feel free to investigate and submit a PR.

@iDevelopper
Copy link
Contributor

Apple should ask which gesture recognizer to use!

@DantePuglisi
Copy link

Hey @iDevelopper could you work around this in the past? I saw another issue where you were talking about something similar to this

@iDevelopper
Copy link
Contributor

@DantePuglisi , which issue?

@DantePuglisi
Copy link

DantePuglisi commented Oct 23, 2020

#213

There you played around with popupContentView.popupInteractionGestureRecognizer.delegate and shouldRecognizeSimultaneouslyWith but I'm not sure if it's the same issue

@iDevelopper

@iDevelopper
Copy link
Contributor

This a very old issue. It was my mistake when I wanted to set the delegate of the gesture with the wrong popupContentView.

@iDevelopper
Copy link
Contributor

iDevelopper commented Oct 26, 2020

@DantePuglisi ,

Try something like this:

import UIKit
import LNPopupController

class TESTViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!
        
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

extension TESTViewController: UIGestureRecognizerDelegate {
	func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
	}
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if let pgr = gestureRecognizer as? UIPanGestureRecognizer {
            let velocity = pgr.velocity(in: view)
            
            if velocity == .zero {
                return true
            }
            else {
                if abs(velocity.x) > abs(velocity.y) {
                    print("panning right or left")
                    return true
                }
                else {
                    print("panning up or down")
                    let page = Int((scrollView.contentOffset.x + (0.5 * scrollView.frame.size.width))/scrollView.frame.width)
                    let offset = page * Int(scrollView.frame.size.width)
                    scrollView.contentOffset.x = CGFloat(offset)
                    return false
                }
            }
        }
        return false
    }
}
import UIKit
import LNPopupController

class ViewController: UIViewController {

	override func viewDidLoad() {
		super.viewDidLoad()
	}
	
	@IBAction func showBarTapped(_ sender: Any) {
		let storyboard = UIStoryboard(name: "Main", bundle: nil)
        guard let vc = storyboard.instantiateViewController(withIdentifier: "TESTViewController") as? TESTViewController else {
            return
        }
		
		popupContentView.popupInteractionGestureRecognizer.delegate = vc
		popupContentView.popupCloseButtonStyle = .chevron
                popupInteractionStyle = .drag
		vc.popupItem.title = "Title"
		vc.popupItem.subtitle = "Subtitle"
		vc.popupItem.progress = 0.34
		presentPopupBar(withContentViewController: vc, animated: true, completion: nil)
	}
}

@DantePuglisi
Copy link

Thanks @iDevelopper that works! It's a pretty smart solution 👏

The only thing (that maybe can't be fixed) is that I have to pan down too much for the viewcontroller to start dismissing, how you faced that as well?

@LeoNatan
Copy link
Owner

That makes sense, since you are waiting for the pan gesture recognizer to fail. What happens if gestureRecognizer(_:shouldRequireFailureOf:) returns always false?

@iDevelopper
Copy link
Contributor

iDevelopper commented Oct 26, 2020

What happens if gestureRecognizer(_:shouldRequireFailureOf:) returns always false?

The scroll view does not scroll horizontally.

@LeoNatan
Copy link
Owner

Maybe needs to also implement shouldBeRequiredToFail -> false

@iDevelopper
Copy link
Contributor

No, I have already tried without success.

@iDevelopper
Copy link
Contributor

iDevelopper commented Oct 26, 2020

TestPopupController 3.zip

Without SPM for framework edit capabilities...

@LeoNatan
Copy link
Owner

Apple's sheet presentation controller has the challenge, where if you pan in a scroll view, it has to understand if you are scrolling or not, and then if not, pull the controller's view to dismiss. I wonder what hacks they do there.

@DantePuglisi
Copy link

@iDevelopper @LeoNatan the weird thing is that if there's a tableView inside the horizontally scrollView it works great (because the tableView fails as soon as it reaches the top I guess). I'm trying to replicate this on a view where no scrolling should happen, when adding a vertical scrollView it obviously doesn't work because the scrolling is ignored since there's no need to scroll. Any ideas? I've tried adding 1pt to the scrollView content at the bottom but it scrolls way more than that point

@LeoNatan
Copy link
Owner

I think things are now much better. If there are still issues with horizontal scrolling, I think I've hit the limit with the framework gesture recognizer logic, and it will be up to the developers to provide a delegate for the interaction gesture recognizer and improve this behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants