mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-03-28 01:28:46 +00:00
Implementing sheet full expansion
This commit is contained in:
@@ -617,6 +617,25 @@ public struct Transition {
|
||||
}
|
||||
}
|
||||
|
||||
public func animateCornerRadius(layer: CALayer, from fromValue: CGFloat, to toValue: CGFloat) {
|
||||
switch self.animation {
|
||||
case .none:
|
||||
break
|
||||
case let .curve(duration, curve):
|
||||
layer.animate(
|
||||
from: fromValue as NSNumber,
|
||||
to: toValue as NSNumber,
|
||||
keyPath: "cornerRadius",
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
curve: curve,
|
||||
removeOnCompletion: true,
|
||||
additive: false,
|
||||
completion: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func animateBoundsOrigin(layer: CALayer, from fromValue: CGPoint, to toValue: CGPoint, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
switch self.animation {
|
||||
case .none:
|
||||
|
||||
@@ -736,6 +736,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
|
||||
private(set) var displayUI: Bool = true
|
||||
var dismissOffset: CGFloat = 0.0
|
||||
var initialOffset: CGFloat = 0.0
|
||||
// TODO: remove (replaced by isFullscreen)
|
||||
var storedIsLandscape: Bool?
|
||||
var isFullscreen: Bool = false
|
||||
@@ -976,6 +977,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
var isFullscreen = state.isFullscreen
|
||||
let isLandscape = context.availableSize.width > context.availableSize.height
|
||||
|
||||
if let videoSize = context.state.videoSize {
|
||||
// Always fullscreen in landscape
|
||||
if /*videoSize.width > videoSize.height &&*/ isLandscape && !isFullscreen {
|
||||
@@ -987,6 +989,16 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
let videoHeight: CGFloat = context.availableSize.width / 16 * 9
|
||||
let bottomPadding = 40 + environment.safeInsets.bottom
|
||||
let sheetHeight: CGFloat = isFullscreen ? context.availableSize.height : (44 + videoHeight + 40 + 69 + 16 + 32 + 70 + bottomPadding)
|
||||
let isFullyDragged = context.availableSize.height - sheetHeight + state.dismissOffset - context.view.safeAreaInsets.top < 30
|
||||
|
||||
var dragOffset = context.state.dismissOffset
|
||||
if isFullyDragged {
|
||||
dragOffset = max(context.state.dismissOffset, sheetHeight - context.availableSize.height + context.view.safeAreaInsets.top)// sheetHeight - UIScreen.main.bounds.height
|
||||
}
|
||||
|
||||
let video = video.update(
|
||||
component: MediaStreamVideoComponent(
|
||||
call: context.component.call,
|
||||
@@ -1324,12 +1336,8 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
subtitle: memberCountString
|
||||
))
|
||||
}
|
||||
|
||||
let videoHeight: CGFloat = context.availableSize.width / 16 * 9
|
||||
let bottomPadding = 40 + environment.safeInsets.bottom
|
||||
let sheetHeight: CGFloat = isFullscreen ? context.availableSize.height : (44 + videoHeight + 40 + 69 + 16 + 32 + 70 + bottomPadding)
|
||||
let isFullyDragged = context.availableSize.height - sheetHeight + state.dismissOffset < 30
|
||||
|
||||
let availableSize = context.availableSize
|
||||
let safeAreaTop = context.view.safeAreaInsets.top
|
||||
context.add(background
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
.gesture(.tap { [weak state] in
|
||||
@@ -1344,9 +1352,9 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
switch panState {
|
||||
case .began:
|
||||
break
|
||||
state.initialOffset = state.dismissOffset
|
||||
case let .updated(offset):
|
||||
state.updateDismissOffset(value: offset.y, interactive: true)
|
||||
state.updateDismissOffset(value: state.initialOffset + offset.y, interactive: true)
|
||||
case let .ended(velocity):
|
||||
// TODO: Dismiss sheet depending on velocity
|
||||
if velocity.y > 200.0 {
|
||||
@@ -1357,7 +1365,11 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
controller.updateOrientation(orientation: .portrait)
|
||||
}
|
||||
} else {
|
||||
let _ = call.leave(terminateIfPossible: false)
|
||||
if isFullyDragged || state.initialOffset != 0 {
|
||||
state.updateDismissOffset(value: 0.0, interactive: false)
|
||||
} else {
|
||||
let _ = call.leave(terminateIfPossible: false)
|
||||
}
|
||||
}
|
||||
/*activatePictureInPicture.invoke(Action { [weak state] in
|
||||
guard let state = state, let controller = controller() as? MediaStreamComponentController else {
|
||||
@@ -1367,7 +1379,16 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
controller.dismiss(closing: false, manual: true)
|
||||
})*/
|
||||
} else {
|
||||
state.updateDismissOffset(value: 0.0, interactive: false)
|
||||
if isFullyDragged {
|
||||
state.updateDismissOffset(value: sheetHeight - availableSize.height + safeAreaTop, interactive: false)
|
||||
} else {
|
||||
if velocity.y < -200 {
|
||||
// Expand
|
||||
state.updateDismissOffset(value: sheetHeight - availableSize.height + safeAreaTop, interactive: false)
|
||||
} else {
|
||||
state.updateDismissOffset(value: 0.0, interactive: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1483,19 +1504,21 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
component: StreamSheetComponent(
|
||||
topComponent: AnyComponent(navigationComponent),
|
||||
bottomButtonsRow: bottomComponent,
|
||||
topOffset: context.availableSize.height - sheetHeight + context.state.dismissOffset,
|
||||
sheetHeight: max(sheetHeight - context.state.dismissOffset, sheetHeight),
|
||||
topOffset: context.availableSize.height - sheetHeight + dragOffset,
|
||||
sheetHeight: max(sheetHeight - dragOffset, sheetHeight),
|
||||
backgroundColor: isFullscreen ? .clear : (isFullyDragged ? fullscreenBackgroundColor : panelBackgroundColor),
|
||||
bottomPadding: bottomPadding,
|
||||
participantsCount: context.state.originInfo?.memberCount ?? 0 // Int.random(in: 0...999998)// [0, 5, 15, 16, 95, 100, 16042, 942539].randomElement()!
|
||||
participantsCount: context.state.originInfo?.memberCount ?? 0, // Int.random(in: 0...999998)// [0, 5, 15, 16, 95, 100, 16042, 942539].randomElement()!
|
||||
//
|
||||
isFullyExtended: isFullyDragged,
|
||||
deviceCornerRadius: deviceCornerRadius ?? 0
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
// TODO: calculate (although not necessary currently)
|
||||
let sheetOffset: CGFloat = context.availableSize.height - sheetHeight + context.state.dismissOffset
|
||||
let sheetOffset: CGFloat = context.availableSize.height - sheetHeight + dragOffset
|
||||
let sheetPosition = sheetOffset + sheetHeight / 2
|
||||
// Sheet underneath the video when in sheet
|
||||
// if !isFullscreen {
|
||||
@@ -1507,7 +1530,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
let videoPos: CGFloat
|
||||
|
||||
if isFullscreen {
|
||||
videoPos = context.availableSize.height / 2 + state.dismissOffset
|
||||
videoPos = context.availableSize.height / 2 + dragOffset
|
||||
} else {
|
||||
videoPos = sheetPosition - sheetHeight / 2 + videoHeight / 2 + 50
|
||||
}
|
||||
@@ -1516,7 +1539,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
)
|
||||
} else {
|
||||
context.add(video
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2 + state.dismissOffset)
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2 + dragOffset)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1571,7 +1594,9 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
sheetHeight: max(sheetHeight - context.state.dismissOffset, sheetHeight),
|
||||
backgroundColor: isFullscreen ? .clear : (isFullyDragged ? fullscreenBackgroundColor : panelBackgroundColor),
|
||||
bottomPadding: 12,
|
||||
participantsCount: -1 // context.state.originInfo?.memberCount ?? 0
|
||||
participantsCount: -1, // context.state.originInfo?.memberCount ?? 0
|
||||
isFullyExtended: isFullyDragged,
|
||||
deviceCornerRadius: deviceCornerRadius ?? 0
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
@@ -1597,6 +1622,9 @@ public final class _MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: pass to component properly
|
||||
var deviceCornerRadius: CGFloat? = nil
|
||||
|
||||
public final class _MediaStreamComponentController: ViewControllerComponentContainer, VoiceChatController {
|
||||
private let context: AccountContext
|
||||
public let call: PresentationGroupCall
|
||||
@@ -1642,12 +1670,15 @@ public final class _MediaStreamComponentController: ViewControllerComponentConta
|
||||
view.expandFromPictureInPicture()
|
||||
}
|
||||
|
||||
if let _ = self.validLayout {
|
||||
if let validLayout = self.validLayout {
|
||||
self.view.clipsToBounds = true
|
||||
|
||||
// TODO: pass to component properly
|
||||
deviceCornerRadius = validLayout.deviceMetrics.screenCornerRadius - 1// 0.5
|
||||
// self.view.layer.cornerRadius = validLayout.deviceMetrics.screenCornerRadius
|
||||
if #available(iOS 13.0, *) {
|
||||
self.view.layer.cornerCurve = .continuous
|
||||
}
|
||||
// if #available(iOS 13.0, *) {
|
||||
// self.view.layer.cornerCurve = .continuous
|
||||
// }
|
||||
|
||||
self.view.layer.animatePosition(from: CGPoint(x: self.view.frame.center.x, y: self.view.bounds.maxY + self.view.bounds.height / 2), to: self.view.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in // [weak self] _ in
|
||||
// self?.view.layer.cornerRadius = 0.0
|
||||
|
||||
@@ -19,6 +19,8 @@ final class StreamSheetComponent: CombinedComponent {
|
||||
let backgroundColor: UIColor
|
||||
let participantsCount: Int
|
||||
let bottomPadding: CGFloat
|
||||
let isFullyExtended: Bool
|
||||
let deviceCornerRadius: CGFloat
|
||||
|
||||
init(
|
||||
// color: UIColor,
|
||||
@@ -28,7 +30,9 @@ final class StreamSheetComponent: CombinedComponent {
|
||||
sheetHeight: CGFloat,
|
||||
backgroundColor: UIColor,
|
||||
bottomPadding: CGFloat,
|
||||
participantsCount: Int
|
||||
participantsCount: Int,
|
||||
isFullyExtended: Bool,
|
||||
deviceCornerRadius: CGFloat
|
||||
) {
|
||||
// self.leftItem = leftItem
|
||||
self.topComponent = topComponent
|
||||
@@ -39,6 +43,8 @@ final class StreamSheetComponent: CombinedComponent {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.bottomPadding = bottomPadding
|
||||
self.participantsCount = participantsCount
|
||||
self.isFullyExtended = isFullyExtended
|
||||
self.deviceCornerRadius = deviceCornerRadius
|
||||
}
|
||||
|
||||
static func ==(lhs: StreamSheetComponent, rhs: StreamSheetComponent) -> Bool {
|
||||
@@ -66,6 +72,9 @@ final class StreamSheetComponent: CombinedComponent {
|
||||
if lhs.participantsCount != rhs.participantsCount {
|
||||
return false
|
||||
}
|
||||
if lhs.isFullyExtended != rhs.isFullyExtended {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
//
|
||||
@@ -80,7 +89,7 @@ final class StreamSheetComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
func update(component: StreamSheetComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
|
||||
self.backgroundColor = .purple.withAlphaComponent(0.6)
|
||||
// self.backgroundColor = .purple.withAlphaComponent(0.6)
|
||||
return availableSize
|
||||
}
|
||||
|
||||
@@ -132,11 +141,21 @@ final class StreamSheetComponent: CombinedComponent {
|
||||
return { context in
|
||||
let availableWidth = context.availableSize.width
|
||||
// let sideInset: CGFloat = 16.0 + context.component.sideInset
|
||||
|
||||
let contentHeight: CGFloat = 44.0
|
||||
let size = context.availableSize// CGSize(width: context.availableSize.width, height:44)// context.component.topInset + contentHeight)
|
||||
|
||||
let background = background.update(component: SheetBackgroundComponent(color: context.component.backgroundColor), availableSize: CGSize(width: size.width, height: context.component.sheetHeight), transition: context.transition)
|
||||
let topOffset = context.component.topOffset
|
||||
let backgroundExtraOffset = context.component.isFullyExtended ? -context.view.safeAreaInsets.top : 0
|
||||
|
||||
let background = background.update(
|
||||
component: SheetBackgroundComponent(
|
||||
color: context.component.backgroundColor,
|
||||
radius: context.component.isFullyExtended ? context.component.deviceCornerRadius : 16,
|
||||
offset: backgroundExtraOffset
|
||||
),
|
||||
availableSize: CGSize(width: size.width, height: context.component.sheetHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let topItem = context.component.topComponent.flatMap { topItemComponent in
|
||||
return topItem.update(
|
||||
@@ -160,10 +179,9 @@ final class StreamSheetComponent: CombinedComponent {
|
||||
)
|
||||
}
|
||||
|
||||
let topOffset = context.component.topOffset
|
||||
|
||||
context.add(background
|
||||
.position(CGPoint(x: size.width / 2.0, y: context.component.topOffset + context.component.sheetHeight / 2))
|
||||
.position(CGPoint(x: size.width / 2.0, y: topOffset + context.component.sheetHeight / 2))
|
||||
// .position(CGPoint(x: size.width / 2.0, y: context.component.topOffset + context.component.sheetHeight / 2 + backgroundExtraOffset))
|
||||
)
|
||||
|
||||
(context.view as? StreamSheetComponent.View)?.overlayComponentsFrames = []
|
||||
@@ -208,27 +226,45 @@ private let latePink = UIColor(rgb: 0xf0436c)
|
||||
|
||||
final class SheetBackgroundComponent: Component {
|
||||
private let color: UIColor
|
||||
private let radius: CGFloat
|
||||
private let offset: CGFloat
|
||||
|
||||
class View: UIView {
|
||||
private let backgroundView = UIView()
|
||||
|
||||
func update(availableSize: CGSize, color: UIColor, transition: Transition) {
|
||||
func update(availableSize: CGSize, color: UIColor, cornerRadius: CGFloat, offset: CGFloat, transition: Transition) {
|
||||
if backgroundView.superview == nil {
|
||||
self.addSubview(backgroundView)
|
||||
}
|
||||
// To fix release animation
|
||||
let extraBottom: CGFloat = 500
|
||||
backgroundView.frame = .init(origin: .zero, size: .init(width: availableSize.width, height: availableSize.height + extraBottom))
|
||||
if backgroundView.backgroundColor != color {
|
||||
|
||||
if backgroundView.backgroundColor != color && backgroundView.backgroundColor != nil {
|
||||
// let initialVelocity: CGFloat = 0
|
||||
// let xtransition = ComponentFlow.Transition(animation: .curve(duration: 0.45, curve: .spring))// .animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
|
||||
|
||||
UIView.animate(withDuration: 0.4) { [self] in
|
||||
backgroundView.backgroundColor = color
|
||||
// TODO: determine if animation is needed (with facts and logic, not color)
|
||||
backgroundView.frame = .init(origin: .init(x: 0, y: offset), size: .init(width: availableSize.width, height: availableSize.height + extraBottom))
|
||||
}
|
||||
|
||||
let anim = CABasicAnimation(keyPath: "cornerRadius")
|
||||
anim.fromValue = backgroundView.layer.cornerRadius
|
||||
backgroundView.layer.cornerRadius = cornerRadius
|
||||
anim.toValue = cornerRadius
|
||||
anim.duration = 0.4
|
||||
backgroundView.layer.add(anim, forKey: "cornerRadius")
|
||||
} else {
|
||||
backgroundView.backgroundColor = color
|
||||
backgroundView.frame = .init(origin: .init(x: 0, y: offset), size: .init(width: availableSize.width, height: availableSize.height + extraBottom))
|
||||
backgroundView.layer.cornerRadius = cornerRadius
|
||||
}
|
||||
backgroundView.isUserInteractionEnabled = false
|
||||
backgroundView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
backgroundView.layer.cornerRadius = 16
|
||||
// let currentRadius = backgroundView.layer.cornerRadius
|
||||
// backgroundView.layer.cornerRadius = cornerRadius
|
||||
// transition.animateCornerRadius(layer: backgroundView.layer, from: currentRadius, to: cornerRadius)
|
||||
backgroundView.clipsToBounds = true
|
||||
backgroundView.layer.masksToBounds = true
|
||||
}
|
||||
@@ -242,6 +278,12 @@ final class SheetBackgroundComponent: Component {
|
||||
if !lhs.color.isEqual(rhs.color) {
|
||||
return false
|
||||
}
|
||||
if lhs.radius != rhs.radius {
|
||||
return false
|
||||
}
|
||||
if lhs.offset != rhs.offset {
|
||||
return false
|
||||
}
|
||||
// if lhs.width != rhs.width {
|
||||
// return false
|
||||
// }
|
||||
@@ -251,12 +293,14 @@ final class SheetBackgroundComponent: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
public init(color: UIColor) {
|
||||
public init(color: UIColor, radius: CGFloat, offset: CGFloat) {
|
||||
self.color = color
|
||||
self.radius = radius
|
||||
self.offset = offset
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
view.update(availableSize: availableSize, color: color, transition: transition)
|
||||
view.update(availableSize: availableSize, color: color, cornerRadius: radius, offset: offset, transition: transition)
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user