Disable gestures for table header view - ios

I am trying to build similar controller to GMSPlacePicker.
I have Map View on background, then Table View with transparent header view. The problem is that all gestures (tap, pan) within header view are passed to table view. I wanna disable them, so all touches will go directly to map view.
I am able to do it If I set:
tableView.userInteractionEnabled = false
but now I am not able to scroll table view.
The question is how to disable all gesture only for header view, but keep getting them for table view.
Basically I wanna get the following behaviour: https://youtu.be/iSBbEZXDyGg

The trick was to create subclass of UITableView and override
func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView?
ANTableView.swift
import UIKit
class ANTableView: UITableView
{
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView?
{
let headerViewFrame = tableHeaderView!.convertRect(tableHeaderView!.frame, toView: self)
if CGRectContainsPoint(headerViewFrame, point) {
return nil
}
return self
}
}

You need to put your map inside the header view. Not behind it.

Related

How to override hitTest to select UITableView Rows?

How do I allow my tableView to be selectable by overriding hitTest?
I have a tableview that lives outside of bounds of its superview so I need to override hitTest of the view containing the superview. However, when I pass the tableview's hittest I can only scroll and not tap on a row.
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let pointInTableview = tableView.convert(point, from: self)
guard tableView.bounds.contains(pointInTableview) else {
return super.hitTest(point, with: event)
}
return tableView.hitTest(pointInTableview, with: event)
}
The datasource and delegates are set. If I manually call tableview.selectRowAt(..) I am able to receive the delegate callback.
Gif of demo
The VC View is the view that is overriding hitTest. We are trying to pass the touches to the tableview because the tableview is outside the bounds of the view it lives in.
The problem wasn't the hitTest.
My VC View had a tap gesture to close keyboards. This tap gesture was cancelling the tableview's tap gesture.
Ref: https://kakubei.github.io/2016/02/24/Tap-Gesture-and-TableView/

iOS: How to implement the mask view event penetrate?

As the following image,I have a collectionView, with a maskView above it.
And a right side view above the maskView.
How to implement the mask view event penetrate?
My PM's idea is that the collectionViewCell is clickable when a mask view above the collectionView. ( a little weird )
The mask view has taken over the collectionView's event.
I think I should override the event responder chain.
I tried to handle the override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
class Mask: UIView {
// ...
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let views = superview?.subviews else {
return self
}
for view in views{
if type(of: view) == UICollectionView.self{
return view
}
}
return self
}
}
Not worked as commanded, the collectionView just scrolls.
How to solve it?
I improved the code a little.
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let views = superview?.subviews else {
return self
}
for view in views{
if type(of: view) == UICollectionView.self{
let collectionView = view as! UICollectionView
for cell in collectionView.visibleCells{
if cell.frame.contains(point){
return cell
}
}
}
}
return self
}
It is not very sharp. Some events are abandoned when switching the cells being clicked. Just like Apple did a hitTest event cache.
If I understood correctly and the mask view is on top of the collectionView and it gets the events you can just set the mask view userInteractionEnabled to false.
This way the event will be sent to the layer below until someone handles it.

Transfer gestures on a UIView to a UITableView in Swift - iOS

I have a layout with a UIView at the top of the page and, right below it, I have a UITableView.
What I am wanting to do is to transfer the gesture interactions on the UIView to the UITableView, so when the user makes a drag up/down on the UIView, the UITableView scrolls vertically.
I tried the following code
tableView.gestureRecognizers?.forEach { uiView.addGestureRecognizer($0) }
but it removed the gestureRecognizers from the UITableView somehow :/
Obs.: the UIView cannot be a Header of the UIScrollView
That's Tricky
What is problem ?
Your top view is not allowed to pass through view behind it...
What would be possible solutions
pass all touches to view behind it (Seems to not possible or very tough practically )
Tell window to ignore touches on top view (Easy one)
Second option is better and easy.
So What you need to do is create subclass of UIView and override
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
and return nil if you found same view on hitTest action
Here Tested and working example
class PassThroughME : UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return super.hitTest(point, with: event) == self ? nil : self
}
}
That's it now use PassThroughME either by adding class to your view in storyboard or programmatically whatever way you have added your view
Check image i have black color view with 0.7 alpha on top still i am able to scroll
Hope it is helpful

UICollectionViewCell signal to UICollectionView didSelect

On top of my UICollectionViewCells in my UICollectionView I've overlaid a UIButton that intercepts touches so I can respond to touch events more granularly. The issue is that now the collection view no longer gets didSelectItemAtIndexPath messages. (For obvious reasons... the button has absorbed the touch and isn't signaling to the collection view that the item was selected.)
Is there a way to signal to the collection view that the cell was selected? I've seen similar questions but none seem to give a convincing answer.
You can override the pointInside:withEvent: message on UIView [and subclasses] and return false to continue propagating the touch event.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instm/UIView/pointInside:withEvent:
class PassThroughButton: UIButton {
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
// do something
// then continue event propigation
return false
}
}

Scrollable UITableView slide up to top of screen in Swift

I am trying to create a very simplified layout, something like Stripe's iOS app has which you can see here: https://stripe.com/img/dashboard_iphone/screencast.mp4
At around 0:06s the table view is swiped up and it moves up to the top of the window.
Are there any simple instructions in Swift that show this design pattern I see it everywhere but no idea how to create it
Add a UIView
Subclass that UIView with a custom class
In you custom UIView, you'll need a couple of variables and a few overrides. Make sure that user interaction is enabled on the UIView in your storyboard, then in your custom class add:
var startPosition: CGPoint?
var originalHeight: CGFloat?
After that, add the following:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
startPosition = touch?.locationInView(self)
originalHeight = self.frame.height
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let endPosition = touch?.locationInView(self)
let difference = endPosition!.y - startPosition!.y
let newFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y + difference, self.frame.width, self.frame.height - difference)
self.frame = newFrame
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
In UIScrollView.h:
// When the user taps the status bar, the scroll view beneath the touch which is closest to the status bar will be scrolled to top, but only if its `scrollsToTop` property is YES, its delegate does not return NO from `shouldScrollViewScrollToTop`, and it is not already at the top.
// On iPhone, we execute this gesture only if there's one on-screen scroll view with `scrollsToTop` == YES. If more than one is found, none will be scrolled.
var scrollsToTop: Bool // default is YES.
Initialize your scroll view delegate (unless you already have, probably in your viewDidLoad), and then set this to true.
Like this:
myScrollView.scrollsToTop = true
If you want to be sure your scroll view delegate allows this, check this method from UIScrollViewDelegate's protocol:
optional func scrollViewShouldScrollToTop(scrollView: UIScrollView) -> Bool // return a yes if you want to scroll to the top. if not defined, assumes YES
Please comment any concerns.

Resources