mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Voice Chat UI improvements
This commit is contained in:
parent
a9c16ef245
commit
fe28e3471a
@ -217,11 +217,11 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
|||||||
transition.updateAlpha(node: self.subtitleLabel, alpha: 0.0)
|
transition.updateAlpha(node: self.subtitleLabel, alpha: 0.0)
|
||||||
transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 0.0)
|
transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 0.0)
|
||||||
} else {
|
} else {
|
||||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.35, curve: .easeInOut) : .immediate
|
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
||||||
transition.updateTransformScale(node: self.backgroundNode, scale: small ? 0.85 : 1.0, delay: 0.12)
|
transition.updateTransformScale(node: self.backgroundNode, scale: small ? 0.85 : 1.0, delay: 0.05)
|
||||||
transition.updateTransformScale(node: self.iconNode, scale: self.pressing ? 0.9 : 1.0, delay: 0.12)
|
transition.updateTransformScale(node: self.iconNode, scale: self.pressing ? 0.9 : 1.0, delay: 0.05)
|
||||||
transition.updateAlpha(node: self.titleLabel, alpha: 1.0, delay: 0.1)
|
transition.updateAlpha(node: self.titleLabel, alpha: 1.0, delay: 0.05)
|
||||||
transition.updateAlpha(node: self.subtitleLabel, alpha: 1.0, delay: 0.1)
|
transition.updateAlpha(node: self.subtitleLabel, alpha: 1.0, delay: 0.05)
|
||||||
transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 1.0)
|
transition.updateAlpha(layer: self.backgroundNode.maskProgressLayer, alpha: 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1826,6 +1826,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let topPanelFrame = self.topPanelNode.view.convert(self.topPanelNode.bounds, to: self.view)
|
let topPanelFrame = self.topPanelNode.view.convert(self.topPanelNode.bounds, to: self.view)
|
||||||
|
let offset: CGFloat = self.contentContainer.bounds.minY
|
||||||
|
|
||||||
self.contentContainer.layer.animateBoundsOriginYAdditive(from: self.contentContainer.bounds.origin.y, to: -(layout.size.height - topPanelFrame.minY) - 44.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
self.contentContainer.layer.animateBoundsOriginYAdditive(from: self.contentContainer.bounds.origin.y, to: -(layout.size.height - topPanelFrame.minY) - 44.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
||||||
offsetCompleted = true
|
offsetCompleted = true
|
||||||
@ -2161,7 +2162,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.panGestureArguments = nil
|
self.panGestureArguments = nil
|
||||||
var dismissing = false
|
var dismissing = false
|
||||||
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
|
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) {
|
||||||
self.controller?.dismiss(closing: false)
|
self.controller?.dismiss(closing: false, manual: true)
|
||||||
dismissing = true
|
dismissing = true
|
||||||
} else if velocity.y < -300.0 || offset < topInset / 2.0 {
|
} else if velocity.y < -300.0 || offset < topInset / 2.0 {
|
||||||
self.isExpanded = true
|
self.isExpanded = true
|
||||||
@ -2342,8 +2343,11 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func dismiss(closing: Bool) {
|
private var dismissedManually: Bool = false
|
||||||
if !closing {
|
public func dismiss(closing: Bool, manual: Bool = false) {
|
||||||
|
if closing {
|
||||||
|
self.isDisconnected = true
|
||||||
|
} else {
|
||||||
if let navigationController = self.navigationController as? NavigationController {
|
if let navigationController = self.navigationController as? NavigationController {
|
||||||
let count = navigationController.viewControllers.count
|
let count = navigationController.viewControllers.count
|
||||||
if count == 2 || navigationController.viewControllers[count - 2] is ChatController {
|
if count == 2 || navigationController.viewControllers[count - 2] is ChatController {
|
||||||
@ -2351,12 +2355,17 @@ public final class VoiceChatController: ViewController {
|
|||||||
} else if let chatController = navigationController.viewControllers[count - 2] as? ChatController, chatController.isSendButtonVisible {
|
} else if let chatController = navigationController.viewControllers[count - 2] as? ChatController, chatController.isSendButtonVisible {
|
||||||
} else if let tabBarController = navigationController.viewControllers[count - 2] as? TabBarController, let chatListController = tabBarController.controllers[tabBarController.selectedIndex] as? ChatListController, chatListController.isSearchActive {
|
} else if let tabBarController = navigationController.viewControllers[count - 2] as? TabBarController, let chatListController = tabBarController.controllers[tabBarController.selectedIndex] as? ChatListController, chatListController.isSearchActive {
|
||||||
} else {
|
} else {
|
||||||
self.detachActionButton()
|
if manual {
|
||||||
|
self.dismissedManually = true
|
||||||
|
Queue.mainQueue().after(0.05) {
|
||||||
|
self.detachActionButton()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.detachActionButton()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.isDisconnected = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
@ -2367,12 +2376,13 @@ public final class VoiceChatController: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let overlayController = VoiceChatOverlayController(actionButton: self.controllerNode.actionButton, audioOutputNode: self.controllerNode.audioOutputNode, leaveNode: self.controllerNode.leaveNode, navigationController: self.navigationController as? NavigationController)
|
let overlayController = VoiceChatOverlayController(actionButton: self.controllerNode.actionButton, audioOutputNode: self.controllerNode.audioOutputNode, leaveNode: self.controllerNode.leaveNode, navigationController: self.navigationController as? NavigationController, initiallyHidden: self.dismissedManually)
|
||||||
if let navigationController = self.navigationController as? NavigationController {
|
if let navigationController = self.navigationController as? NavigationController {
|
||||||
navigationController.presentOverlay(controller: overlayController, inGlobal: true, blockInteraction: false)
|
navigationController.presentOverlay(controller: overlayController, inGlobal: true, blockInteraction: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentOverlayController = overlayController
|
self.currentOverlayController = overlayController
|
||||||
|
self.dismissedManually = false
|
||||||
|
|
||||||
self.reclaimActionButton = { [weak self, weak overlayController] in
|
self.reclaimActionButton = { [weak self, weak overlayController] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
@ -204,51 +204,39 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
completion(true)
|
completion(true)
|
||||||
} else {
|
} else {
|
||||||
self.animating = true
|
self.animating = true
|
||||||
|
|
||||||
let sourcePoint = actionButton.position
|
let sourcePoint = actionButton.position
|
||||||
var midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0 - 30.0, y: (sourcePoint.y + targetPosition.y) / 2.0 + 30.0)
|
let transitionNode = ASDisplayNode()
|
||||||
if sourcePoint.y < layout.size.height - 100.0 {
|
transitionNode.position = sourcePoint
|
||||||
midPoint.x = (sourcePoint.x + targetPosition.x) / 2.0 + 30.0
|
transitionNode.addSubnode(actionButton)
|
||||||
midPoint.y = (sourcePoint.y + targetPosition.y) / 2.0 + 40.0
|
actionButton.position = CGPoint()
|
||||||
}
|
self.addSubnode(transitionNode)
|
||||||
|
|
||||||
let x1 = sourcePoint.x
|
|
||||||
let y1 = sourcePoint.y
|
|
||||||
let x2 = midPoint.x
|
|
||||||
let y2 = midPoint.y
|
|
||||||
let x3 = targetPosition.x
|
|
||||||
let y3 = targetPosition.y
|
|
||||||
|
|
||||||
let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
|
|
||||||
let b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
|
|
||||||
let c = (x2 * x2 * (x3 * y1 - x1 * y3) + x2 * (x1 * x1 * y3 - x3 * x3 * y1) + x1 * x3 * (x3 - x1) * y2) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
|
|
||||||
|
|
||||||
var keyframes: [AnyObject] = []
|
|
||||||
for i in 0 ..< 10 {
|
|
||||||
let k = CGFloat(i) / CGFloat(10 - 1)
|
|
||||||
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
|
|
||||||
let y = a * x * x + b * x + c
|
|
||||||
keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let leftButtonPosition = self.initialLeftButtonPosition, let rightButtonPosition = self.initialRightButtonPosition {
|
if let leftButtonPosition = self.initialLeftButtonPosition, let rightButtonPosition = self.initialRightButtonPosition {
|
||||||
let center = CGPoint(x: actionButton.frame.width / 2.0, y: actionButton.frame.height / 2.0)
|
let center = CGPoint(x: actionButton.frame.width / 2.0, y: actionButton.frame.height / 2.0)
|
||||||
|
|
||||||
leftButton.isHidden = false
|
leftButton.isHidden = false
|
||||||
leftButton.layer.animatePosition(from: center, to: leftButtonPosition, duration: 0.28, delay: 0.05, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
|
||||||
|
|
||||||
rightButton.isHidden = false
|
rightButton.isHidden = false
|
||||||
rightButton.layer.animatePosition(from: center, to: rightButtonPosition, duration: 0.28, delay: 0.05, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
|
||||||
|
|
||||||
leftButton.layer.animateScale(from: 0.5, to: 1.0, duration: 0.28, delay: 0.05, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
leftButton.layer.animatePosition(from: center, to: leftButtonPosition, duration: 0.26, delay: 0.07, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
||||||
rightButton.layer.animateScale(from: 0.5, to: 1.0, duration: 0.28, delay: 0.05, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
rightButton.layer.animatePosition(from: center, to: rightButtonPosition, duration: 0.26, delay: 0.07, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
||||||
|
|
||||||
|
leftButton.layer.animateScale(from: 0.55, to: 1.0, duration: 0.26, delay: 0.06, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||||
|
rightButton.layer.animateScale(from: 0.55, to: 1.0, duration: 0.26, delay: 0.06, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||||
|
|
||||||
leftButton.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, delay: 0.05)
|
leftButton.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, delay: 0.05)
|
||||||
rightButton.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, delay: 0.05)
|
rightButton.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, delay: 0.05)
|
||||||
}
|
}
|
||||||
|
|
||||||
actionButton.update(snap: false, animated: true)
|
actionButton.update(snap: false, animated: true)
|
||||||
actionButton.position = targetPosition
|
actionButton.position = CGPoint(x: targetPosition.x - sourcePoint.x, y: 80.0)
|
||||||
actionButton.layer.animateKeyframes(values: keyframes, duration: 0.5, keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.5, 1.1 + Float(1.0 / 3.0), 1, 1), completion: { _ in
|
|
||||||
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
||||||
|
transition.animateView {
|
||||||
|
transitionNode.position = CGPoint(x: transitionNode.position.x, y: targetPosition.y - 80.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
actionButton.layer.animatePosition(from: CGPoint(), to: actionButton.position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, completion: { _ in
|
||||||
self.animating = false
|
self.animating = false
|
||||||
completion(false)
|
completion(false)
|
||||||
})
|
})
|
||||||
@ -296,7 +284,17 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
self.didAnimateIn = true
|
self.didAnimateIn = true
|
||||||
actionButton.ignoreHierarchyChanges = true
|
actionButton.ignoreHierarchyChanges = true
|
||||||
self.addSubnode(actionButton)
|
self.addSubnode(actionButton)
|
||||||
|
var hidden = false
|
||||||
|
if let initiallyHidden = self.controller?.initiallyHidden, initiallyHidden {
|
||||||
|
hidden = initiallyHidden
|
||||||
|
}
|
||||||
|
if hidden {
|
||||||
|
self.update(hidden: true, slide: true, animated: false)
|
||||||
|
}
|
||||||
self.animateIn(from: convertedRect)
|
self.animateIn(from: convertedRect)
|
||||||
|
if hidden {
|
||||||
|
self.controller?.setupVisibilityUpdates()
|
||||||
|
}
|
||||||
actionButton.ignoreHierarchyChanges = false
|
actionButton.ignoreHierarchyChanges = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,33 +313,26 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
|
|
||||||
private weak var parentNavigationController: NavigationController?
|
private weak var parentNavigationController: NavigationController?
|
||||||
private var currentParams: ([UIViewController], [UIViewController], VoiceChatActionButton.State)?
|
private var currentParams: ([UIViewController], [UIViewController], VoiceChatActionButton.State)?
|
||||||
|
fileprivate var initiallyHidden: Bool
|
||||||
|
|
||||||
init(actionButton: VoiceChatActionButton, audioOutputNode: CallControllerButtonItemNode, leaveNode: CallControllerButtonItemNode, navigationController: NavigationController?) {
|
init(actionButton: VoiceChatActionButton, audioOutputNode: CallControllerButtonItemNode, leaveNode: CallControllerButtonItemNode, navigationController: NavigationController?, initiallyHidden: Bool) {
|
||||||
self.actionButton = actionButton
|
self.actionButton = actionButton
|
||||||
self.audioOutputNode = audioOutputNode
|
self.audioOutputNode = audioOutputNode
|
||||||
self.leaveNode = leaveNode
|
self.leaveNode = leaveNode
|
||||||
self.parentNavigationController = navigationController
|
self.parentNavigationController = navigationController
|
||||||
|
self.initiallyHidden = initiallyHidden
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
|
|
||||||
if case .active(.cantSpeak) = actionButton.stateValue {
|
if case .active(.cantSpeak) = actionButton.stateValue {
|
||||||
} else {
|
} else if !initiallyHidden {
|
||||||
self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
|
self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0)
|
||||||
}
|
}
|
||||||
if let navigationController = navigationController {
|
|
||||||
let controllers: Signal<[UIViewController], NoError> = .single([])
|
if !self.initiallyHidden {
|
||||||
|> then(navigationController.viewControllersSignal)
|
self.setupVisibilityUpdates()
|
||||||
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|
|
||||||
|> then(navigationController.overlayControllersSignal)
|
|
||||||
|
|
||||||
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers, actionButton.state)).start(next: { [weak self] controllers, overlayControllers, state in
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.currentParams = (controllers, overlayControllers, state)
|
|
||||||
strongSelf.updateVisibility()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,6 +349,22 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func setupVisibilityUpdates() {
|
||||||
|
if let navigationController = self.parentNavigationController, let actionButton = self.actionButton {
|
||||||
|
let controllers: Signal<[UIViewController], NoError> = .single([])
|
||||||
|
|> then(navigationController.viewControllersSignal)
|
||||||
|
let overlayControllers: Signal<[UIViewController], NoError> = .single([])
|
||||||
|
|> then(navigationController.overlayControllersSignal)
|
||||||
|
|
||||||
|
self.disposable = (combineLatest(queue: Queue.mainQueue(), controllers, overlayControllers, actionButton.state)).start(next: { [weak self] controllers, overlayControllers, state in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.currentParams = (controllers, overlayControllers, state)
|
||||||
|
strongSelf.updateVisibility()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override func dismiss(completion: (() -> Void)? = nil) {
|
public override func dismiss(completion: (() -> Void)? = nil) {
|
||||||
super.dismiss(completion: completion)
|
super.dismiss(completion: completion)
|
||||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
@ -389,6 +396,7 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
var slide = true
|
var slide = true
|
||||||
var hidden = true
|
var hidden = true
|
||||||
var animated = true
|
var animated = true
|
||||||
|
var animateInsets = true
|
||||||
if controllers.count == 1 || controllers.last is ChatController {
|
if controllers.count == 1 || controllers.last is ChatController {
|
||||||
if let chatController = controllers.last as? ChatController {
|
if let chatController = controllers.last as? ChatController {
|
||||||
slide = false
|
slide = false
|
||||||
@ -413,7 +421,8 @@ public final class VoiceChatOverlayController: ViewController {
|
|||||||
}
|
}
|
||||||
if hasVoiceChatController {
|
if hasVoiceChatController {
|
||||||
hidden = false
|
hidden = false
|
||||||
animated = false
|
animated = self.initiallyHidden
|
||||||
|
self.initiallyHidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
self.controllerNode.update(hidden: hidden, slide: slide, animated: animated)
|
self.controllerNode.update(hidden: hidden, slide: slide, animated: animated)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user