Voice Chat UI fixes

This commit is contained in:
Ilya Laktyushin 2020-12-06 10:27:59 +04:00
parent 7e769a725c
commit d2229c6192
19 changed files with 4316 additions and 4220 deletions

View File

@ -5994,3 +5994,7 @@ Sorry for the inconvenience.";
"Call.VoiceChatInProgressMessageCall" = "Leave voice chat in %1$@ and start a call with %2$@?";
"Conversation.Dice.u1F3B3" = "Send a bowling emoji to try your luck.";
"VoiceOver.Common.TapToChange" = "Tap To Change";
"VoiceOver.Common.On" = "On";
"VoiceOver.Common.Off" = "Off";

View File

@ -2,6 +2,10 @@ import Foundation
import UIKit
import AsyncDisplayKit
public protocol AccessibilityFocusableNode {
func accessibilityElementDidBecomeFocused()
}
public final class AccessibilityAreaNode: ASDisplayNode {
public var activate: (() -> Bool)?
public var focused: (() -> Void)?
@ -27,7 +31,7 @@ public final class AccessibilityAreaNode: ASDisplayNode {
var supernode = self.supernode
while true {
if let supernodeValue = supernode {
if let listItemNode = supernodeValue as? ListViewItemNode {
if let listItemNode = supernodeValue as? AccessibilityFocusableNode {
listItemNode.accessibilityElementDidBecomeFocused()
break
} else {

View File

@ -83,7 +83,7 @@ public struct ListViewItemLayoutParams {
}
}
open class ListViewItemNode: ASDisplayNode {
open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
let rotated: Bool
final var index: Int?

View File

@ -39,6 +39,8 @@ public final class ToolbarNode: ASDisplayNode {
super.init()
self.isAccessibilityContainer = false
self.addSubnode(self.leftTitle)
self.addSubnode(self.leftButton)
self.addSubnode(self.rightTitle)
@ -63,6 +65,7 @@ public final class ToolbarNode: ASDisplayNode {
}
}
}
self.leftButton.accessibilityTraits = .button
self.rightButton.addTarget(self, action: #selector(self.rightPressed), forControlEvents: .touchUpInside)
self.rightButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
@ -75,6 +78,7 @@ public final class ToolbarNode: ASDisplayNode {
}
}
}
self.rightButton.accessibilityTraits = .button
self.middleButton.addTarget(self, action: #selector(self.middlePressed), forControlEvents: .touchUpInside)
self.middleButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
@ -87,6 +91,7 @@ public final class ToolbarNode: ASDisplayNode {
}
}
}
self.middleButton.accessibilityTraits = .button
}
public func updateTheme(_ theme: TabBarControllerTheme) {
@ -100,8 +105,14 @@ public final class ToolbarNode: ASDisplayNode {
let sideInset: CGFloat = 16.0
self.leftTitle.attributedText = NSAttributedString(string: toolbar.leftAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.leftAction?.isEnabled ?? false) ? self.theme.tabBarSelectedTextColor : self.theme.tabBarTextColor)
self.leftButton.accessibilityLabel = toolbar.leftAction?.title
self.rightTitle.attributedText = NSAttributedString(string: toolbar.rightAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.rightAction?.isEnabled ?? false) ? self.theme.tabBarSelectedTextColor : self.theme.tabBarTextColor)
self.rightButton.accessibilityLabel = toolbar.rightAction?.title
self.middleTitle.attributedText = NSAttributedString(string: toolbar.middleAction?.title ?? "", font: Font.regular(17.0), textColor: (toolbar.middleAction?.isEnabled ?? false) ? self.theme.tabBarSelectedTextColor : self.theme.tabBarTextColor)
self.middleButton.accessibilityLabel = toolbar.middleAction?.title
let leftSize = self.leftTitle.updateLayout(size)
let rightSize = self.rightTitle.updateLayout(size)
let middleSize = self.middleTitle.updateLayout(size)

View File

@ -251,8 +251,8 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
strongSelf.activateArea.accessibilityLabel = item.title
strongSelf.activateArea.accessibilityValue = item.value ? "On" : "Off"
strongSelf.activateArea.accessibilityHint = "Tap to change"
strongSelf.activateArea.accessibilityValue = item.value ? item.presentationData.strings.VoiceOver_Common_On : item.presentationData.strings.VoiceOver_Common_Off
strongSelf.activateArea.accessibilityHint = item.presentationData.strings.VoiceOver_Common_TapToChange
var accessibilityTraits = UIAccessibilityTraits()
if item.enabled {
} else {

View File

@ -150,7 +150,7 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
strongSelf.activateArea.accessibilityLabel = attributedText.string
strongSelf.accessibilityLabel = attributedText.string
strongSelf.activateArea.accessibilityLabel = attributedText.string
let _ = titleApply()

View File

@ -161,20 +161,24 @@ public final class PeerOnlineMarkerNode: ASDisplayNode {
if animated {
let initialScale: CGFloat = strongSelf.iconNode.isHidden ? 0.0 : CGFloat((strongSelf.iconNode.value(forKeyPath: "layer.presentationLayer.transform.scale.x") as? NSNumber)?.floatValue ?? 1.0)
let targetScale: CGFloat = online ? 1.0 : 0.0
strongSelf.iconNode.isHidden = false
strongSelf.iconNode.layer.animateScale(from: initialScale, to: targetScale, duration: 0.2, removeOnCompletion: false, completion: { [weak self] finished in
if let strongSelf = self, finished {
strongSelf.iconNode.isHidden = !online
if let animationNode = strongSelf.animationNode, !online {
animationNode.removeFromSupernode()
if initialScale != targetScale {
strongSelf.iconNode.isHidden = false
strongSelf.iconNode.layer.animateScale(from: initialScale, to: targetScale, duration: 0.2, removeOnCompletion: false, completion: { [weak self] finished in
if let strongSelf = self, finished {
strongSelf.iconNode.isHidden = !online
if let animationNode = strongSelf.animationNode, !online {
strongSelf.animationNode = nil
animationNode.removeFromSupernode()
}
}
}
})
})
}
} else {
strongSelf.iconNode.isHidden = !online
if let animationNode = strongSelf.animationNode, !online {
strongSelf.animationNode = nil
animationNode.removeFromSupernode()
}
}

View File

@ -38,6 +38,7 @@ swift_library(
"//submodules/AlertUI:AlertUI",
"//submodules/DirectionalPanGesture:DirectionalPanGesture",
"//submodules/PeerInfoUI:PeerInfoUI",
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
],
visibility = [
"//visibility:public",

View File

@ -9,6 +9,7 @@ import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import LegacyComponents
import AnimatedCountLabelNode
private let blue = UIColor(rgb: 0x0078ff)
private let lightBlue = UIColor(rgb: 0x59c7f8)

View File

@ -578,12 +578,15 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
}
private func playBlobsDisappearanceAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.foregroundCircleLayer.isHidden = false
CATransaction.commit()
self.updateGlowAndGradientAnimations(active: nil, previousActive: nil)
self.maskBlobView.startAnimating()
self.maskBlobView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak self] _ in
self.maskBlobView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak self] _ in
self?.maskBlobView.isHidden = true
self?.maskBlobView.stopAnimating()
self?.maskBlobView.layer.removeAllAnimations()
@ -595,6 +598,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
growthAnimation.toValue = 1.0
growthAnimation.duration = 0.15
growthAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
growthAnimation.isRemovedOnCompletion = false
CATransaction.setCompletionBlock {
CATransaction.begin()
@ -602,6 +606,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.maskGradientLayer.isHidden = true
self.maskCircleLayer.isHidden = true
self.foregroundCircleLayer.isHidden = true
self.foregroundCircleLayer.removeAllAnimations()
CATransaction.commit()
}

View File

@ -39,6 +39,7 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
private let iconNode: ASImageNode
private let textNode: ImmediateTextNode
private let bottomSeparatorNode: ASDisplayNode
private let activateArea: AccessibilityAreaNode
private var item: PeerInfoScreenActionItem?
@ -57,6 +58,8 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
self.bottomSeparatorNode = ASDisplayNode()
self.bottomSeparatorNode.isLayerBacked = true
self.activateArea = AccessibilityAreaNode()
super.init()
bringToFrontForHighlightImpl = { [weak self] in
@ -66,6 +69,8 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
self.addSubnode(self.bottomSeparatorNode)
self.addSubnode(self.selectionNode)
self.addSubnode(self.textNode)
self.addSubnode(self.activateArea)
}
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
@ -95,6 +100,7 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
self.textNode.maximumNumberOfLines = 1
self.textNode.attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: textColorValue)
self.activateArea.accessibilityLabel = item.text
let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
@ -123,6 +129,8 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: height - UIScreenPixel), size: CGSize(width: width - separatorInset, height: UIScreenPixel)))
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
self.activateArea.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
return height
}
}

