无法为自定义 UITableViewCell 配置语音辅助功能
Unable to configure voice over accessibility for a custom UITableViewCell
我们的 iPhone 应用目前支持 IOS 8/9/10。我很难支持自定义 UITableViewCell 的语音辅助功能。我浏览了以下 SO 帖子,但 none 的建议已经奏效。我希望可以访问各个组件。
- Custom UITableview cell accessibility not working correctly
- Custom UITableViewCell trouble with UIAccessibility elements
- Accessibility in custom drawn UITableViewCell
- https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html#//apple_ref/doc/uid/TP40008785-CH102-SW10
- http://useyourloaf.com/blog/voiceover-accessibility/
对我来说不幸的是,可访问性检查器没有检测到该单元格。有没有一种方法可以通过可访问性来选择 table 视图单元格中的单个元素?在设备和模拟器上调试这个问题时,我发现 XCode 调用了 isAccessibleElement
函数。当函数returnsNO
时,则跳过其余方法。我正在 XCode 中的 IOS 9.3 上进行测试。
我的自定义 table 视图单元格由一个标签和一个开关组成,如下所示。
标签添加到内容视图,开关添加到自定义附件视图。
接口定义如下
@interface MyCustomTableViewCell : UITableViewCell
///Designated initializer
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
///Property that determines if the switch displayed in the cell is ON or OFF.
@property (nonatomic, assign) BOOL switchIsOn;
///The label to be displayed for the alert
@property (nonatomic, strong) UILabel *alertLabel;
@property (nonatomic, strong) UISwitch *switch;
#pragma mark - Accessibility
// Used for setting up accessibility values. This is used to generate accessibility labels of
// individual elements.
@property (nonatomic, strong) NSString* accessibilityPrefix;
-(void)setAlertHTMLText:(NSString*)title;
@end
下面给出了实现块
@interface MyCustomTableViewCell()
@property (nonatomic, strong) UIView *customAccessoryView;
@property (nonatomic, strong) NSString *alertTextString;
@property (nonatomic, strong) NSMutableArray* accessibleElements;
@end
@implementation MyCustomTableViewCell
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if(self = [super initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:reuseIdentifier]) {
[self configureTableCell];
}
return self;
}
- (void)configureTableCell
{
if (!_accessibleElements) {
_accessibleElements = [[NSMutableArray alloc] init];
}
//Alert label
self.alertLabel = [[self class] makeAlertLabel];
[self.contentView setIsAccessibilityElement:YES];
//
[self.contentView addSubview:self.alertLabel];
// Custom AccessoryView for easy styling.
self.customAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
[self.customAccessoryView setIsAccessibilityElement:YES];
[self.contentView addSubview:self.customAccessoryView];
//switch
self.switch = [[BAUISwitch alloc] initWithFrame:CGRectZero];
[self.switch addTarget:self action:@selector(switchWasFlipped:) forControlEvents:UIControlEventValueChanged];
[self.switch setIsAccessibilityElement:YES];
[self.switch setAccessibilityTraits:UIAccessibilityTraitButton];
[self.switch setAccessibilityLabel:@""];
[self.switch setAccessibilityHint:@""];
self.switch.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self.customAccessoryView addSubview:self.switch];
}
+ (UILabel *)makeAlertLabel
{
UILabel *alertLabel = [[UILabel alloc] initWithFrame:CGRectZero];
alertLabel.backgroundColor = [UIColor clearColor];
alertLabel.HTMLText = @"";
alertLabel.numberOfLines = 0;
alertLabel.lineBreakMode = LINE_BREAK_WORD_WRAP
[alertLabel setIsAccessibilityElement:YES];
return alertLabel;
}
-(void)setAlertHTMLText:(NSString*)title{
_alertTextString = [NSString stringWithString:title];
[self.alertLabel setText:_alertTextString];
}
- (BOOL)isAccessibilityElement {
return NO;
}
// The view encapsulates the following elements for the purposes of
// accessibility.
-(NSArray*) accessibleElements {
if (_accessibleElements && [_accessibleElements count] > 0) {
[_accessibleElements removeAllObjects];
}
// Fetch a new copy as the values may have changed.
_accessibleElements = [[NSMutableArray alloc] init];
UIAccessibilityElement* alertLabelElement =
[[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
//alertLabelElement.accessibilityFrame = [self convertRect:self.contentView.frame toView:nil];
alertLabelElement.accessibilityLabel = _alertTextString;
alertLabelElement.accessibilityTraits = UIAccessibilityTraitStaticText;
[_accessibleElements addObject:alertLabelElement];
UIAccessibilityElement* switchElement =
[[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
// switchElement.accessibilityFrame = [self convertRect:self.customAccessoryView.frame toView:nil];
switchElement.accessibilityTraits = UIAccessibilityTraitButton;
// If you want custom values, just override it in the invoking function.
NSMutableString* accessibilityString =
[NSMutableString stringWithString:self.accessibilityPrefix];
[accessibilityString appendString:@" Switch "];
if (self.switchh.isOn) {
[accessibilityString appendString:@"On"];
} else {
[accessibilityString appendString:@"Off"];
}
switchElement.accessibilityLabel = [accessibilityString copy];
[_accessibleElements addObject:switchElement];
}
return _accessibleElements;
}
// In case accessibleElements is not initialized.
- (void) initializeAccessibleElements {
_accessibleElements = [self accessibleElements];
}
- (NSInteger)accessibilityElementCount
{
return [_accessibleElements count]
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
[self initializeAccessibleElements];
return [_accessibleElements objectAtIndex:index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
[self initializeAccessibleElements];
return [_accessibleElements indexOfObject:element];
}
@end
首先,根据您描述的模式,我不确定您为什么要区分单元格中的不同元素。通常,Apple 将每个单元格都保留为一个可访问性元素。设置应用程序中是查看带有标签和开关的单元格的预期 iOS VO 行为的好地方。
如果您仍然认为处理单元格的最佳方式是让它们包含单独的元素,那么这实际上是单元格的默认行为当 UITableViewCell 本身不存在时有无障碍标签。所以,我修改了下面的代码,并在我的 iOS 设备 (运行ning 9.3) 上 运行 修改了它,它按照你描述的方式工作。
您会注意到一些事情。
- 我删除了所有自定义 accessibilityElements 代码。没必要。
- 我删除了 UITableViewCell 子类本身的 isAccessibilityElement 覆盖。我们想要默认行为。
- 我注释掉了将内容视图设置为 accessibilityElement——我们希望它为 NO,以便树构建器在其中查找元素。
- 出于与上述相同的原因,我也将 customAccessoryView 的 isAccessibilityElement 设置为 NO。通常,NO 表示 "keep looking down the tree",YES 表示 "stop here, this is my leaf as far as accessibility is concerned."
希望对您有所帮助。再一次,我确实鼓励您在设计辅助功能时模仿 Apple 的 VO 模式。我认为您确保您的应用程序可供访问,这很棒!
#import "MyCustomTableViewCell.h"
@interface MyCustomTableViewCell()
@property (nonatomic, strong) UIView *customAccessoryView;
@property (nonatomic, strong) NSString *alertTextString;
@property (nonatomic, strong) NSMutableArray* accessibleElements;
@end
@implementation MyCustomTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if(self = [super initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:reuseIdentifier]) {
[self configureTableCell];
}
return self;
}
// just added this here to get the cell to lay out for myself
- (void)layoutSubviews {
[super layoutSubviews];
const CGFloat margin = 8;
CGRect b = self.bounds;
CGSize labelSize = [self.alertLabel sizeThatFits:b.size];
CGFloat maxX = CGRectGetMaxX(b);
self.alertLabel.frame = CGRectMake(margin, margin, labelSize.width, labelSize.height);
CGSize switchSize = [self.mySwitch sizeThatFits:b.size];
self.customAccessoryView.frame = CGRectMake(maxX - switchSize.width - margin * 2, b.origin.y + margin, switchSize.width + margin * 2, switchSize.height);
self.mySwitch.frame = CGRectMake(margin, 0, switchSize.width, switchSize.height);
}
- (void)configureTableCell
{
//Alert label
self.alertLabel = [[self class] makeAlertLabel];
//[self.contentView setIsAccessibilityElement:YES];
//
[self.contentView addSubview:self.alertLabel];
// Custom AccessoryView for easy styling.
self.customAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
[self.customAccessoryView setIsAccessibilityElement:NO]; // Setting this to NO tells the the hierarchy builder to look inside
[self.contentView addSubview:self.customAccessoryView];
self.customAccessoryView.backgroundColor = [UIColor purpleColor];
//switch
self.mySwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
//[self.mySwitch addTarget:self action:@selector(switchWasFlipped:) forControlEvents:UIControlEventValueChanged];
[self.mySwitch setIsAccessibilityElement:YES]; // This is default behavior
[self.mySwitch setAccessibilityTraits:UIAccessibilityTraitButton]; // No tsure why this is here
[self.mySwitch setAccessibilityLabel:@"my swich"];
[self.mySwitch setAccessibilityHint:@"Tap to do something."];
self.mySwitch.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self.customAccessoryView addSubview:self.mySwitch];
}
+ (UILabel *)makeAlertLabel
{
UILabel *alertLabel = [[UILabel alloc] initWithFrame:CGRectZero];
alertLabel.backgroundColor = [UIColor clearColor];
alertLabel.text = @"";
alertLabel.numberOfLines = 0;
[alertLabel setIsAccessibilityElement:YES];
return alertLabel;
}
-(void)setAlertHTMLText:(NSString*)title{
_alertTextString = [NSString stringWithString:title];
[self.alertLabel setText:_alertTextString];
}
@end
我们的 iPhone 应用目前支持 IOS 8/9/10。我很难支持自定义 UITableViewCell 的语音辅助功能。我浏览了以下 SO 帖子,但 none 的建议已经奏效。我希望可以访问各个组件。
- Custom UITableview cell accessibility not working correctly
- Custom UITableViewCell trouble with UIAccessibility elements
- Accessibility in custom drawn UITableViewCell
- https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html#//apple_ref/doc/uid/TP40008785-CH102-SW10
- http://useyourloaf.com/blog/voiceover-accessibility/
对我来说不幸的是,可访问性检查器没有检测到该单元格。有没有一种方法可以通过可访问性来选择 table 视图单元格中的单个元素?在设备和模拟器上调试这个问题时,我发现 XCode 调用了 isAccessibleElement
函数。当函数returnsNO
时,则跳过其余方法。我正在 XCode 中的 IOS 9.3 上进行测试。
我的自定义 table 视图单元格由一个标签和一个开关组成,如下所示。
标签添加到内容视图,开关添加到自定义附件视图。
接口定义如下
@interface MyCustomTableViewCell : UITableViewCell
///Designated initializer
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
///Property that determines if the switch displayed in the cell is ON or OFF.
@property (nonatomic, assign) BOOL switchIsOn;
///The label to be displayed for the alert
@property (nonatomic, strong) UILabel *alertLabel;
@property (nonatomic, strong) UISwitch *switch;
#pragma mark - Accessibility
// Used for setting up accessibility values. This is used to generate accessibility labels of
// individual elements.
@property (nonatomic, strong) NSString* accessibilityPrefix;
-(void)setAlertHTMLText:(NSString*)title;
@end
下面给出了实现块
@interface MyCustomTableViewCell()
@property (nonatomic, strong) UIView *customAccessoryView;
@property (nonatomic, strong) NSString *alertTextString;
@property (nonatomic, strong) NSMutableArray* accessibleElements;
@end
@implementation MyCustomTableViewCell
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if(self = [super initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:reuseIdentifier]) {
[self configureTableCell];
}
return self;
}
- (void)configureTableCell
{
if (!_accessibleElements) {
_accessibleElements = [[NSMutableArray alloc] init];
}
//Alert label
self.alertLabel = [[self class] makeAlertLabel];
[self.contentView setIsAccessibilityElement:YES];
//
[self.contentView addSubview:self.alertLabel];
// Custom AccessoryView for easy styling.
self.customAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
[self.customAccessoryView setIsAccessibilityElement:YES];
[self.contentView addSubview:self.customAccessoryView];
//switch
self.switch = [[BAUISwitch alloc] initWithFrame:CGRectZero];
[self.switch addTarget:self action:@selector(switchWasFlipped:) forControlEvents:UIControlEventValueChanged];
[self.switch setIsAccessibilityElement:YES];
[self.switch setAccessibilityTraits:UIAccessibilityTraitButton];
[self.switch setAccessibilityLabel:@""];
[self.switch setAccessibilityHint:@""];
self.switch.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self.customAccessoryView addSubview:self.switch];
}
+ (UILabel *)makeAlertLabel
{
UILabel *alertLabel = [[UILabel alloc] initWithFrame:CGRectZero];
alertLabel.backgroundColor = [UIColor clearColor];
alertLabel.HTMLText = @"";
alertLabel.numberOfLines = 0;
alertLabel.lineBreakMode = LINE_BREAK_WORD_WRAP
[alertLabel setIsAccessibilityElement:YES];
return alertLabel;
}
-(void)setAlertHTMLText:(NSString*)title{
_alertTextString = [NSString stringWithString:title];
[self.alertLabel setText:_alertTextString];
}
- (BOOL)isAccessibilityElement {
return NO;
}
// The view encapsulates the following elements for the purposes of
// accessibility.
-(NSArray*) accessibleElements {
if (_accessibleElements && [_accessibleElements count] > 0) {
[_accessibleElements removeAllObjects];
}
// Fetch a new copy as the values may have changed.
_accessibleElements = [[NSMutableArray alloc] init];
UIAccessibilityElement* alertLabelElement =
[[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
//alertLabelElement.accessibilityFrame = [self convertRect:self.contentView.frame toView:nil];
alertLabelElement.accessibilityLabel = _alertTextString;
alertLabelElement.accessibilityTraits = UIAccessibilityTraitStaticText;
[_accessibleElements addObject:alertLabelElement];
UIAccessibilityElement* switchElement =
[[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
// switchElement.accessibilityFrame = [self convertRect:self.customAccessoryView.frame toView:nil];
switchElement.accessibilityTraits = UIAccessibilityTraitButton;
// If you want custom values, just override it in the invoking function.
NSMutableString* accessibilityString =
[NSMutableString stringWithString:self.accessibilityPrefix];
[accessibilityString appendString:@" Switch "];
if (self.switchh.isOn) {
[accessibilityString appendString:@"On"];
} else {
[accessibilityString appendString:@"Off"];
}
switchElement.accessibilityLabel = [accessibilityString copy];
[_accessibleElements addObject:switchElement];
}
return _accessibleElements;
}
// In case accessibleElements is not initialized.
- (void) initializeAccessibleElements {
_accessibleElements = [self accessibleElements];
}
- (NSInteger)accessibilityElementCount
{
return [_accessibleElements count]
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
[self initializeAccessibleElements];
return [_accessibleElements objectAtIndex:index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
[self initializeAccessibleElements];
return [_accessibleElements indexOfObject:element];
}
@end
首先,根据您描述的模式,我不确定您为什么要区分单元格中的不同元素。通常,Apple 将每个单元格都保留为一个可访问性元素。设置应用程序中是查看带有标签和开关的单元格的预期 iOS VO 行为的好地方。
如果您仍然认为处理单元格的最佳方式是让它们包含单独的元素,那么这实际上是单元格的默认行为当 UITableViewCell 本身不存在时有无障碍标签。所以,我修改了下面的代码,并在我的 iOS 设备 (运行ning 9.3) 上 运行 修改了它,它按照你描述的方式工作。
您会注意到一些事情。
- 我删除了所有自定义 accessibilityElements 代码。没必要。
- 我删除了 UITableViewCell 子类本身的 isAccessibilityElement 覆盖。我们想要默认行为。
- 我注释掉了将内容视图设置为 accessibilityElement——我们希望它为 NO,以便树构建器在其中查找元素。
- 出于与上述相同的原因,我也将 customAccessoryView 的 isAccessibilityElement 设置为 NO。通常,NO 表示 "keep looking down the tree",YES 表示 "stop here, this is my leaf as far as accessibility is concerned."
希望对您有所帮助。再一次,我确实鼓励您在设计辅助功能时模仿 Apple 的 VO 模式。我认为您确保您的应用程序可供访问,这很棒!
#import "MyCustomTableViewCell.h"
@interface MyCustomTableViewCell()
@property (nonatomic, strong) UIView *customAccessoryView;
@property (nonatomic, strong) NSString *alertTextString;
@property (nonatomic, strong) NSMutableArray* accessibleElements;
@end
@implementation MyCustomTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if(self = [super initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:reuseIdentifier]) {
[self configureTableCell];
}
return self;
}
// just added this here to get the cell to lay out for myself
- (void)layoutSubviews {
[super layoutSubviews];
const CGFloat margin = 8;
CGRect b = self.bounds;
CGSize labelSize = [self.alertLabel sizeThatFits:b.size];
CGFloat maxX = CGRectGetMaxX(b);
self.alertLabel.frame = CGRectMake(margin, margin, labelSize.width, labelSize.height);
CGSize switchSize = [self.mySwitch sizeThatFits:b.size];
self.customAccessoryView.frame = CGRectMake(maxX - switchSize.width - margin * 2, b.origin.y + margin, switchSize.width + margin * 2, switchSize.height);
self.mySwitch.frame = CGRectMake(margin, 0, switchSize.width, switchSize.height);
}
- (void)configureTableCell
{
//Alert label
self.alertLabel = [[self class] makeAlertLabel];
//[self.contentView setIsAccessibilityElement:YES];
//
[self.contentView addSubview:self.alertLabel];
// Custom AccessoryView for easy styling.
self.customAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
[self.customAccessoryView setIsAccessibilityElement:NO]; // Setting this to NO tells the the hierarchy builder to look inside
[self.contentView addSubview:self.customAccessoryView];
self.customAccessoryView.backgroundColor = [UIColor purpleColor];
//switch
self.mySwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
//[self.mySwitch addTarget:self action:@selector(switchWasFlipped:) forControlEvents:UIControlEventValueChanged];
[self.mySwitch setIsAccessibilityElement:YES]; // This is default behavior
[self.mySwitch setAccessibilityTraits:UIAccessibilityTraitButton]; // No tsure why this is here
[self.mySwitch setAccessibilityLabel:@"my swich"];
[self.mySwitch setAccessibilityHint:@"Tap to do something."];
self.mySwitch.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self.customAccessoryView addSubview:self.mySwitch];
}
+ (UILabel *)makeAlertLabel
{
UILabel *alertLabel = [[UILabel alloc] initWithFrame:CGRectZero];
alertLabel.backgroundColor = [UIColor clearColor];
alertLabel.text = @"";
alertLabel.numberOfLines = 0;
[alertLabel setIsAccessibilityElement:YES];
return alertLabel;
}
-(void)setAlertHTMLText:(NSString*)title{
_alertTextString = [NSString stringWithString:title];
[self.alertLabel setText:_alertTextString];
}
@end