Hide UITableViewCell from VoiceOver - ios

I have a static UITableView with various cells in it. I need to hide/show some of those cells, so I've implemented heightForRowAtIndexPath and return 0 when appropriate in order to hide the right cells. This works great for sighted users, but for those who use VoiceOver those elements are still highlighted and accessible when they should not be. How can I ensure those UITableViewCells are no longer accessible when I change their height to 0?
I've tried setting the cell to not be an accessible element as well as setting the elements to be hidden but this has no effect on it. The cell has not been subclassed - it's just a UITableViewCell. I have not set anything in regards to accessibility on the cell nor the cell's contents (textLabel, detailTextLabel).
Doesn't do the trick:
self.cellToHide.isAccessibilityElement = NO;
self.cellToHide.accessibilityElementsHidden = YES;

Update Cell VoiceOver Elements By Reloading Cell(s)
After reading about a UITableView taking control over Accessibility elements and observing apps that have similar features, I figured that a TableView must update its Accessibility information upon loading or reloading a cell. I tried forcing the cell to reload after changing its Accessibility properties and that solved the problem. VoiceOver information was updated.
The following is an example of code that runs when the cell in question is tapped. Alternatively, it could run when some other event requires that VoiceOver elements are updated.
// Make changes to accessibility properties such as
cell.isAccessibilityElement = false
cell.accessibilityElementsHidden = true
// reloadRows() allows VoiceOver to update its element list for the related cell(s)
// "indexPath" is for the desired row
// reloadRows() expects an array of IndexPaths so an array of one is created inline
tableView.reloadRows(at: [indexPath], with: .automatic)
// Calling UIAccessibilityPostNotification() is not necessary to realize the VoiceOver changes in the TableViewCell
Background
I wrestled with this problem for a while before finding a solution. In my case the TableView cells are created in code. There are no storyboards or nibs involved. However, this solution should work regardless of how the TableView was constructed.
I have custom, subclassed TableView cells with view hierarchies built in code and added as a subview of the UITableViewCell's contentView. I assumed I could modify the isAccessibilityElement and/or accessibilityElementsHidden properties of various subviews and call UIAccessibilityPostNotification() to realize the VoiceOver changes as I have done outside of TableView's. These changes were not recognized by VoiceOver, only the accessibility state the cell was in when it was loaded was recognized.
For the cell I wrestled with, the height dynamically changes to accommodate a DatePicker that is shown and hidden on cell tap. I only want the DatePicker visible to VoiceOver when it is visible on the screen. I try to avoid reloading the TableView, Sections or Rows to make dynamic changes if at all possible. If I have to reload, I try to make it as isolated as possible (reload one cell or one section not the whole TableView). In this case I did not need to reload anything to make the cell expand to reveal the DatePicker so it did not occur to me try reloading the cell for Accessibility updates.
Related Information: UIAccessibility API Reference on Apple's web site

Try adding below code after you set accessibilityElementHidden.
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, yourTableView);

Related

iOS UICollectionView switch cell NIB dynamically

I am updating my iOS app from table views to collection views to support a "gridded" option.
I have two cell types. One that is more suitable to a "tableView" type look, and takes up the full width of the screen. The other is a cell that is more suitable for grids.
I can't use the same cell type for both because the tableView type cell has a lot more horizontal information than the grid cell.
I have a button press event that will cycle between my cell types, updating the layout and changing the cell reuse identifier appropriately. The problem is, any cells that were already in view do not change to the new cell type. If I scroll for a bit, the new cell types start coming in.
I imagine that this has something to do with the dequeue function for collectionView taking an indexPath. I can explicitly reload those cells to get them to use the new type, but this causes an unwanted animation, not to mention feels like a hack.
How can I switch cell types dynamically?
Linked is a video demonstrating the issue.
https://imgur.com/1Q0Xu9S
As you can see, after I change the cells they remain using the old NIB (with all the extra information). But if I scroll down the new NIB will be used.
There are two solution for this: -
Use two collection view for each type of cell, hide and show respective collection view on click of button.
Remove the collection view from super view on click of button, add again and reload

CollectionView Cell inside UITableView is not scrolling horizontally for Voice Over accessibility

In my app, I have a screen where I need to support voice over accessibility. The screen has a TableView and inside that a collectionView. When voice over is ON and when I try to single or three fingers swipe, the collectionView scroll is not working. The accessibility focus is not moving to the next cell. Voice over is working fine only for visible cells in collectionView.
I tried by disabling accessibility for collectionView Cell and UITableViewCell but still, it didn't work.
I want to make it work like in the AppStore, where we will have a similar kind of design.
I am attaching screenshots of my screen and some codes.
On your country cell on awakeFromNib function or your init function depends how you initialize the cell add the line self.contentView.isUserInteractionEnabled = false after super call.
The purpose of your screen isn't really clear to me but I can suggest an interesting way to use collection views with VoiceOver:
Define a UIAccessibilityElement to be inserted as a carousel element (element of your collection view).
Set its trait to .adjustable in order to select the different elements by swiping right or left.
Adapt the label and the value according to the selected cell.
Define the collection view [accessibilityElements].
It may be tedious 😓 but that's what Apple's engineers recommend ⟹ a complete and detailed example is introduced in a WWDC session, don't hesitate to take a look at it to perfectly implement your collection view for the VoiceOver users. 👍
what you need is just to say to tableview cell that your collection is accessible doing something like this:
accessibilityElements = [collectionView]