View File

@ -18,6 +18,7 @@ final class PeerInfoScreenCommentItem: PeerInfoScreenItem {
private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode {
private let textNode: ImmediateTextNode
private let activateArea: AccessibilityAreaNode
private var item: PeerInfoScreenCommentItem?
@ -26,9 +27,13 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode {
self.textNode.displaysAsynchronously = false
self.textNode.isUserInteractionEnabled = false
self.activateArea = AccessibilityAreaNode()
self.activateArea.accessibilityTraits = .staticText
super.init()
self.addSubnode(self.textNode)
self.addSubnode(self.activateArea)
}
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
@ -43,6 +48,7 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode {
self.textNode.maximumNumberOfLines = 0
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: presentationData.theme.list.freeTextColor)
self.activateArea.accessibilityLabel = item.text
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
@ -52,6 +58,8 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode {
transition.updateFrame(node: self.textNode, frame: textFrame)
self.activateArea.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
return height
}
}

View File

@ -54,6 +54,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
private let textNode: ImmediateTextNode
private let arrowNode: ASImageNode
private let bottomSeparatorNode: ASDisplayNode
private let activateArea: AccessibilityAreaNode
private var item: PeerInfoScreenDisclosureItem?
@ -87,6 +88,8 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
self.bottomSeparatorNode = ASDisplayNode()
self.bottomSeparatorNode.isLayerBacked = true
self.activateArea = AccessibilityAreaNode()
super.init()
bringToFrontForHighlightImpl = { [weak self] in
@ -98,6 +101,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
self.addSubnode(self.labelNode)
self.addSubnode(self.textNode)
self.addSubnode(self.arrowNode)
self.addSubnode(self.activateArea)
}
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
@ -180,6 +184,9 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
let labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth, y: labelFrame.minY - 1.0), size: CGSize(width: badgeWidth, height: badgeDiameter))
self.activateArea.accessibilityLabel = item.text
self.activateArea.accessibilityValue = item.label.text
transition.updateFrame(node: self.labelBadgeNode, frame: labelBadgeNodeFrame)
transition.updateFrame(node: self.labelNode, frame: labelFrame)
transition.updateFrame(node: self.textNode, frame: textFrame)
@ -191,6 +198,8 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: height - UIScreenPixel), size: CGSize(width: width - separatorInset, height: UIScreenPixel)))
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
self.activateArea.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
return height
}
}

