Open robcecil opened 6 years ago
@robcecil I am still looking into what can be done here. When scrolling a point may be either not on a row cell (e.g. header or footer). If the point is not visible (if a user scrolls beyond 400px in your example) then the layout for that area may not be calculated for performance reasons. In theory I could add in a check to manually calculate the layout in the region to determine the indexPath (if the point does correspond to a cell). I'm not sure if this would be very performant for high volume calling, but is that something which would be interesting for you?
Ok, maybe if I describe my end goal that would be more helpful. I need to attach an Edit Menu (UIMenuController) that will show when the user double-taps a cell.
@objc func wasDoubleTapped(_ sender: UITapGestureRecognizer) {
print("was double tapped ")
guard let senderView = sender.view,
let superView = sender.view?.superview
else { return }
let pt = sender.location(in: senderView)
if let indexPath = self.dataGridView.indexPathForItem(at: pt) {
if let cell = self.dataGridView.cellForItem(at: indexPath) {
cell.becomeFirstResponder()
let drillData = UIMenuItem(title: "Drill Data", action: #selector(drillDataMenuTapped))
let drillSource = UIMenuItem(title: "Drill Source", action: #selector(drillSourceMenuTapped))
UIMenuController.shared.menuItems = [drillData, drillSource]
UIMenuController.shared.setTargetRect(cell.frame, in: superView)
UIMenuController.shared.setMenuVisible(true, animated: true)
}
}
}
Unfortunately, the single-tap selection is getting in the way. What I mean is I need to invoke the Edit Menu, and keep the current selection stable. So, I have had to eschew didDeselectCellAtIndexPath and instead I have tried installing two gesture handlers - one for tap, the other for double tap:
singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(GridViewController.wasSingleTapped(_:)))
doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(GridViewController.wasDoubleTapped(_:)))
singleTapGesture.require(toFail: doubleTapGesture)
doubleTapGesture.numberOfTapsRequired = 2
singleTapGesture.numberOfTapsRequired = 1
singleTapGesture.delaysTouchesBegan = true
self.dataGridView.addGestureRecognizer(doubleTapGesture)
self.dataGridView.addGestureRecognizer(singleTapGesture)
Which brings me to my original question regarding dataGridView.indexPathForItem(at: CGPoint) always returns nil, so I have no way to get my handlers to work.
I now see what was going wrong with your attempts. Since the collectionView is private, the gesture recognizer was not pulling the expected location/point. In the short term I have added a new method in v0.6.6 which allows you to pass a gesture recognizer to get the expected location. I will need to investigate gestures as a whole more to see what can be done to make this a bit easier to implement.
Use the following instead of "sender.location(in: senderView)"
self.dataGridView.location(for: sender)
Pretty close. Except the setTargetRect also needs a valid view as the second parameter. Once the view has scrolled, I cannot get it to work, unless I expose the underlying UICollectionView and use that.
UIMenuController.shared.setTargetRect(cell.frame, in: self.dataGridView.collectionView)
Yeah, it doesn't look like there's too much of an elegant way around this short term so I also exposed the internal collectionView in v0.6.7. I'll continue looking into what can be done here.
dataGridView.indexPathForItem(at:CGPoint) works fine until I scroll around. It seems it always returns nil after I scroll for a large range of values, like