8

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?

Suraj
  • 81
  • 1
  • 2
  • did you try changeing the attributes of the view returned in the header view? – Teja Nandamuri Aug 26 '16 at 14:53
  • 1
    Hi 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 Answers5

7

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.

CocoaAficionado
  • 189
  • 1
  • 8
2

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 UIAccessibilityElements 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

UIViews 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

Ashton-W
  • 216
  • 1
  • 9
0

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)
        }
    }

}

abalas
  • 1
0

@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)
           }
       }

  • 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
-1

Rewrite setAccessibilityTraits of these controls such as buttons and segmented controls.

For example:

- (void)setAccessibilityTraits:(UIAccessibilityTraits)accessibilityTraits
{
    accessibilityTraits = UIAccessibilityTraitButton;
    [super setAccessibilityTraits:accessibilityTraits];
}
applezqp
  • 1
  • 4