View File

@ -18,6 +18,7 @@ final class PeerInfoScreenHeaderItem: PeerInfoScreenItem {
private final class PeerInfoScreenHeaderItemNode: PeerInfoScreenItemNode {
private let textNode: ImmediateTextNode
private let activateArea: AccessibilityAreaNode
private var item: PeerInfoScreenHeaderItem?
@ -26,9 +27,13 @@ private final class PeerInfoScreenHeaderItemNode: PeerInfoScreenItemNode {
self.textNode.displaysAsynchronously = false
self.textNode.isUserInteractionEnabled = false
self.activateArea = AccessibilityAreaNode()
self.activateArea.accessibilityTraits = [.staticText, .header]
super.init()
self.addSubnode(self.textNode)
self.addSubnode(self.activateArea)
}
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
@ -43,6 +48,7 @@ private final class PeerInfoScreenHeaderItemNode: PeerInfoScreenItemNode {
self.textNode.maximumNumberOfLines = 0
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(13.0), textColor: presentationData.theme.list.freeTextColor)
self.activateArea.accessibilityLabel = item.text
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
@ -52,6 +58,8 @@ private final class PeerInfoScreenHeaderItemNode: PeerInfoScreenItemNode {
transition.updateFrame(node: self.textNode, frame: textFrame)
self.activateArea.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
return height
}
}

View File

@ -25,6 +25,7 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
private let textNode: ImmediateTextNode
private let switchNode: SwitchNode
private let bottomSeparatorNode: ASDisplayNode
private let activateArea: AccessibilityAreaNode
private var item: PeerInfoScreenSwitchItem?
@ -43,6 +44,8 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
self.bottomSeparatorNode = ASDisplayNode()
self.bottomSeparatorNode.isLayerBacked = true
self.activateArea = AccessibilityAreaNode()
super.init()
bringToFrontForHighlightImpl = { [weak self] in
@ -53,10 +56,20 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
self.addSubnode(self.selectionNode)
self.addSubnode(self.textNode)
self.addSubnode(self.switchNode)
self.addSubnode(self.activateArea)
self.switchNode.valueUpdated = { [weak self] value in
self?.item?.toggled?(value)
}
self.activateArea.activate = { [weak self] in
guard let strongSelf = self, let item = strongSelf.item else {
return false
}
let value = !strongSelf.switchNode.isOn
item.toggled(value)
return true
}
}
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
@ -87,6 +100,10 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
self.textNode.maximumNumberOfLines = 1
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
self.activateArea.accessibilityLabel = item.text
self.activateArea.accessibilityValue = item.value ? presentationData.strings.VoiceOver_Common_On : presentationData.strings.VoiceOver_Common_Off
self.activateArea.accessibilityHint = presentationData.strings.VoiceOver_Common_TapToChange
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0 - 56.0, height: .greatestFiniteMagnitude))
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 12.0), size: textSize)
@ -113,6 +130,8 @@ private final class PeerInfoScreenSwitchItemNode: PeerInfoScreenItemNode {
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
self.activateArea.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
return height
}
}

