mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Throttling playback status change
This commit is contained in:
parent
bfa7de8e27
commit
17ef4ae35b
@ -801,7 +801,8 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
strongSelf.updated(transition: .immediate)
|
strongSelf.updated(transition: .immediate)
|
||||||
})
|
})
|
||||||
|
|
||||||
self.networkStateDisposable = (call.account.networkState |> deliverOnMainQueue).start(next: { [weak self] state in
|
// TODO: retest to uncomment or delete. Relying only on video frames
|
||||||
|
/*self.networkStateDisposable = (call.account.networkState |> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let strongSelf = self else { return }
|
guard let strongSelf = self else { return }
|
||||||
switch state {
|
switch state {
|
||||||
case .waitingForNetwork, .connecting:
|
case .waitingForNetwork, .connecting:
|
||||||
@ -828,7 +829,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
if prev != self?.videoStalled {
|
if prev != self?.videoStalled {
|
||||||
self?.updated(transition: .immediate)
|
self?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
})
|
})*/
|
||||||
|
|
||||||
let callPeer = call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: call.peerId))
|
let callPeer = call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: call.peerId))
|
||||||
|
|
||||||
@ -956,6 +957,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
|
|
||||||
public static var body: Body {
|
public static var body: Body {
|
||||||
let background = Child(Rectangle.self)
|
let background = Child(Rectangle.self)
|
||||||
|
let dismissTapComponent = Child(Rectangle.self)
|
||||||
let video = Child(MediaStreamVideoComponent.self)
|
let video = Child(MediaStreamVideoComponent.self)
|
||||||
// let navigationBar = Child(NavigationBarComponent.self)
|
// let navigationBar = Child(NavigationBarComponent.self)
|
||||||
// let toolbar = Child(ToolbarComponent.self)
|
// let toolbar = Child(ToolbarComponent.self)
|
||||||
@ -1037,6 +1039,14 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
dragOffset = max(context.state.dismissOffset, sheetHeight - context.availableSize.height + context.view.safeAreaInsets.top)// sheetHeight - UIScreen.main.bounds.height
|
dragOffset = max(context.state.dismissOffset, sheetHeight - context.availableSize.height + context.view.safeAreaInsets.top)// sheetHeight - UIScreen.main.bounds.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dismissTapAreaHeight = isFullscreen ? 0 : (context.availableSize.height - sheetHeight + dragOffset)
|
||||||
|
let dismissTapComponent = dismissTapComponent.update(
|
||||||
|
component: Rectangle(color: .red.withAlphaComponent(0)),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width, height: dismissTapAreaHeight),
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
let video = video.update(
|
let video = video.update(
|
||||||
component: MediaStreamVideoComponent(
|
component: MediaStreamVideoComponent(
|
||||||
call: context.component.call,
|
call: context.component.call,
|
||||||
@ -1044,7 +1054,7 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
isVisible: environment.isVisible && context.state.isVisibleInHierarchy,
|
isVisible: environment.isVisible && context.state.isVisibleInHierarchy,
|
||||||
isAdmin: context.state.canManageCall,
|
isAdmin: context.state.canManageCall,
|
||||||
peerTitle: context.state.peerTitle,
|
peerTitle: context.state.peerTitle,
|
||||||
// TODO: find out how to get image
|
// TODO: remove // find out how to get image
|
||||||
peerImage: nil,
|
peerImage: nil,
|
||||||
isFullscreen: isFullscreen,
|
isFullscreen: isFullscreen,
|
||||||
videoLoading: context.state.videoStalled,
|
videoLoading: context.state.videoStalled,
|
||||||
@ -1068,8 +1078,12 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
state?.videoSize = size
|
state?.videoSize = size
|
||||||
},
|
},
|
||||||
onVideoPlaybackLiveChange: { [weak state] isLive in
|
onVideoPlaybackLiveChange: { [weak state] isLive in
|
||||||
state?.videoStalled = !isLive
|
guard let state else { return }
|
||||||
state?.updated()
|
let wasLive = !state.videoStalled
|
||||||
|
if isLive != wasLive {
|
||||||
|
state.videoStalled = !isLive
|
||||||
|
state.updated()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
availableSize: context.availableSize,
|
availableSize: context.availableSize,
|
||||||
@ -1381,15 +1395,8 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
let availableSize = context.availableSize
|
let availableSize = context.availableSize
|
||||||
let safeAreaTop = context.view.safeAreaInsets.top
|
let safeAreaTop = context.view.safeAreaInsets.top
|
||||||
context.add(background
|
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
let onPanGesture: ((Gesture.PanGestureState) -> Void) = { [weak state] panState in
|
||||||
.gesture(.tap { [weak state] in
|
|
||||||
guard let state = state else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
state.toggleDisplayUI()
|
|
||||||
})
|
|
||||||
.gesture(.pan { [weak state] panState in
|
|
||||||
guard let state = state else {
|
guard let state = state else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1434,11 +1441,31 @@ public final class _MediaStreamComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.add(background
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||||
|
.gesture(.tap { [weak state] in
|
||||||
|
guard let state = state, state.isFullscreen else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.toggleDisplayUI()
|
||||||
|
})
|
||||||
|
.gesture(.pan { panState in
|
||||||
|
onPanGesture(panState)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
// var bottomComponent: AnyComponent<Empty>?
|
// var bottomComponent: AnyComponent<Empty>?
|
||||||
// var fullScreenToolbarComponent: AnyComponent<Empty>?
|
// var fullScreenToolbarComponent: AnyComponent<Empty>?
|
||||||
|
|
||||||
|
context.add(dismissTapComponent
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2, y: dismissTapAreaHeight / 2))
|
||||||
|
.gesture(.tap {
|
||||||
|
_ = call.leave(terminateIfPossible: false)
|
||||||
|
})
|
||||||
|
.gesture(.pan(onPanGesture))
|
||||||
|
)
|
||||||
|
|
||||||
if !isFullscreen {
|
if !isFullscreen {
|
||||||
let bottomComponent = AnyComponent(ButtonsRowComponent(
|
let bottomComponent = AnyComponent(ButtonsRowComponent(
|
||||||
bottomInset: environment.safeInsets.bottom,
|
bottomInset: environment.safeInsets.bottom,
|
||||||
|
|||||||
@ -238,10 +238,10 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
// self.loadingBlurView.animator.fractionComplete = intensity
|
// self.loadingBlurView.animator.fractionComplete = intensity
|
||||||
// self.loadingBlurView.animator.fractionComplete = 0.4
|
// self.loadingBlurView.animator.fractionComplete = 0.4
|
||||||
// self.loadingBlurView.effect = UIBlurEffect(style: .light)
|
// self.loadingBlurView.effect = UIBlurEffect(style: .light)
|
||||||
if let frame = lastFrame[component.call.peerId.id.description] {
|
if let frameView = lastFrame[component.call.peerId.id.description] {
|
||||||
placeholderView.subviews.forEach { $0.removeFromSuperview() }
|
placeholderView.subviews.forEach { $0.removeFromSuperview() }
|
||||||
placeholderView.addSubview(frame)
|
placeholderView.addSubview(frameView)
|
||||||
frame.frame = placeholderView.bounds
|
frameView.frame = placeholderView.bounds
|
||||||
// placeholderView.backgroundColor = .green
|
// placeholderView.backgroundColor = .green
|
||||||
} else {
|
} else {
|
||||||
// placeholderView.addSubview(avatarPlaceholderView)
|
// placeholderView.addSubview(avatarPlaceholderView)
|
||||||
@ -252,8 +252,12 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
if !hadVideo && placeholderView.superview == nil {
|
if !hadVideo && placeholderView.superview == nil {
|
||||||
addSubview(placeholderView)
|
addSubview(placeholderView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let needsFadeInAnimation = hadVideo
|
||||||
|
|
||||||
if loadingBlurView.superview == nil {
|
if loadingBlurView.superview == nil {
|
||||||
addSubview(loadingBlurView)
|
addSubview(loadingBlurView)
|
||||||
|
if needsFadeInAnimation {
|
||||||
let anim = CABasicAnimation(keyPath: "opacity")
|
let anim = CABasicAnimation(keyPath: "opacity")
|
||||||
anim.duration = 0.5
|
anim.duration = 0.5
|
||||||
anim.fromValue = 0
|
anim.fromValue = 0
|
||||||
@ -262,20 +266,22 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
anim.isRemovedOnCompletion = false
|
anim.isRemovedOnCompletion = false
|
||||||
loadingBlurView.layer.add(anim, forKey: "opacity")
|
loadingBlurView.layer.add(anim, forKey: "opacity")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if shimmerBorderLayer.superlayer == nil {
|
if shimmerBorderLayer.superlayer == nil {
|
||||||
// loadingBlurView.contentView.layer.addSublayer(shimmerOverlayLayer)
|
// loadingBlurView.contentView.layer.addSublayer(shimmerOverlayLayer)
|
||||||
loadingBlurView.contentView.layer.addSublayer(shimmerBorderLayer)
|
loadingBlurView.contentView.layer.addSublayer(shimmerBorderLayer)
|
||||||
}
|
}
|
||||||
loadingBlurView.clipsToBounds = true
|
loadingBlurView.clipsToBounds = true
|
||||||
if shimmerOverlayLayer.mask == nil {
|
// if shimmerOverlayLayer.mask == nil {
|
||||||
shimmer = .init()
|
// shimmer = .init()
|
||||||
shimmer.layer = shimmerOverlayLayer
|
// shimmer.layer = shimmerOverlayLayer
|
||||||
shimmerOverlayView.compositingFilter = "softLightBlendMode"
|
// shimmerOverlayView.compositingFilter = "softLightBlendMode"
|
||||||
shimmer.testUpdate(background: .clear, foreground: .white.withAlphaComponent(0.4))
|
// shimmer.testUpdate(background: .clear, foreground: .white.withAlphaComponent(0.4))
|
||||||
}
|
// }
|
||||||
// loadingBlurView.layer.cornerRadius = 10
|
// loadingBlurView.layer.cornerRadius = 10
|
||||||
shimmerOverlayLayer.opacity = 0.6
|
|
||||||
let cornerRadius = loadingBlurView.layer.cornerRadius
|
let cornerRadius = loadingBlurView.layer.cornerRadius
|
||||||
|
// shimmerOverlayLayer.opacity = 0.6
|
||||||
shimmerBorderLayer.cornerRadius = cornerRadius // TODO: check isFullScreeen
|
shimmerBorderLayer.cornerRadius = cornerRadius // TODO: check isFullScreeen
|
||||||
shimmerBorderLayer.masksToBounds = true
|
shimmerBorderLayer.masksToBounds = true
|
||||||
shimmerBorderLayer.compositingFilter = "softLightBlendMode"
|
shimmerBorderLayer.compositingFilter = "softLightBlendMode"
|
||||||
@ -295,14 +301,14 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
// testBorder.frame = shimmerBorderLayer.bounds
|
// testBorder.frame = shimmerBorderLayer.bounds
|
||||||
// let borderMask = CALayer()
|
// let borderMask = CALayer()
|
||||||
// shimmerBorderLayer.removeAllAnimations()
|
// shimmerBorderLayer.removeAllAnimations()
|
||||||
// if shimmerBorderLayer.mask == nil {
|
// if shimmerBorderLayer.mask == nil {
|
||||||
borderShimmer = .init()
|
borderShimmer = .init()
|
||||||
shimmerBorderLayer.mask = borderMask
|
shimmerBorderLayer.mask = borderMask
|
||||||
borderShimmer.layer = shimmerBorderLayer
|
borderShimmer.layer = shimmerBorderLayer
|
||||||
shimmerBorderLayer.backgroundColor = UIColor.clear.cgColor
|
shimmerBorderLayer.backgroundColor = UIColor.clear.cgColor
|
||||||
// shimmerBorderLayer.backgroundColor = UIColor.green.withAlphaComponent(0.4).cgColor
|
// shimmerBorderLayer.backgroundColor = UIColor.green.withAlphaComponent(0.4).cgColor
|
||||||
borderShimmer.testUpdate(background: .clear, foreground: .white)
|
borderShimmer.testUpdate(background: .clear, foreground: .white)
|
||||||
// }
|
// }
|
||||||
loadingBlurView.alpha = 1
|
loadingBlurView.alpha = 1
|
||||||
} else {
|
} else {
|
||||||
if hadVideo {
|
if hadVideo {
|
||||||
@ -313,32 +319,32 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
anim.toValue = 0
|
anim.toValue = 0
|
||||||
anim.fillMode = .forwards
|
anim.fillMode = .forwards
|
||||||
anim.isRemovedOnCompletion = false
|
anim.isRemovedOnCompletion = false
|
||||||
anim.completion = { [self] _ in
|
anim.completion = { [weak self] _ in
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
|
guard self?.videoStalled == false else { return }
|
||||||
loadingBlurView.removeFromSuperview()
|
self?.loadingBlurView.removeFromSuperview()
|
||||||
// loadingBlurView = .init(effect: UIBlurEffect(style: .light), intensity: 0.4)
|
self?.placeholderView.removeFromSuperview()
|
||||||
placeholderView.removeFromSuperview()
|
|
||||||
}
|
}
|
||||||
loadingBlurView.layer.add(anim, forKey: "opacity")
|
loadingBlurView.layer.add(anim, forKey: "opacity")
|
||||||
} else {
|
} else {
|
||||||
// Accounting for delay in first frame received
|
// Accounting for delay in first frame received
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
|
||||||
guard !self.videoStalled else { return }
|
guard self?.videoStalled == false else { return }
|
||||||
|
|
||||||
// TODO: animate blur intesity with UIPropertyAnimator
|
// TODO: animate blur intesity with UIPropertyAnimator
|
||||||
loadingBlurView.layer.removeAllAnimations()
|
self?.loadingBlurView.layer.removeAllAnimations()
|
||||||
let anim = CABasicAnimation(keyPath: "opacity")
|
let anim = CABasicAnimation(keyPath: "opacity")
|
||||||
anim.duration = 0.5
|
anim.duration = 0.5
|
||||||
anim.fromValue = 1
|
anim.fromValue = 1
|
||||||
anim.toValue = 0
|
anim.toValue = 0
|
||||||
anim.fillMode = .forwards
|
anim.fillMode = .forwards
|
||||||
anim.isRemovedOnCompletion = false
|
anim.isRemovedOnCompletion = false
|
||||||
anim.completion = { _ in
|
anim.completion = { [weak self] _ in
|
||||||
|
guard self?.videoStalled == false else { return }
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
|
||||||
self.loadingBlurView.removeFromSuperview()
|
self?.loadingBlurView.removeFromSuperview()
|
||||||
self.placeholderView.removeFromSuperview()
|
self?.placeholderView.removeFromSuperview()
|
||||||
}
|
}
|
||||||
loadingBlurView.layer.add(anim, forKey: "opacity")
|
self?.loadingBlurView.layer.add(anim, forKey: "opacity")
|
||||||
// UIView.transition(with: self, duration: 0.2, animations: {
|
// UIView.transition(with: self, duration: 0.2, animations: {
|
||||||
//// self.loadingBlurView.animator.fractionComplete = 0
|
//// self.loadingBlurView.animator.fractionComplete = 0
|
||||||
//// self.loadingBlurView.effect = nil
|
//// self.loadingBlurView.effect = nil
|
||||||
@ -361,6 +367,7 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
var timeLastFrameReceived: CFAbsoluteTime?
|
var timeLastFrameReceived: CFAbsoluteTime?
|
||||||
|
|
||||||
var isFullscreen: Bool = false
|
var isFullscreen: Bool = false
|
||||||
|
let videoLoadingThrottler = Throttler<Bool>(duration: 1, queue: .main)
|
||||||
|
|
||||||
func update(component: _MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
|
func update(component: _MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
|
||||||
self.state = state
|
self.state = state
|
||||||
@ -381,7 +388,7 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if component.videoLoading || self.videoStalled {
|
if !component.hasVideo || component.videoLoading || self.videoStalled {
|
||||||
updateVideoStalled(isStalled: true)
|
updateVideoStalled(isStalled: true)
|
||||||
} else {
|
} else {
|
||||||
updateVideoStalled(isStalled: false)
|
updateVideoStalled(isStalled: false)
|
||||||
@ -396,10 +403,13 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
let currentTime = CFAbsoluteTimeGetCurrent()
|
let currentTime = CFAbsoluteTimeGetCurrent()
|
||||||
if let lastFrameTime = strongSelf.timeLastFrameReceived,
|
if let lastFrameTime = strongSelf.timeLastFrameReceived,
|
||||||
currentTime - lastFrameTime > 0.5 {
|
currentTime - lastFrameTime > 0.5 {
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
strongSelf.videoStalled = true
|
strongSelf.videoLoadingThrottler.publish(true, includingLatest: true) { isStalled in
|
||||||
strongSelf.onVideoPlaybackChange(false)
|
strongSelf.videoStalled = isStalled
|
||||||
|
strongSelf.onVideoPlaybackChange(!isStalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
} }
|
} }
|
||||||
// TODO: use mapToThrottled (?)
|
// TODO: use mapToThrottled (?)
|
||||||
@ -409,7 +419,7 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
// strongSelf.stallTimer?.invalidate()
|
// strongSelf.stallTimer?.invalidate()
|
||||||
// TODO: optimize with throttle
|
// TODO: optimize with throttle
|
||||||
strongSelf.timeLastFrameReceived = CFAbsoluteTimeGetCurrent()
|
strongSelf.timeLastFrameReceived = CFAbsoluteTimeGetCurrent()
|
||||||
DispatchQueue.main.async {
|
// DispatchQueue.main.async {
|
||||||
// strongSelf.stallTimer = _stallTimer
|
// strongSelf.stallTimer = _stallTimer
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
// print(strongSelf.videoStalled)
|
// print(strongSelf.videoStalled)
|
||||||
@ -417,9 +427,13 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
// strongSelf.stallTimer?.fire()
|
// strongSelf.stallTimer?.fire()
|
||||||
// }
|
// }
|
||||||
// RunLoop.main.add(strongSelf.stallTimer!, forMode: .common)
|
// RunLoop.main.add(strongSelf.stallTimer!, forMode: .common)
|
||||||
strongSelf.videoStalled = false
|
strongSelf.videoLoadingThrottler.publish(false, includingLatest: true) { isStalled in
|
||||||
strongSelf.onVideoPlaybackChange(true)
|
strongSelf.videoStalled = isStalled
|
||||||
|
strongSelf.onVideoPlaybackChange(!isStalled)
|
||||||
}
|
}
|
||||||
|
// strongSelf.videoStalled = false
|
||||||
|
// strongSelf.onVideoPlaybackChange(true)
|
||||||
|
// }
|
||||||
})
|
})
|
||||||
stallTimer = _stallTimer
|
stallTimer = _stallTimer
|
||||||
// RunLoop.main.add(stallTimer!, forMode: .common)
|
// RunLoop.main.add(stallTimer!, forMode: .common)
|
||||||
@ -743,22 +757,24 @@ final class _MediaStreamVideoComponent: Component {
|
|||||||
|
|
||||||
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||||
// Fading to make
|
// Fading to make
|
||||||
let presentation = self.videoView!.snapshotView(afterScreenUpdates: false)!
|
if let presentation = self.videoView!.snapshotView(afterScreenUpdates: false) {
|
||||||
self.addSubview(presentation)
|
self.addSubview(presentation)
|
||||||
presentation.frame = self.videoView!.frame
|
presentation.frame = self.videoView!.frame
|
||||||
lastFrame[self.component!.call.peerId.id.description] = presentation
|
lastFrame[self.component!.call.peerId.id.description] = presentation
|
||||||
// let image = UIGraphicsImageRenderer(size: presentation.bounds.size).image { context in
|
|
||||||
// presentation.render(in: context.cgContext)
|
// let image = UIGraphicsImageRenderer(size: presentation.bounds.size).image { context in
|
||||||
// }
|
// presentation.render(in: context.cgContext)
|
||||||
// print(image)
|
// }
|
||||||
|
// print(image)
|
||||||
self.videoView?.alpha = 0
|
self.videoView?.alpha = 0
|
||||||
// self.videoView?.alpha = 0.5
|
// self.videoView?.alpha = 0.5
|
||||||
// presentation.animateAlpha(from: 1, to: 0, duration: 0.1, completion: { _ in presentation.removeFromSuperlayer() })
|
// presentation.animateAlpha(from: 1, to: 0, duration: 0.1, completion: { _ in presentation.removeFromSuperlayer() })
|
||||||
UIView.animate(withDuration: 0.1, animations: {
|
UIView.animate(withDuration: 0.1, animations: {
|
||||||
presentation.alpha = 0
|
presentation.alpha = 0
|
||||||
}, completion: { _ in
|
}, completion: { _ in
|
||||||
presentation.removeFromSuperview()
|
presentation.removeFromSuperview()
|
||||||
})
|
})
|
||||||
|
}
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||||
// presentation.removeFromSuperlayer()
|
// presentation.removeFromSuperlayer()
|
||||||
// }
|
// }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user