Reputation: 40265
Using UIPanGesture
on a UITableView
(Swift 4.0) to drag the tableview position (i.e. origin). UIPanGestureRecognizer
detecting touch events and consuming them. At some point I want to delegate the events to table so it will scroll its items. How can I do so?
What have I tried:
@objc func tableViewDragged(gestureRecognizer: UIPanGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.began || gestureRecognizer.state == UIGestureRecognizerState.changed {
let translation = gestureRecognizer.translation(in: self.view)
// if origin moved to map origin it should not scroll above but cell should scroll normally. But returning does not help scrolling cells
if self.tableView.frame.origin == self.mapContainer.frame.origin && translation.y < 0 {
return
}
var frame = self.tableView.frame
let nHeight = (frame.origin.y + translation.y)
if (nHeight < self.view.frame.size.height) {
frame.origin.y = nHeight
self.tableView.frame = frame
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self.view)
}
}
}
Gesture added to tableView
let gesture = UIPanGestureRecognizer(target: self, action: #selector(self.tableViewDragged(gestureRecognizer:)))
self.tableView.addGestureRecognizer(gesture)
self.tableView.isUserInteractionEnabled = true
gesture.delegate = self
Problem Summary:
For the same pan gesture at any point of tableview (i.e. on any cell), I need to scroll items when tableviews origin got fixed in top left corner of the container. Otherwise I have to move the tableview until its got attached to top left point. Moreover If If nothing to scroll down I need to again move the table below for pan down gesture.
*Problem Usecases: *
Upvotes: 2
Views: 4767
Reputation: 4930
I don't know a better solution than subclassing your tableView and blocks its contentOffset
in the layoutSubviews
of it. Something like :
class MyTableView: UITableView {
var blocksOffsetAtZero = false
override func layoutSubviews() {
super.layoutSubviews()
guard blocksOffsetAtZero else { return }
contentOffset = .zero
}
}
I think this is the solution used in the Maps app. When the user drags the tableView on the home screen, it does not scroll but lifts until it reaches the top of the screen and then scrolls again.
To do so, you add a gesture to your tableview which recognizes simultaneously with the recognizer of the tableView and blocks the contentOffset
of the tableView each time the recognizer moves the frame of the tableView.
UPDATE https://github.com/gaetanzanella/mapLike
Did you implement the delegate correctly ?
// MARK: - UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Try something like :
@objc func tableViewDragged(gestureRecognizer: UIPanGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.began || gestureRecognizer.state == UIGestureRecognizerState.changed {
let translation = gestureRecognizer.translation(in: self.view)
var frame = self.tableView.frame
let nHeight = (frame.origin.y + translation.y)
if (nHeight < self.view.frame.size.height) {
frame.origin.y = nHeight
self.tableView.frame = frame
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self.view)
tableView.blocksOffsetAtZero = true
} else {
tableView.blocksOffsetAtZero = false
}
}
}
The main idea : when you are changing the frame, block the scroll.
You could also use the delegate technique presented by kamaldeep singh bhatia but you will not be able to move first tableView then scroll in a single gesture.
See a example here
Upvotes: 1
Reputation: 732
If I am understanding it correctly so you want to cancel PanGesture when you want to Scroll.
Try this :
func isItemAvailabe(gesture: UISwipeGestureRecognizer) -> Bool {
if gesture.direction == .down {
// check if we have some values in down if yes return true else false
} else if gesture.direction == .up {
// check if we have some values in up if yes return true else false
}
return false
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == self.panGesture && otherGestureRecognizer == self.scrollGesture && isItemAvailabe(gesture: otherGestureRecognizer) {
return true
}
return false
}
Let me know if this solve your problem or I am not getting it correctly.
Also remember to add this too :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
You can check more here
Upvotes: 1
Reputation: 4956
Best way to do this using tableView's own panGestureRecognizer (without adding new gestureRecognizer), like:
tableView.panGestureRecognizer.addTarget(self, action: #selector(self.tableViewDragged(gestureRecognizer:))
Action
@objc func tableViewDragged(gestureRecognizer: UIPanGestureRecognizer) {
guard tableView.contentOffset.y < 0 else { return }
if gestureRecognizer.state == UIGestureRecognizerState.began || gestureRecognizer.state == UIGestureRecognizerState.changed {
let translation = gestureRecognizer.translation(in: self.view)
// if origin moved to map origin it should not scroll above but cell should scroll normally. But returning does not help scrolling cells
if self.tableView.frame.origin == self.mapContainer.frame.origin && translation.y < 0 {
return
}
var frame = self.tableView.frame
let nHeight = (frame.origin.y + translation.y)
if (nHeight < self.view.frame.size.height) {
frame.origin.y = nHeight
self.tableView.frame = frame
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self.view)
}
}
}
Upvotes: 3