I have a gesture recogniser that is stopping the collectionView function from working properly. Example code is added below.
The view controller has a collection view, where cells can be deleted in a similar way to how apps are deleted on an iPhone home screen. The user long presses the screen for the collection cells to start shaking and stops shaking once the screen is tapped (not on the delete button for the cells).
Now that this delete functionality is working, I can no longer click on the cells to open a new view controller with the associated information. Is there a way to add a conditional so that the tapRecognizer only happens while the cells are shaking, so that the collectionView function works when a cell in the collection view is tapped?
override func viewDidLoad() {
super.viewDidLoad()
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressed))
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapPressed))
self.collectionView.addGestureRecognizer(longPressRecognizer)
self.collectionView.addGestureRecognizer(tapRecognizer)
}
//Tap screen
#objc func tapPressed(sender: UITapGestureRecognizer) {
stopCellShaking()
}
//Open the information for the selected cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//Function to open new screen
showInformation(chosenCell: exampleList[indexPath.row])
}
Is there a way to add a conditional so that the tapRecognizer only happens while the cells are shaking
Absolutely. In general, just turn off the tap recognizer by setting its isEnabled to false. When the shaking starts (long press), set the tap gesture recognizer's isEnabled to true.
Related
Question and situation explaination
Hi I have a custom collection view that I make each collectionview cell the size of the screen and make it scroll horizontally so I get like a paging effect.
In each collectionview cell I have a tableview which also take up the whole size of that collectionview cell. I'm trying to implement trailing and leading swipe action of my tableview cell but it is being ignore because of the swipe gesture to scroll the collectionview cells.
Is there a way to maybe like set the collectionview cell to only watch for pangestures that are near the edge of the screen then scroll the collectionview, other wise the pan gesture will just be send to the tableview instead?
Alternative solution (temporary work around)
Though I have found a work around but I still very much interested in finding out if what I'm trying to do above is possible so if you know please leave your answer
So after searching and searching stackoverflow there seem to be other people with the same problem as mine but doesnt seem to be able to found an answer. Thus I have come up with another idea and I like to share so if anyone come across this problem they can have options to choose from.
My idea is adding a long press gesture regconizer for the tableview you can achieve that as follow in swift 4
func setupLongPressGesture() {
let longPressGesture: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongPress))
longPressGesture.minimumPressDuration = 1.0 // 1 second press
longPressGesture.delegate = self
dataDisplayTable.addGestureRecognizer(longPressGesture)
}
#objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer){
if gestureRecognizer.state == .began {
let touchPoint = gestureRecognizer.location(in: dataDisplayTable)
if let indexPath = dataDisplayTable.indexPathForRow(at: touchPoint) {
// let cell = dataDisplayTable.cellForRow(at: indexPath) - use this to retreive the cell at the above indexPath to do something with it
// print(cell?.reuseIdentifier) - use this to retreive the cell identifier and do something with it
}
}
}
Then add this to your viewDidLoad()
setupLongPressGesture()
The problem is that I have a tableview with webview
Inside each cell and the content of webview can scroll
Horizontally, but not vertically. It’s height is the same as
Cells height. So the problem is when I am trying to detect
DidSelect of tableview cell it doesn’t response. But when I tried to put userinteraction of webview to false it works but webview can not scroll anymore. Is there any alternative ways of doing this. Btw webview cells must contain webview no other ways.
Here is the screenshot of why I need webview inside cell.
Each cell contains webview content
You can add UITapGestureRecognizer to the WebView and do the action inside the UITapGestureRecognizer. (Here I'm adding how to add a gesture recognizer, you have to adapt it to your own case)
let tapRecognizer = UITapGestureRecognizer(target: webView, action: #selector(self.handleSingleTap(_:)))
tapRecognizer.numberOfTapsRequired = 1
self.view.addGestureRecognizer(tapRecognizer)
func handleSingleTap(recognizer: UITapGestureRecognizer) {
//Do something here with the gesture
}
you can set delay touch false in order to have select event for cell.
tableview.delaysContentTouches = false
use this link for detail info - https://developer.apple.com/documentation/uikit/uiscrollview/1619398-delayscontenttouches
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
}
So I have a collection view of cards, and what I want is when I swipe left I want the next item to be centered in the middle, this doesn't work with paging enabled
So when I swipe left I want the next card to be centered, this doesn't work with the normal behaviour because I just want to swipe one card.
So I have added UISwipeGestureRecognizer and disabled scrolling on the collection view
class ViewController: UIViewController,UICollectionViewDataSource {
#IBOutlet var joke_cards: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad();
//Add gestures
let leftSwipeGest = UISwipeGestureRecognizer(target: self, action: #selector(funcForGesture))
leftSwipeGest.direction = .left
joke_cards.addGestureRecognizer(leftSwipeGest)
}
func funcForGesture(sender: UISwipeGestureRecognizer){
if sender.direction == .left {
//scroll to next item
}
}
Now my problem is how can I scroll to the next item? since I don't know the indexPath? because if I want to use this self.joke_cards.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true), I will need the index path, so I think I need to figure out the current indexPath on the view and add one when the user swipes.
Any suggestions?
//updated
So I have managed to get it to work, but it only works when I swipe the first time:
func funcForGesture(sender: UISwipeGestureRecognizer){
if sender.direction == .left {
//scroll to next item
let cellItems = self.joke_cards.indexPathsForVisibleItems
let next = cellItems[0] as! IndexPath
self.joke_cards.scrollToItem(at: next, at: .centeredHorizontally, animated: true)
}
}
One solution:
1: Add an NSIndexPath property to your cells and just set the indexPath to the property in your cellForItem method.
2: Get an array of visible cells.
3: Get the rect of the visible cells and calculate their position on the screen and you can allso get the correct indexPath from the cell property.
Second solution:
1: Add a UILongPressGestureRecognizer to your cell contentView
2: Set minimumPressDuration to 0.
3: Add delegate methods to fire acitons or this.
Third solution:
1: Keep your swipe gesture.
2: And use this method (same as option 2) indexPathForItemAtPoint:
Why are you saying that this does not work with paging enabled? This should actually be the best way to do it in my opinion. You can just make the width of the UICollectionViewCells to be equal to the width of the UICollectionView, set the scroll direction to be horizontal and enable paging, that should do the trick.
In this way you won't need any UIGestureRecognizers.
Ok so the solution is this one:
#IBOutlet var joke_cards: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad();
//Add gestures
let leftSwipeGest = UISwipeGestureRecognizer(target: self, action: #selector(funcForGesture))
leftSwipeGest.direction = .left
joke_cards.addGestureRecognizer(leftSwipeGest)
}
func funcForGesture(sender: UISwipeGestureRecognizer){
if sender.direction == .left {
//scroll to next item
let cellItems = self.joke_cards.indexPathsForVisibleItems
self.joke_cards.scrollToItem(at: cellItems.max()!, at: .centeredHorizontally, animated: true)
}
}
I am trying to build a CollectionView for a list all programmatically in Swift without using Storyboard. I was able to add a tap gesture event to the cell.
However, When I added a set of buttons to the UICollectionViewCell's contentView, the buttons do not receive any touch events.
Inside Controller
let sampleCollectionView = UICollectionView(frame: (..), collectionViewLayout: layout)
//Register the UICollectionViewCell inside UICollectionView
sampleCollectionView.registerClass(sampleCollectionViewCell.self, forCellWithReuseIdentifier: "sampleCell")
//Add Tap Gesture event to the cell area
let tap = UITapGestureRecognizer(target: self, action: "handleTapForCell:")
sampleCollectionView.addGestureRecognizer(tap)
func handleTapForCell(recognizer: UITapGestureRecognizer){
//I can break in here
}
Inside CollectionViewCell
class sampleCollectionViewCell: UICollectionViewCell
{
override init(frame: CGRect) {
var buttonBack = UIButton(type: .Custom)
buttonBack.addTarget(self, action: "actionGoBack:", forControlEvents: UIControlEvents.TouchUpInside)
..
self.contentView.addSubview(buttonBack)
}
func actionGoBack(sender: UIButton){
//I want to get my touch action break in here when I tap right inside the button but it won't
}
}
Is CollectionViewCell suited to accept more than one type of tap action (tapping on the whole cell versus multiple buttons inside cell)?
Here is what I tried so far:
Disable Tap Gesture on CollectionView, still not receiving events
Without adding tap events to button, I tried to find the "location" of tap inside the cell, then check if this is in bounds of the button, if so, fire an event. I find this work around to be buggy when I try to add animation or scroll.
Thanks for your suggestions and help.
You can set the gesture recognizer to not block the touches in subview
gestureRecognizer.cancelsTouchesInView = false
You can also implement UIGestureRecognizerDelegate and set yourself as delegate. Then you can implement shouldReceiveTouch
optional public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool
There you can check which view is targeted and return false if you don't want to react to the touch in the cells gesture recognizer.