Voice Chat UI improvements

This commit is contained in:
Ilya Laktyushin 2020-12-04 20:30:51 +04:00
parent 4c27869b04
commit e0aacc155c
7 changed files with 66 additions and 11 deletions

View File

@ -697,7 +697,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
transition.updateAlpha(layer: self.highlightedBackgroundNode.layer, alpha: highlightProgress)
if let item = self.item {
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: self.onlineIsVoiceChat), color: nil)
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: self.onlineIsVoiceChat), color: nil, transition: transition)
}
} else {
if self.highlightedBackgroundNode.supernode != nil {
@ -717,7 +717,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: self.onlineIsVoiceChat)
}
self.onlineNode.setImage(onlineIcon, color: nil)
self.onlineNode.setImage(onlineIcon, color: nil, transition: transition)
}
}
}
@ -1543,7 +1543,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat)
}
strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor)
strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor, transition: .immediate)
let _ = measureApply()
let _ = dateApply()

View File

@ -1426,11 +1426,16 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
if self.stackFromBottom {
let previousCompleteHeight = completeHeight
let updatedCompleteHeight = max(completeHeight, self.visibleSize.height)
let deltaCompleteHeight = updatedCompleteHeight - completeHeight
topItemEdge -= deltaCompleteHeight
bottomItemEdge -= deltaCompleteHeight
completeHeight = updatedCompleteHeight
if let _ = self.keepMinimalScrollHeightWithTopInset {
completeHeight += effectiveInsets.top + previousCompleteHeight
}
}
}
}

View File

@ -209,7 +209,7 @@ public final class HorizontalPeerItemNode: ListViewItemNode {
strongSelf.badgeBackgroundNode.isHidden = true
}
strongSelf.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.theme, state: .regular), color: nil)
strongSelf.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.theme, state: .regular), color: nil, transition: .immediate)
strongSelf.onlineNode.frame = CGRect(x: itemLayout.size.width - onlineLayout.width - 18.0, y: itemLayout.size.height - onlineLayout.height - 18.0, width: onlineLayout.width, height: onlineLayout.height)
let _ = badgeApply()

View File

@ -123,7 +123,17 @@ public final class PeerOnlineMarkerNode: ASDisplayNode {
self.addSubnode(self.iconNode)
}
public func setImage(_ image: UIImage?, color: UIColor?) {
public func setImage(_ image: UIImage?, color: UIColor?, transition: ContainedViewLayoutTransition) {
if case let .animated(duration, curve) = transition {
if let snapshotLayer = self.iconNode.layer.snapshotContentTree() {
snapshotLayer.frame = self.iconNode.frame
self.iconNode.layer.insertSublayer(snapshotLayer, at: 0)
snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in
snapshotLayer?.removeFromSuperlayer()
})
}
}
self.iconNode.image = image
if let color = color {
self.color = color

View File

@ -166,7 +166,7 @@ public final class SelectablePeerNode: ASDisplayNode {
let (onlineSize, onlineApply) = onlineLayout(online, false)
let _ = onlineApply(false)
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(theme, state: .panel), color: nil)
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(theme, state: .panel), color: nil, transition: .immediate)
self.onlineNode.frame = CGRect(origin: CGPoint(), size: onlineSize)
self.setNeedsLayout()

View File

