I have a UICollectionView where I overrode hitTest:withEvent: in my UICollectionViewCells in order to allow for taps just outside of the cells to register as taps on the cells.
When I do this and I tap just outside the cells that now register as hits, I get calls to didHighlightItemAtIndexPath and didUnhighlightItemAtIndexPath, but I don't get a call to didSelectItemAtIndexPath. If I tap inside the cell I get all of the expected highlight and select item calls as I did before.
I don't have any custom gesture recognizers set up and I don't override touchesBegan or anything like that.
So does anyone know under what conditions you get a call for didHighlightItemAtIndexPath without a call to didSelectItemAtIndexPath? Is there any way to get my didSelectItemAtIndexPath called? Thanks.
EDIT
I forgot to mention that my UICollectionView is within a Today Widget, so it is contained within the Notification Center scroll view. If I move my select code into the didUnhighlightItemAtIndexPath, then it is called when you tap outside the cell, but the result is that you can't actually scroll the Notification Center without selecting one of the cells.
So perhaps the difference between the highlighting and selecting that I'm experiencing here has something to do with the scroll view responder canceling the selection outside of the cell?
OK, I figured out what was going on.
I added a new UITapGestureRecognizer to my UICollectionView. Implementing it like this led me the the solution:
- (void)cellSingleTap:(UITapGestureRecognizer *)sender
{
CGPoint point = [sender locationInView:collectionView_];
NSIndexPath *indexPath = [collectionView_ indexPathForItemAtPoint:point];
[ .... ]
}
When I checked the points returned when I got highlighting but no selection, it became apparent that it happened when the point tapped on was within the section insets of the collection view layout. And when the taps were on the section insets, the indexPathForItemAtPoint calls returned nil.
So basically the collection view will highlight, but not select, taps that are outside cells but are within its section insets. As long as the taps are outside the cells and not within the insets, those taps will result in calls to didSelectItemAtIndexPath.
Since I would like taps within the insets to count as taps on cells, I was able to workaround this issue by adjusting the tap points before my call to indexPathForItemAtPoint.
Related
I'm adding Accessibility support to my iOS app and I'm having trouble with a collection view in one of my table view cells.
For example, when the user scrolls (horizontally) from the first cell to the second cell, Accessibility still reads the contents of the first cell. If I try to tap on a view in the second cell, it highlights an empty space to the left of the second cell (where the first cell would be but no longer visible) and reads the contents of the first cell.
When the collection view is not in a table view cell (i.e. a subview of a UIView), this does not happen.
I'm suspecting this has something to do with calling UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification) and I've tried calling it in many different places, but nothing has worked.
The following two screenshots show a collection view inside a UIView. Accessibility is enabled, so it gets selected with a black border when tapped.
When user taps first cell, it will get selected.
When the user taps 'Next', goes to the second cell, and taps the cell, the new cell will get selected.
The next two screenshots show the collection view inside a table view cell.
When the user taps the first cell, it gets selected and VoiceOver properly reads "I'm label 0".
However, when the user taps 'Next', goes to the next cell, and taps the second cell, it does not get selected and VoiceOver will still read, "I'm label 0".
The code is available here on github.
I had simillar problem which I eventually resolved.
I think that you have mismatched elements with
isAccessibilityElement = true
over each other.
I have a table view which scrolls vertically and each cell contains a title and collection view which scrolls horizontally.
I set isAccessibilityElement to true only on title and collection view cells, false on the rest.
Then, I subclassed UICollectionView and overrode the following NSObject methods:
func accessibilityElementCount() -> Int
func accessibilityElement(at: Int) -> Any?
func index(ofAccessibilityElement element: Any) -> Int
It basically just tells the voice over that your collection view has these accessible elements. The collection view itself is not accessible which is not a problem, the contrary. You could probably use
open var accessibilityElements: [Any]?
instead.
Here is some more reading from documentation (UIAccessibility.h):
UIAccessibilityContainer methods can be overridden to vend individual elements that are managed by a single UIView.
For example, a single UIView might draw several items that (to an end user) have separate meaning and functionality. It is important to vend each item as an individual accessibility element.
Sub-elements of a container that are not represented by concrete UIView
instances (perhaps painted text or icons) can be represented using instances of UIAccessibilityElement class (see UIAccessibilityElement.h).
Accessibility containers MUST return NO to -isAccessibilityElement.
To allow nice 3-finger voice over scroll you probably want to override
func accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool
as well and scroll your collection view accordingly.
You may want to try this:
let nextCell = collectionView.cellForItemAtIndexPath(nextIndexPath)
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
argument: nextCell)
in your onNextButtonTapped method, after your call to scrollToItemAtIndexPath.
This will focus Accessibility on the next collection view cell.
I have got a TableView and each cell is covered by a Text View.
I think that because of this reason I can't select any cell.
Whenever I try to print it I don't get anything:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var i = Int()
i = indexPath.row
print("i=\(i)")
}
**Its print and not println because of Swift 2.0.
Please ensure following:
You are correctly setting your view controller as delegate of the table view.
Your touches are intercepted by text view and are not reaching underneath cell. To fix this set userInteractionEnabled to false in cellForRowAtIndexPath: and set it back to true in didSelectRowAtIndexPath:.
Take a look at UITextView's userInteractionEnabled, multipleTouchEnabled, and exclusiveTouch. Those properties alter whether or not a view receives touches; if it doesn't, the touches get passed to the view behind it (or at least I think that's how it works). I forget where it is, but there's also a method that's called to ask a view which view should be the target of a specific touch, which would allow you to explicitly tell it to send them to the underlying view.
However, from a design perspective, I would re-evaluate having a UITextView on top of a UITableViewCell - the text view is a scroll view and the cell is in a scroll view, so they will always conflict. UILabel is generally more appropriate for putting in a text view.
For those who are using storyboard just uncheck User Interaction Enabled and Multiple Touch as showing below in the screenshot:
I have listed below points for your solution.
you need to check delegate is connected to your view controller.
next you need to check table view selection is enabled for your table view you can check it from table view attributed inspector.
next make sure user interaction is enabled for your text view.
Hope it will be the right solution for you.
I am able to detect when a cell is removed from the UITableView by writing a handler for the method tableView:didEndDisplayingCell:forRowAtIndexPath:. This method is called whenever a cell is removed from the display. However, there is one exception.
When the cell has a UITextField and the field is the first responder, this method is never called even when it's scrolled off the display and the cells immediately before it and after it are.
The cell is also confirmed to be removed from the UITableView with a test while the cell is scrolled off the screen. The call to cellForRowAtIndexPath: returns nil under this condition.
I also subclassed the UITableViewCell and wrote a handler for removeFromSuperView. Again this method is called for all the cells when the are scrolled off the screen except when the cell has a UITextField and it is the first responder.
Another thing to note is that the UITextField in the cell accepts key input while it is scrolled off the screen and the call to cellForRowAtIndexPath: returns nil. I can see this when the cell is scrolled back into view.
Does anyone have any solutions for detecting when the cell is scrolled out of view, so that the controller can get access to the UITextField?
You could try to resign your first responder manually before the cell disappears. Depending on your requirements, this could be done in multiple ways, usually when the user starts scrolling. You could restore the first responder after he finishes scrolling, if the cell is still visible. Probably better from the graphical design point of view as well.
Alternatively, you could try to implement delegate's tableView:willDisplayCell:forRowAtIndexPath: and make a previous visible cells set, which you'd intersect with tableview's visibleCells array. The elements that are not in the current visibleCells but are in the previous were removed. Then assign a copy of visibleCells to previousVisibleCells.
This is probably a bug in Apple's code, you could file a radar for it.
In my app i have a main UITableView, and every cell has its own functionality.
All the cells look like a disk case, with a disk inside, and when the user clicks on the cell, the disk animates itself out of the case, and disappears.
This animation is preformed by the UITableViewCell.
To make this animation possible, i had to make the disk view, a SubView of the UITableView superclass, so the animation can continue out of the cell.
-Till here everything works perfectly!
The problem:
Every time a UITableViewCell is shown, the UITableView calls the UITableViewCells method:
(void)refresh{
//Disk configuration:
[self.diskImage setHidden:NO];
[self addSubview:self.diskMainView];
[self.diskMainView setFrame:originalDiskFrame];
}
This method is returning the "diskMainView" to the original subview (self = UITableViewCell)
After this happens, the diskMainView View, is in the right place but it receives no user interactions,
So all the buttons that are on it do not call any methods.
I don't understand what is happening here.. i'm thinking maybe once you add UIViews and UIButtons form the cell, to itself, it cannot receive user interactions...
Any suggestions would help!
Thanks!!
I wanted to find out how to do the following:
I have a UItableView, which enters edit mode when the user taps and holds one of the rows
Once it enters edit mode I need the cell to remain selected and give the effect of popping out, under the users finger.
The user should be able to drag the popped out cell and reposition it to another row, without lifting the finger.
What I already have in place:
I have a long tap gesture recognizer and I set the table into editing in the long tap gesture recognizer selector.
However in order to drag the cell I currently require to raise the finger and re tap to drag the cell , which is not what I want.
Any help would be appreciated.
At current moment there is a workaround (not easy enough): https://stackoverflow.com/a/7501076/326017 .
And I have found code snippet here: https://github.com/FlorianMielke/FMMoveTableView
"FMMoveTable is an UITableView subclass that provides moving rows by simply tap and hold an appropriate row without switching the table to it's edit mode" - from description
The article Reordering a UITableViewCell from any touch point discusses this exact scenario.
Essentially you do the following:
Find the UITableViewCellReorderControl (a private class).
Expand it so it spans the entire cell.
Hide it.
The user will now be able to drag the cell from anywhere.
Another solution, Cookbook: Moving Table View Cells with a Long Press Gesture, achieves the same effect by doing the following:
Add a long press gesture recognizer on the table view.
Create a snapshot of the cell when the cell is dragged.
As the cell is dragged, move the snapshot around, and call the -[UITableView moveRowAtIndexPath:toIndexPath:].
When the gesture ends, hide the cell snapshot.