ComponentKit/Accessibility/CKComponentAccessibility.mm (64 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import "CKComponentAccessibility.h" #import <RenderCore/RCAssert.h> /** Helper that converts the accessibility context characteristics to a map of component view attributes */ static CKViewComponentAttributeValueMap ViewAttributesFromAccessibilityContext(const RCAccessibilityContext &accessibilityContext) { CKViewComponentAttributeValueMap accessibilityAttributes; if (accessibilityContext.isAccessibilityElement) { accessibilityAttributes[@selector(setIsAccessibilityElement:)] = accessibilityContext.isAccessibilityElement; } if (accessibilityContext.accessibilityLabel.hasText()) { accessibilityAttributes[@selector(setAccessibilityLabel:)] = accessibilityContext.accessibilityLabel.value(); } if (accessibilityContext.accessibilityHint.hasText()) { accessibilityAttributes[@selector(setAccessibilityHint:)] = accessibilityContext.accessibilityHint.value(); } if (accessibilityContext.accessibilityValue.hasText()) { accessibilityAttributes[@selector(setAccessibilityValue:)] = accessibilityContext.accessibilityValue.value(); } if (accessibilityContext.accessibilityTraits) { accessibilityAttributes[@selector(setAccessibilityTraits:)] = accessibilityContext.accessibilityTraits; } return accessibilityAttributes; } CKComponentViewConfiguration CK::Component::Accessibility::AccessibleViewConfiguration(const CKComponentViewConfiguration &viewConfiguration) { RCCAssertMainThread(); // Copy is intentional so we can move later. RCAccessibilityContext accessibilityContext = viewConfiguration.accessibilityContext(); const CKViewComponentAttributeValueMap &accessibilityAttributes = ViewAttributesFromAccessibilityContext(accessibilityContext); if (accessibilityAttributes.size() > 0) { CKViewComponentAttributeValueMap newAttributes(*viewConfiguration.attributes()); newAttributes.insert(accessibilityAttributes.begin(), accessibilityAttributes.end()); // Copy is intentional so we can move later. CKComponentViewClass viewClass = viewConfiguration.viewClass(); // If the specified view class doesn't have a view, force the creation of one // so the accessibility attributes can be realized. return CKComponentViewConfiguration(viewClass.hasView() ? std::move(viewClass) : CKComponentViewClass([UIView class]), std::move(newAttributes), std::move(accessibilityContext)); } else { return viewConfiguration; } } static BOOL _forceAccessibilityEnabled = NO; static BOOL _forceAccessibilityDisabled = NO; void CK::Component::Accessibility::SetForceAccessibilityEnabled(BOOL enabled) { _forceAccessibilityEnabled = enabled; _forceAccessibilityDisabled = !enabled; } void CK::Component::Accessibility::ResetForceAccessibility() { _forceAccessibilityEnabled = NO; _forceAccessibilityDisabled = NO; } BOOL CK::Component::Accessibility::IsAccessibilityEnabled() { RCCAssertMainThread(); return !_forceAccessibilityDisabled && (_forceAccessibilityEnabled || UIAccessibilityIsVoiceOverRunning()); } NSString *const CKAccessibilityExtraActionKey = @"CKAccessibilityExtraActionKey"; id CKAccessibilityExtraActionValue(CKAction<> action) { // Mostly this function exists to ensure the correct type is passed // (CKAction<> not CKAction<Foo>) and that the action is captured // by value, not by reference. return ^{ return action; }; } CKAction<> CKAccessibilityActionFromExtraValue(id extraValue) { CKAction<> (^block)(void) = extraValue; return block ? block() : CKAction<>(); }