Change dragging one view to another in same gesture - ios

I'm trying to reproduce the buttom sheet in Apple's iOS 10 Maps app. Most of it is working. I've been looking at this SO post and Pulley on GitHub, but none of them solves my issue.
When the sheets is fully opened, it is possible to scroll the content of the sheet as a UITableView, but when the user tries to scroll down (where the UITableView's contentOffset would be negative), the gesture is dragging in the sheet instead of the UITableView. The gesture seamlessly changes from dragging the UITableView to dragging the sheet.
It is possiple disable the scrolling of the UITableView in the gesture delegate's shouldRecognizeSimultaneouslyWith, but this is only called when a gesture begins.
I can't control the panGestureRecognizer of the UITableView, so I can't just capture the gesture and determine what view it should move.
How can I change what UIGestureRecognizer should recognize touches, in the middle of a gesture?

Try
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == myCustomPanGesture {
return self.tableView.contentOffset == 0
}
return true
}

Related

UISearchBar Sometimes Resigns When Tapping On Views

My view controller setup is as follows: UISearchBar on top of screen, keyboard on the bottom, and other views between them: custom UIView, UIView, UITableView. When I tap on the table view cells and UIButtons inside UIViews, I can trigger my actions successfully while keeping UISearchBar still visible on screen. But when I tap on UIViews that do not have buttons, this triggers searchBarShouldEndEditing as if search bar loses the focus. I want to disable this behavior and let only Cancel button and keyboard's Done button to trigger UISearchBar closure.
I thought this has to do with bubbling effect and tried to implement this fix:
extension UIViewController : UIGestureRecognizerDelegate{
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return touch.view == gestureRecognizer.view
}
#IBAction func didTap(_ gestureRecognizer : UITapGestureRecognizer ) {
//
}
but this did not address the issue and UISearchBar still calls searchBarShouldEndEditing. Of course, I could add logic to searchBarShouldEndEditing to return false but not sure this is effective enough.
How can I ensure UISearchBar remains in place while tapping on other elements like UIViews?

Is it possible to make Tap Gesture Recognizer work if UIImageView it's connected to is under UIScrollView?

Please take a look on a screenshot
I have a UIImageView with Tap Gesture Recognizer connected to it ("Mans body image view"). When I tap this UIImage I want it to call a keyboard with colors so I can change skin color of this mans figure.
For now it does not work because on top of Mans body image view I have 4 scroll views which are responsible for hair, facial hair and clothing (tops and bottoms).
Is there any way I can make Tap Gesture Recognizer (under scroll views) to react on my taps?
Thank you
Make a UIScrollView subclass and implement
func gesture​Recognizer(UIGesture​Recognizer, should​Recognize​Simultaneously​With:​ UIGesture​Recognizer)
Asks the delegate if two gesture recognizers should be allowed to recognize gestures simultaneously.
func gestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UISwipeGestureRecognizer) -> Bool {
return true
}
or use horizontal UICollectionView instead and implement delegate method didSelectItemAtIndexPath.

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.

Disabling user interaction for subview interferes with event handling for superview?

CustomView contains a subview, SubView, which implements a tap handler. However, for this implementation, SubView should ignore taps and let CustomView handle them.
The code below is supposed to achieve this, but setting userInteractionEnabled to false prevents taps on SubView from cascading to CustomView. Shouldn't CustomView still receive tap events if SubView has userInteractionEnabled set to false?
The Apple documentation says setting userInteractionEnabled to false causes events to get ignored, not that the view will swallow them so superviews don't receive them.
// CustomView class, which is a subclass of UIView
// Handle taps
let singleTap = UITapGestureRecognizer(target: self, action: #selector(doTap))
addGestureRecognizer(singleTap)
// Add SubView
view.insertSubview(SubView, atIndex: 0)
SubView.userInteractionEnabled = false
Updated code (doInit is called since buttonTapped is invoked on taps) but still not working:
class CustomButton : UIButton, UIGestureRecognizerDelegate {
private func doInit() {
...
// Handle taps
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(buttonTapped))
tapRecognizer.delegate = self
addGestureRecognizer(tapRecognizer)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
SubView will still receive the taps, but it will ignore them and it will stop there. If a view has userInteractionEnabled set to false, it will block touches that are meant for parent views.
My guess is you want to add this gesture recognizer delegate:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool
This will allow your parent view to receive touches at the same time as your SubView.
Building on Laynemoseley's answer, this is the relevant bit from the Apple docs:
In iOS 6.0 and later,
default control actions prevent overlapping gesture recognizer
behavior. For example, the default action for a button is a single
tap. If you have a single tap gesture recognizer attached to a
button’s parent view, and the user taps the button, then the button’s
action method receives the touch event instead of the gesture
recognizer. This applies only to gesture recognition that overlaps the
default action for a control, which includes:
A single finger single tap on a UIButton, UISwitch, UIStepper,
UISegmentedControl, and UIPageControl. A single finger swipe on the
knob of a UISlider, in a direction parallel to the slider. A single
finger pan gesture on the knob of a UISwitch, in a direction parallel
to the switch. If you have a custom subclass of one of these controls
and you want to change the default action, attach a gesture recognizer
directly to the control instead of to the parent view. Then, the
gesture recognizer receives the touch event first. As always, be sure
to read the iOS Human Interface Guidelines to ensure that your app
offers an intuitive user experience, especially when overriding the
default behavior of a standard control.
It seems the only solution is to override the gesture recognition and allow simultaneous gestures to get recognized.

Add a Pan Gesture Recognizer to an UITableView in Swift

How can I add a Pan Gesture Recognizer to an UITableView, without blocking my scrolling feature of my TableView?
This is my code:
#IBOutlet var ScanPanGestureRecognizer: UIPanGestureRecognizer!
#IBAction func ScanPanGestureRecognizer(sender: UIPanGestureRecognizer)
{
print("TEST")
}
override func viewDidLoad()
{
ScanTableView.addGestureRecognizer(ScanPanGestureRecognizer)
}
So the code works and I get a lot of prints with "Test" but I'm not able to move (scroll) my TableView any more. I have read some other questions / answers but I couldn't find my issue. I thought that the extension "addGestureRecognizer" only add a gesture and not overwrite the TableView pan gesture... Thanks!
I think whatever you want to do with a pan gesture recognizer on a UITableView you can do it in UITableView's delegate method scrollViewDidScroll: in that method the scrollView.contentOffset will tell you how much the tableView has scrolled

Resources