Only first collectionViewCell reacts to user interactions

The setup
I have a rather complex view hierarchy which made me do terrible things with tableViews and collectionViews. Right now I'm having a regular grouped TableView with custom TableCells. TableCell itself contains a couple of views and a Collection View. The height of the table cell is calculated based on number of items in the data source. On its creation table cell creates a collection view with a calculated size to fit necessary data.
-- UITableView
---- UITableViewCell
------ UICollectionView
--------- UICollectionViewCell
The problem
I've encountered unusual problem with a custom collectionViewCell. I have a vertical single-column collectionView with dynamic amount of cells. Ideally tapping on the cell should call didSelectItemAt. The cell also has three buttons. Tapping on the button should trigger some action. All of the desired functions work only for a first cell. The rest of the cells are not responsive to any actions.
Things that look strange
By default the scrolling of a collectionView inside of the tableViewCell is disabled because it basically fits all the content based on calculated height and doesn't require scrolling. (Also I don't want it to interfere with tableView scrolling logic).
First
I've tried to hardcode some value for the height of collectionView and enable scrolling. What happened is a mystery for me.
Let's say that calculated height required for the collectionView to show all the content without scrolling is 740. When I manually set it to be 280 (this is enough for exactly 2 cells to fit) and enabled scrolling my first cell were still working, but also when I scrolled collection just a little bit my second cell started to act normally as well. When I scrolled back to the top of the collectionView it was disabled once again.
So it looks like when the scrolling is enabled and will actually occur because of insufficient height to fit the content, cells behave as they should. As soon as I set height of the collectionView to be enough to fit its content, things go wrong.
Second
In some cases I can actually tap on the second cell and it will call the delegate. But the weird thing is it works when I tap in the top area of the cell, like 10pts from the top. The other areas of the cell are unresponsive so are the rest of the cells in the collection.
The working delegates and buttons with enabled scrolling forces to think that this has nothing to do with delayed or canceled touches. The frame for collectionView and height of the table cell are calculated properly as well.
xCode 8.3, iOS9+
As it turned out when I was creating my tableViewCell, I hardcoded the height of the collection to value of 214. The reason for that is very simple. I create a cell programmatically and with tableViewCells created programmatically the height of the cell is always 44. When you override init(style: UITableViewCellStyle, reuseIdentifier: String?)
You need to either hardcore the height, or update it in cellForRowAtIndexPath. Even though I updated collection frame when it was filled with data, the container view of this collection wasn't updated resulting in some sort of clipping area for user interactions.

How to add extra table view cells to handle user input?

Recently I have been trying to create a table view with different table view cells. What I want to do is that when users click on each table view cell, it shows an extra cell underneath the selected cell to handle user inputs and the extra section disappears when the cell is unselected.
I am fairly new to iOS development and I am wondering what would be the best way to achieve this. At the moment I am thinking of hiding the extra cells initially and displaying each of them when the cell above is selected.
Any help would be appreciated.
Apple has a great set of sample code that demonstrates the behavior you're looking for — displaying a cell beneath another cell when selected. This behavior is used in Calendar when displaying a date picker, and it's pretty much what you've described.
Question: Will each cell have an identical set of options?
If so, I'd consider including the user inputs as part of the source cell and adjusting the height of the source on selection. You can animate the cell's height changing using tableView's beginUpdates and endUpdates. This way, you avoid messing around with cell indices.

UITableView cell selection disabled and UITableViewHeader Enabled

I am adding a my header view to
self.tableView.tableHeaderView=headerView;
This tableView has 10 cells.
I want to disable the cell Selection but, headerView touch events must be enabled.
To achieve this I added the following code:
self.tableView.userInteractionDisabled=YES;
self.headerView.userInteractionDisabled=NO;
self.headerView.exclusiveTouch=YES;
Where I am wrong?
Basic idea of implementation is , If headerView is enabled then cell selection is disabled and vice-versa.
I'm not sure that I completely understand what you are asking, but if you want to avoid seeing any cell highlighting set the UITableViewCell selectionStyle to UITableViewCellSelectionStyleNone. That's what I do, and then don't implement the UITableViewDelegate method tableView:didSelectRowAtIndexPath:.
I've never tried it, but I'm pretty sure you can also disallow the selection of any rows by setting the UITableView property allowsSelection to NO.
The userInteractionDisabled property of the table view should be set to NO. Otherwise your headerView, which is a subview of tableView, will not get touch events. Setting a superview's userInteractionDisabled property to no disables touch events for all of its subviews.

Resources