View File

@ -69,6 +69,8 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
super.init()
self.accessibilityTraits = .button
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.backgroundNode)
self.containerNode.addSubnode(self.textNode)
@ -132,6 +134,7 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
}
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(12.0), textColor: presentationData.theme.list.itemAccentColor)
self.accessibilityLabel = text
let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude))
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size))
@ -1838,6 +1841,9 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
super.init(pointerStyle: .default)
self.isAccessibilityElement = true
self.accessibilityTraits = .button
self.addSubnode(self.regularTextNode)
self.addSubnode(self.whiteTextNode)
self.addSubnode(self.iconNode)
@ -1874,6 +1880,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
case .editVideo:
text = presentationData.strings.Settings_EditVideo
}
self.accessibilityLabel = text
let font: UIFont = isBold ? Font.semibold(17.0) : Font.regular(17.0)

View File

@ -59,12 +59,16 @@ protocol PeerInfoScreenItem: class {
func node() -> PeerInfoScreenItemNode
}
class PeerInfoScreenItemNode: ASDisplayNode {
class PeerInfoScreenItemNode: ASDisplayNode, AccessibilityFocusableNode {
var bringToFrontForHighlight: (() -> Void)?
func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
preconditionFailure()
}
override open func accessibilityElementDidBecomeFocused() {
// (self.supernode as? ListView)?.ensureItemNodeVisible(self, animated: false, overflow: 22.0)
}
}
private final class PeerInfoScreenItemSectionContainerNode: ASDisplayNode {