So, I have a tableview and inside the viewforheaderinsection, I create a view, create some controls such as buttons and segmented controls programmatically. I add those controls as subview of the view and then return the view. The problem is when Accessibility reads the controls, it appends "heading" at the end. It says "button" pauses and then says "heading". I know I can convert the headerview to cells to suppress the "heading" callout but that is not an option. The project is pretty big and it requires a lot of time to change headerviews to cells. Is there a way to suppress the "heading" callout without changing headerview to cell?
-
did you try changeing the attributes of the view returned in the header view? – Teja Nandamuri Aug 26 '16 at 14:53
-
1Hi Teja, I did not understand what you meant. Can you expand on your answer? Thanks. – Suraj Aug 29 '16 at 21:54
-
The button element has an attribute button, so the voice over says button at the end for all the button type elements. Similary it says heading in your case. you can change the attribute for the header view returned by the tableView and give the attribtue as static text. – Teja Nandamuri Aug 30 '16 at 12:54
-
Fortunately I only have 1 section, so I ended up with a separate subview and a tableview.contentInset. Could not get it working in iOS 12 with any of the suggested solutions, – Rool Paap Dec 07 '18 at 10:26
-
I am struggling with the same issue and no answer which is mentioned below is not working. Any other ways to fix the issue?? – kumar reddy Mar 25 '19 at 16:28
-
4 years later and this is still broken. Accessibility on iOS is the most poorly implemented feature by Apple I have ever seen. Broken. – RunLoop Feb 07 '20 at 14:06
5 Answers
You need to implement
-(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section;
for example
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
view.accessibilityTraits = UIAccessibilityTraitNone;
}
inside that function, set the accessibility trait for the header to UIAccessibilityTraitNone, or simply remove UIAccessibilityTraitHeader.
Doing it inside
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
will not do any good because the iOS will add the header trait later on. You need to modify the trait right before the header is being displayed.

- 189
- 1
- 8
The answer by @cocoaaficionado did not work for me on iOS 10.3.
What I had to do was the following:
For your header view, you need to froce by subclassing, the accessibilityElements
property of the view, and use UIAccessibilityElement
s instead of UIView
/UILabel
/UIButton
directly.
@interface NotAHeadingView : UIView
@end
@implementation NotAHeadingView
- (void)layoutSubviews
{
[super layoutSubviews];
NSMutableArray *accessibilityElements = [NSMutableArray arrayWithCapacity:self.subviews.count];
for (UIView *view in self.subviews) {
UIAccessibilityElement *element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
// remove Header trait
element.accessibilityTraits = view.accessibilityTraits & ~UIAccessibilityTraitHeader;
element.accessibilityFrame = view.accessibilityFrame;
element.accessibilityLabel = view.accessibilityLabel;
element.accessibilityValue = view.accessibilityValue;
element.accessibilityHint = view.accessibilityHint;
element.accessibilityFrameInContainerSpace = view.frame;
[accessibilityElements addObject:element];
}
self.accessibilityElements = accessibilityElements;
}
@end
UIView
s seem to automatically inherit the Header trait from the view hierarchy, even overriding traits for their direct superview isn't enough. This worked on an iOS 10.3 device for me.
This works for simple cases, but did not work for me when applied to a very complex section header view (multiple child viewControllers managing the subviews, deep view hierarchy including scrollViews) - instead it was much easier to resign how this view worked to avoid section headers for these elements. YMMV

- 216
- 1
- 9
Swift solution
Subclassing header elements and overriding their accessibilityTraits
property was the easiest solution for me.
For example, I had the same situation for buttons in header, and here's the solution that works for me:
class HeaderButton: UIButton {
private var _accessibilityTraits: UIAccessibilityTraits = UIAccessibilityTraits.button
override var accessibilityTraits: UIAccessibilityTraits {
get {
return _accessibilityTraits
}
set {
_accessibilityTraits = newValue
_accessibilityTraits.remove(UIAccessibilityTraits.header)
}
}
}

- 1
@abalas solution works for me:
Try using.
private var _accessibilityTraits: UIAccessibilityTraits = UIAccessibilityTraits.none
override var accessibilityTraits: UIAccessibilityTraits {
get {
return _accessibilityTraits
}
set {
_accessibilityTraits = newValue
_accessibilityTraits.remove(UIAccessibilityTraits.header)
}
}

- 31
- 2
-
Can you explain why this answer fixes the OPs original question? Posting just code snippets can be confusing. – Harry J Sep 24 '20 at 11:06
Rewrite setAccessibilityTraits
of these controls such as buttons and segmented controls.
For example:
- (void)setAccessibilityTraits:(UIAccessibilityTraits)accessibilityTraits
{
accessibilityTraits = UIAccessibilityTraitButton;
[super setAccessibilityTraits:accessibilityTraits];
}

- 1
- 4