@ -411,6 +411,12 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.maskView.addSubview(self.maskBlobView)
self.maskView.layer.addSublayer(self.maskCircleLayer)
self.maskBlobView.scaleUpdated = { [weak self] scale in
if let strongSelf = self {
strongSelf.updateGlowScale(strongSelf.isActive ? scale : nil)
}
}
}
private func setupGradientAnimations() {
@ -483,6 +489,18 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
}
}
func updateGlowScale(_ scale: CGFloat?) {
return
if let scale = scale {
self.maskGradientLayer.transform = CATransform3DMakeScale(0.89 + 0.11 * scale, 0.89 + 0.11 * scale, 1.0)
} else {
// let initialScale: CGFloat = ((self.maskGradientLayer.value(forKeyPath: "presentationLayer.transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? (((self.maskGradientLayer.value(forKeyPath: "transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? (effectivePreviousActive ? 0.95 : 0.8))
// let targetScale: CGFloat = self.isActive ? 0.89 : 0.85
// self.maskGradientLayer.transform = CATransform3DMakeScale(targetScale, targetScale, 1.0)
// self.maskGradientLayer.animateScale(from: initialScale, to: targetScale, duration: 0.3)
}
}
func updateGlowAndGradientAnimations(active: Bool?, previousActive: Bool? = nil) {
let effectivePreviousActive = previousActive ?? false
@ -587,6 +605,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
CATransaction.commit()
}
var isActive = false
func updateAnimations() {
if !self.isCurrentlyInHierarchy {
self.foregroundGradientLayer.removeAllAnimations()
@ -601,10 +620,12 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
case .connecting:
self.updatedActive?(false)
self.setupProgressAnimations()
self.isActive = false
case let .blob(newActive):
if let transition = self.transition {
if transition == .connecting {
self.playConnectionAnimation(active: newActive) { [weak self] in
self?.isActive = newActive
self?.transition = nil
}
} else if transition == .disabled {
@ -612,11 +633,14 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
} else if case let .blob(previousActive) = transition {
updateGlowAndGradientAnimations(active: newActive, previousActive: previousActive)
self.transition = nil
self.isActive = newActive
}
} else {
self.maskBlobView.startAnimating()
}
case .disabled:
self.isActive = false
self.updateGlowScale(nil)
break
}
}
@ -684,6 +708,12 @@ private final class VoiceBlobView: UIView {
private var audioLevel: CGFloat = 0.0
var presentationAudioLevel: CGFloat = 0.0
var scaleUpdated: ((CGFloat) -> Void)? {
didSet {
self.bigBlob.scaleUpdated = self.scaleUpdated
}
}
private(set) var isAnimating = false
public typealias BlobRange = (min: CGFloat, max: CGFloat)
@ -805,12 +835,15 @@ final class BlobView: UIView {
let minScale: CGFloat
let maxScale: CGFloat
var scaleUpdated: ((CGFloat) -> Void)?
var level: CGFloat = 0 {
didSet {
CATransaction.begin()
CATransaction.setDisableActions(true)
let lv = minScale + (maxScale - minScale) * level
shapeLayer.transform = CATransform3DMakeScale(lv, lv, 1)
self.scaleUpdated?(level)
CATransaction.commit()
}
}

View File

@ -456,6 +456,7 @@ public final class VoiceChatController: ViewController {
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
self.listNode.clipsToBounds = true
self.listNode.stackFromBottom = true
self.listNode.keepMinimalScrollHeightWithTopInset = 0
self.topPanelNode = ASDisplayNode()
self.topPanelNode.backgroundColor = panelBackgroundColor
@ -1360,7 +1361,7 @@ public final class VoiceChatController: ViewController {
actionButtonState = .connecting
actionButtonTitle = self.presentationData.strings.VoiceChat_Connecting
actionButtonSubtitle = ""
audioButtonAppearance = .color(.custom(0x1c1c1e))
audioButtonAppearance = .color(.custom(0x2c2c2e))
actionButtonEnabled = false
case .connected:
if let muteState = callState.muteState, !self.pushingToTalk {
@ -1506,6 +1507,7 @@ public final class VoiceChatController: ViewController {
}
}
private var isFirstTime = true
private func dequeueTransition() {
guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else {
return
@ -1522,7 +1524,14 @@ public final class VoiceChatController: ViewController {
options.insert(.LowLatency)
options.insert(.PreferSynchronousResourceLoading)
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
var scrollToItem: ListViewScrollToItem?
if self.isFirstTime {
self.isFirstTime = false
scrollToItem = ListViewScrollToItem(index: 0, position: .bottom(0), animated: false, curve: .Default(duration: nil), directionHint: .Up)
}
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
@ -1638,7 +1647,6 @@ public final class VoiceChatController: ViewController {
if let gestureRecognizers = view.gestureRecognizers, view != self.view {
for gestureRecognizer in gestureRecognizers {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
print(view)
if panGestureRecognizer.state != .began {
panGestureRecognizer.isEnabled = false
panGestureRecognizer.isEnabled = true
@ -1691,7 +1699,6 @@ public final class VoiceChatController: ViewController {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
print("actually hitting")
if result === self.topPanelNode.view || result === self.bottomPanelNode.view {
return self.view
}