Using UICollectionView, is it possible to select multiple cells by dragging your finger over a few of them? E.g., if you drag your finger over a row of 6, and down into the next row, it would select all of them.
Tried something simple:
UISwipeGestureRecognizer *swipeGuesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture:)];
[self.collectionView addGestureRecognizer:swipeGuesture];
But that seemed to only call the method on the first cell that was touched.
Any ideas?
Swift 3: Swipe to select with auto scrolling and working scroll.
var selectMode = false
var lastSelectedCell = IndexPath()
func setupCollectionView() {
collectionView.canCancelContentTouches = false
collectionView.allowsMultipleSelection = true
let longpressGesture = UILongPressGestureRecognizer(target: self, action: #selector(didLongpress))
longpressGesture.minimumPressDuration = 0.15
longpressGesture.delaysTouchesBegan = true
longpressGesture.delegate = self
collectionView.addGestureRecognizer(longpressGesture)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(toSelectCells:)))
panGesture.delegate = self
collectionView.addGestureRecognizer(panGesture)
}
func selectCell(_ indexPath: IndexPath, selected: Bool) {
if let cell = collectionView.cellForItem(at: indexPath) {
if cell.isSelected {
collectionView.deselectItem(at: indexPath, animated: true)
collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredVertically, animated: true)
} else {
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: UICollectionViewScrollPosition.centeredVertically)
}
if let numberOfSelections = collectionView.indexPathsForSelectedItems?.count {
title = "\(numberOfSelections) items selected"
}
}
}
func didPan(toSelectCells panGesture: UIPanGestureRecognizer) {
if !selectMode {
collectionView?.isScrollEnabled = true
return
} else {
if panGesture.state == .began {
collectionView?.isUserInteractionEnabled = false
collectionView?.isScrollEnabled = false
}
else if panGesture.state == .changed {
let location: CGPoint = panGesture.location(in: collectionView)
if let indexPath: IndexPath = collectionView?.indexPathForItem(at: location) {
if indexPath != lastSelectedCell {
self.selectCell(indexPath, selected: true)
lastSelectedCell = indexPath
}
}
} else if panGesture.state == .ended {
collectionView?.isScrollEnabled = true
collectionView?.isUserInteractionEnabled = true
swipeSelect = false
}
}
}
func didLongpress() {
swipeSelect = true
}
You could use UIPanGestureRecognizer. And based on the location of the pan events, tracking what cells are passed through. When the gesture ends, you would have an array of selected cells.
Make sure that cancelsTouchesInView is set to NO. You'll need to set the delegate with gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: and gestureRecognizerShouldBegin implemented to make sure the CollectionView can still scroll
In iOS 13 and later, you can simply use the built-in multiselection gestures. See Selecting Multiple Items with a Two-Finger Pan Gesture. Make sure you set collectionView.allowsMultipleSelectionDuringEditing to true.
Related
Currently, I have a collection view with a UILongPressGestureRecognizer on the cell in cellForItemAt:
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressOnCell))
cell.addGestureRecognizer(longPress)
When the user holds down on a cell, it triggers a function to show a menu called cellDeleteAppear(). However, after the menu is on the screen, the user can then hold down on another cell which will cause the menu to pop up again.
#objc func handleLongPressOnCell(_ sender: UILongPressGestureRecognizer) {
if sender.state == .began {
cellDeleteAppear()
let gestureLocation = sender.location(in: self.trayCollectionView)
if let indexPath = self.trayCollectionView.indexPathForItem(at: gestureLocation) {
indexPathForDeletion = indexPath
trayCollectionView.allowsSelection = false
} else {
print("long press error at index path")
}
}
}
My goal is: while the menu is active, the user should not be able to hold down on another cell to trigger the menu to pop up. Any help is appreciated!
Then do
var menuShown = false
#objc func handleLongPressOnCell(_ sender: UILongPressGestureRecognizer) {
if sender.state == .began {
guard !menuShown else { return }
menuShown = true
And when you hide it do
menuShown = false
Hello i am adding UILongPressGesture in my tableview and i have added successfully but issue is that how to show that cell is selected i mean i want change color of selected cell and when i again do longpress on selected cell than i want to delselect cell
i have try to add long press in my tableview with code and assign delegates on LongPress here is my code
#objc func longPress(_ longPressGestureRecognizer: UILongPressGestureRecognizer) {
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: self.tblList)
if let indexPath = tblList.indexPathForRow(at: touchPoint) {
}
}
}
And in viewDidload() i am writing this code
let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(SMSChatViewController.longPress(_:)))
longPressGesture.minimumPressDuration = 1.0 // 1 second press
longPressGesture.delegate = self
self.tblList.addGestureRecognizer(longPressGesture)
so from this code i am able to select cell but how to show user that the cell is selected i don't know how to do this
so i just want like that when user is do longpress than cell color change and set as selected and then again do longpress than deselect cell with its original color
What about you change just the backgroundColor of the cell, when the longPressGesture is recognized? Something like this:
#objc func longPress(_ longPressGestureRecognizer: UILongPressGestureRecognizer) {
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: self.tblList)
if let indexPath = tblList.indexPathForRow(at: touchPoint) {
let cell = tblList.cellForRow(at: indexPath)
if (cell.isSelected) {
cell.backgroundColor = UIColor.clear // or whatever color you need as default
cell.setSelected(false, animated: true)
} else {
cell.backgroundColor = UIColor.orange
cell.setSelected(true, animated: true)
}
}
}
}
If you need clarification or i missunderstood a thing let me know and i will edit my answer.
I have a UITableView in ViewController and 3 UILabel in it with a Prototype Cell labeled Date, Party and Amount. Now situation is that i want whenever a user tapped on a UILabel, say Date, it shows all records from Database table for particular Date or if User tapped on Party Label then All transactions of particular party will be shown. So please help me in getting this. How could i do this..
There are many to do the same.
You can use button without any border or background image. Just give simple text to button and it would look like label. Add action to the button.
You can use UIGestureRecognizers for the same. You can add Gesture Recognizer directly to all UILabel separately as explained by #LalitKumar. .
You can also add Gesture Recognizer to your Table View and access all its subviews in your UIGesture Recognizer Action .
Add Gesture Recognizer in viewDidLoad as
let tap = UITapGestureRecognizer(target: self, action: #selector(Yourcontroller.handleTap))
yourTableView.isUserInteractionEnabled = true
yourTableView.addGestureRecognizer(tap)
Define action for Gesture Recognizer and get indexpath of cell as well as subviews of cell.
func handleTap(_ gesture: UITapGestureRecognizer){
let tapLocation = gesture.location(in: self.tableView)
if let tapIndexPath = self.tableView.indexPathForRow(at: tapLocation)
{
if let tappedCell = self.tableView.cellForRow(at: tapIndexPath) as? UITableViewCell
{
print("Row Selected") // access tapped cell and its subview as
let touchpoint:CGPoint = gesture.location(in: cell)
if cell.datelabel.frame.contains(touchpoint) {
// user tapped date label
} elseif cell.imageView.frame.contains(touchpoint) {
// user tapped image
}
}
}
}
let tap = UITapGestureRecognizer(target: self, action: #selector(Yourcontroller.tapLabelFunction))
yourLabel.isUserInteractionEnabled = true
yourLabel.addGestureRecognizer(tap)
// if you make label on the cell
cell.yourLabel.userInteractionEnabled = true
cell.yourLabel.tag = indexPath.row
cell.yourLabel.addGestureRecognizer(tap)
}
func tapLabelFunction(sender:UITapGestureRecognizer) {
print("label tapped",sender.view!.tag); // for tag
}
Add this in cellForRowAtIndexPath
yourLabel?.isUserInteractionEnabled = true
if ((yourLabel?.gestureRecognizers?.description) == nil) {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tap(onPeopleName:)))
tapGesture.delegate = self
tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1
tapGesture.cancelsTouchesInView = false
yourLabel?.addGestureRecognizer(tapGesture)
}
Selector
func tap(onPeopleName gesture: UIGestureRecognizer) {
let lbl: UILabel? = (gesture.view as? UILabel)
let cell: UITableViewCell? = (lbl?.superview?.superview as? UITableViewCell)
let indexPath: IndexPath? = tblViewPracticeDetail.indexPath(for: cell!)
}
The default behavior while a collection view is mid-scroll:
tap #1: stops the scrolling
tap #2: triggers didSelectItemAtIndexPath
What I want while a collection view is mid-scroll:
tap #1: triggers didSelectItemAtIndexPath
What would be a clean, correct approach to achieve this? FWIW, I realize this might be unexpected behavior.
I think the best approach is to use the UICollectionView addGestureRecognizer to add a touch gesture recognizer, then process the touch gesture (e.g. get the touch location in the collection view, use that to get the indexPath of the item that was touched, then call the collectionView.didSelectItemAtIndexPath yourself). As for the scrolling, you could use the UISrollViewDelegate methods to disable user interaction on the collection view once the scroll starts, then re-enable user interaction on the collection view once the scrolling stops and/or in the viewDidDisappear view controller function.
Like this:
public class MyViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var collectionViewTap:UITapGestureRecognizer?
override public func viewDidLoad() {
collectionViewTap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
self.view.addGestureRecognizer(collectionViewTap!)
}
override public func viewDidDisappear(animated: Bool) {
collectionView.userInteractionEnabled = true
}
func handleTap (sender:UITapGestureRecognizer) {
let touchPoint = sender.locationOfTouch(0, inView: collectionView)
let indexPath = collectionView.indexPathForItemAtPoint(touchPoint)
if (indexPath != nil) {
collectionView(collectionView, didSelectItemAtIndexPath: indexPath!)
}
}
public func scrollViewWillBeginDragging(scrollView: UIScrollView) {
collectionView.userInteractionEnabled = false
}
public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
collectionView.userInteractionEnabled = true
}
}
In your initializer for the collection view, add an additional target for the pan gesture
self.panGestureRecognizer.addTarget(self, action: #selector(allowSelectionOfItemDuringScroll(_:)))
Then, you can implement it like this:
#objc private func allowSelectionOfItemDuringScroll(_ sender: UIPanGestureRecognizer) {
let yTranslation = sender.translation(in: self).y
var isScrolling: Bool {
if sender.state == .began {
return false
}
if isDragging && isDecelerating {
return false
}
return isDragging || isDecelerating
}
if yTranslation == 0 && isScrolling {
let selectionPoint = sender.translation(in: self)
if let index = indexPathForItem(at: selectionPoint) {
self.delegate?.collectionView?(self, didSelectItemAt: index)
}
}
I have a UITableView with multiple sections. Each section has a section header (a custom view) is there an easy way to detect when someone selects the section header? (Just like didSelectRowAtIndexPath, but for the header?)
This isn't radically different than #rckoenes answer, but it does provide a more orthodox way of handling events on views rather than using invisible buttons.
I'd rather add a UITapGestureRecognizer to my header view instead of adding invisible buttons and resizing them:
UITapGestureRecognizer *singleTapRecogniser = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)] autorelease];
[singleTapRecogniser setDelegate:self];
singleTapRecogniser.numberOfTouchesRequired = 1;
singleTapRecogniser.numberOfTapsRequired = 1;
[yourHeaderView addGestureRecognizer:singleTapRecogniser];
and then:
- (void) handleGesture:(UIGestureRecognizer *)gestureRecognizer;
You can use gesture.view to see which was touched. Then do whatever you need to do to find out which header it was (tags, data array lookup... )
No there is no way to do it with the UITableViewDelegate.
What you can do is to add a button the size of the section header view and add it to the view. Set the tag of the button to the section index.
Then just add the UIViewController as a target for the UIControlEventTouchUpInside.
Then by looking at the tag of the button you can see which section is clicked.
Here is what worked for me in Swift 2:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let footerView = UITableViewHeaderFooterView()
footerView.textLabel?.text = "Header Text"
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
tapRecognizer.delegate = self
tapRecognizer.numberOfTapsRequired = 1
tapRecognizer.numberOfTouchesRequired = 1
footerView.addGestureRecognizer(tapRecognizer)
return footerView
}
#objc func handleTap(gestureRecognizer: UIGestureRecognizer) {
print("Tapped")
}
This worked for evaluating the section and row. I hope it helps everybody else who is struggling to get this work properly...
override func viewDidLoad() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(sectionTapped(sender:)))
tableView?.addGestureRecognizer(tapGesture)
tapGesture.delegate = self as? UIGestureRecognizerDelegate
}
#objc func sectionTapped(sender: UITapGestureRecognizer) {
if sender.state == UIGestureRecognizerState.ended {
guard let tableView = self.tableView else {
return
}
if let view = sender.view {
let tapLocation = sender.location(in: tableView)
if let tapIndexPath = tableView.indexPathForRow(at: tapLocation) {
if (tableView?.cellForRow(at: tapIndexPath) as? UITableViewCell) != nil {
// do something with the row
print("tapped on row at index: \(tapIndexPath.row)")
}
} else {
for i in 0..<tableView.numberOfSections {
let sectionHeaderArea = tableView.rectForHeader(inSection: i)
if sectionHeaderArea.contains(tapLocation) {
// do something with the section
print("tapped on section at index: \(i)")
}
}
}
}
}
}