From d8cd427f55802cd06d9caff75a6fc222a4c65331 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 19 Mar 2019 01:22:50 +0400 Subject: [PATCH] Accessibility updates --- Display/ContextMenuActionNode.swift | 28 +++++++++++++--- Display/NativeWindowHostView.swift | 52 +++++++++++++++++++++++++++-- Display/SwitchNode.swift | 2 ++ Display/WindowContent.swift | 11 +++--- 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/Display/ContextMenuActionNode.swift b/Display/ContextMenuActionNode.swift index b5616cfb22..d614f7ed8a 100644 --- a/Display/ContextMenuActionNode.swift +++ b/Display/ContextMenuActionNode.swift @@ -12,20 +12,28 @@ final private class ContextMenuActionButton: HighlightTrackingButton { } final class ContextMenuActionNode: ASDisplayNode { - private let textNode: ASTextNode? + private let textNode: ImmediateTextNode? + private var textSize: CGSize? private let iconNode: ASImageNode? private let action: () -> Void private let button: ContextMenuActionButton + private let actionArea: AccessibilityAreaNode var dismiss: (() -> Void)? init(action: ContextMenuAction) { + self.actionArea = AccessibilityAreaNode() + self.actionArea.accessibilityTraits = UIAccessibilityTraitButton + switch action.content { case let .text(title): - let textNode = ASTextNode() + self.actionArea.accessibilityLabel = title + + let textNode = ImmediateTextNode() textNode.isUserInteractionEnabled = false textNode.displaysAsynchronously = false textNode.attributedText = NSAttributedString(string: title, font: Font.regular(14.0), textColor: UIColor.white) + textNode.isAccessibilityElement = false self.textNode = textNode self.iconNode = nil @@ -41,6 +49,7 @@ final class ContextMenuActionNode: ASDisplayNode { self.action = action.action self.button = ContextMenuActionButton() + self.button.isAccessibilityElement = false super.init() @@ -56,6 +65,12 @@ final class ContextMenuActionNode: ASDisplayNode { self?.backgroundColor = highlighted ? UIColor(white: 0.0, alpha: 0.4) : UIColor(white: 0.0, alpha: 0.8) } self.view.addSubview(self.button) + self.addSubnode(self.actionArea) + + self.actionArea.activate = { [weak self] in + self?.buttonPressed() + return true + } } override func didLoad() { @@ -75,7 +90,8 @@ final class ContextMenuActionNode: ASDisplayNode { override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { if let textNode = self.textNode { - let textSize = textNode.measure(constrainedSize) + let textSize = textNode.updateLayout(constrainedSize) + self.textSize = textSize return CGSize(width: textSize.width + 36.0, height: 54.0) } else if let iconNode = self.iconNode, let image = iconNode.image { return CGSize(width: image.size.width + 36.0, height: 54.0) @@ -88,8 +104,10 @@ final class ContextMenuActionNode: ASDisplayNode { super.layout() self.button.frame = self.bounds - if let textNode = self.textNode { - textNode.frame = CGRect(origin: CGPoint(x: floor((self.bounds.size.width - textNode.calculatedSize.width) / 2.0), y: floor((self.bounds.size.height - textNode.calculatedSize.height) / 2.0)), size: textNode.calculatedSize) + self.actionArea.frame = self.bounds + + if let textNode = self.textNode, let textSize = self.textSize { + textNode.frame = CGRect(origin: CGPoint(x: floor((self.bounds.size.width - textSize.width) / 2.0), y: floor((self.bounds.size.height - textSize.height) / 2.0)), size: textSize) } if let iconNode = self.iconNode, let image = iconNode.image { let iconSize = image.size diff --git a/Display/NativeWindowHostView.swift b/Display/NativeWindowHostView.swift index 359f637568..e1e3c12c75 100644 --- a/Display/NativeWindowHostView.swift +++ b/Display/NativeWindowHostView.swift @@ -52,6 +52,9 @@ private final class WindowRootViewControllerView: UIView { } private final class WindowRootViewController: UIViewController, UIViewControllerPreviewingDelegate { + private var voiceOverStatusObserver: AnyObject? + private var registeredForPreviewing = false + var presentController: ((UIViewController, PresentationSurfaceLevel, Bool, (() -> Void)?) -> Void)? var transitionToSize: ((CGSize, Double) -> Void)? @@ -106,12 +109,26 @@ private final class WindowRootViewController: UIViewController, UIViewController super.init(nibName: nil, bundle: nil) self.extendedLayoutIncludesOpaqueBars = true + + if #available(iOSApplicationExtension 11.0, *) { + self.voiceOverStatusObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIAccessibilityVoiceOverStatusDidChange, object: nil, queue: OperationQueue.main, using: { [weak self] _ in + if let strongSelf = self { + strongSelf.updatePreviewingRegistration() + } + }) + } } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + deinit { + if let voiceOverStatusObserver = self.voiceOverStatusObserver { + NotificationCenter.default.removeObserver(voiceOverStatusObserver) + } + } + override func preferredScreenEdgesDeferringSystemGestures() -> UIRectEdge { return self.gestureEdges } @@ -132,14 +149,45 @@ private final class WindowRootViewController: UIViewController, UIViewController self.view.isOpaque = false self.view.backgroundColor = nil - if #available(iOSApplicationExtension 9.0, *) { - self.registerForPreviewing(with: self, sourceView: self.view) + self.updatePreviewingRegistration() + } + + private var previewingContext: AnyObject? + + private func updatePreviewingRegistration() { + var shouldRegister = false + + var isVoiceOverRunning = false + if #available(iOSApplicationExtension 10.0, *) { + isVoiceOverRunning = UIAccessibility.isVoiceOverRunning + } + if !isVoiceOverRunning { + shouldRegister = true + } + + if shouldRegister != self.registeredForPreviewing { + self.registeredForPreviewing = shouldRegister + if shouldRegister { + if #available(iOSApplicationExtension 9.0, *) { + self.previewingContext = self.registerForPreviewing(with: self, sourceView: self.view) + } + } else if let previewingContext = self.previewingContext { + self.previewingContext = nil + if let previewingContext = previewingContext as? UIViewControllerPreviewing { + if #available(iOSApplicationExtension 9.0, *) { + self.unregisterForPreviewing(withContext: previewingContext) + } + } + } } } private weak var previousPreviewingHostView: (UIView & PreviewingHostView)? public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { + if UIAccessibility.isVoiceOverRunning { + return nil + } if #available(iOSApplicationExtension 9.0, *) { guard let result = self.view.hitTest(location, with: nil) else { return nil diff --git a/Display/SwitchNode.swift b/Display/SwitchNode.swift index cdfc498be6..5d5052de69 100644 --- a/Display/SwitchNode.swift +++ b/Display/SwitchNode.swift @@ -62,6 +62,8 @@ open class SwitchNode: ASDisplayNode { override open func didLoad() { super.didLoad() + self.view.isAccessibilityElement = false + (self.view as! UISwitch).backgroundColor = self.backgroundColor (self.view as! UISwitch).tintColor = self.frameColor //(self.view as! UISwitch).thumbTintColor = self.handleColor diff --git a/Display/WindowContent.swift b/Display/WindowContent.swift index bcf1bdc838..38e6d0970b 100644 --- a/Display/WindowContent.swift +++ b/Display/WindowContent.swift @@ -333,9 +333,9 @@ public class Window1 { private var isInteractionBlocked = false - private var accessibilityElements: [Any]? { + /*private var accessibilityElements: [Any]? { return self.viewController?.view.accessibilityElements - } + }*/ public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) { self.hostView = hostView @@ -425,9 +425,9 @@ public class Window1 { }) } - self.hostView.getAccessibilityElements = { [weak self] in + /*self.hostView.getAccessibilityElements = { [weak self] in return self?.accessibilityElements - } + }*/ self.presentationContext.view = self.hostView.containerView self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation), transition: .immediate) @@ -572,6 +572,9 @@ public class Window1 { if let keyboardTypeChangeObserver = self.keyboardTypeChangeObserver { NotificationCenter.default.removeObserver(keyboardTypeChangeObserver) } + if let voiceOverStatusObserver = self.voiceOverStatusObserver { + NotificationCenter.default.removeObserver(voiceOverStatusObserver) + } } public func setupVolumeControlStatusBarGraphics(_ graphics: (UIImage, UIImage, UIImage)) {