mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Various fixes
This commit is contained in:
parent
53d5425341
commit
6df559ff97
9 changed files with 71 additions and 189 deletions
|
|
@ -5,6 +5,11 @@ import Display
|
|||
import Markdown
|
||||
|
||||
public final class MultilineTextComponent: Component {
|
||||
public final class CrossfadeTransition {
|
||||
public init() {
|
||||
}
|
||||
}
|
||||
|
||||
public enum TextContent: Equatable {
|
||||
case plain(NSAttributedString)
|
||||
case markdown(text: String, attributes: MarkdownAttributes)
|
||||
|
|
@ -141,7 +146,7 @@ public final class MultilineTextComponent: Component {
|
|||
attributedString = parseMarkdownIntoAttributedString(text, attributes: attributes)
|
||||
}
|
||||
|
||||
//let previousText = self.attributedText?.string
|
||||
let previousText = self.attributedText?.string
|
||||
|
||||
self.attributedText = attributedString
|
||||
self.maximumNumberOfLines = component.maximumNumberOfLines
|
||||
|
|
@ -160,7 +165,7 @@ public final class MultilineTextComponent: Component {
|
|||
self.tapAttributeAction = component.tapAction
|
||||
self.longTapAttributeAction = component.longTapAction
|
||||
|
||||
/*if case let .curve(duration, _) = transition.animation, let previousText = previousText, previousText != attributedString.string {
|
||||
if case let .curve(duration, _) = transition.animation, let _ = transition.userData(CrossfadeTransition.self), let previousText = previousText, previousText != attributedString.string {
|
||||
if let snapshotView = self.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.center = self.center
|
||||
self.superview?.addSubview(snapshotView)
|
||||
|
|
@ -170,7 +175,7 @@ public final class MultilineTextComponent: Component {
|
|||
})
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
let size = self.updateLayout(availableSize)
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,6 @@ private let placeholderFont = Font.regular(16.0)
|
|||
private final class UniversalVideoGalleryItemPictureInPictureNode: ASDisplayNode {
|
||||
enum Mode {
|
||||
case pictureInPicture
|
||||
case airplay
|
||||
}
|
||||
|
||||
private let iconNode: ASImageNode
|
||||
|
|
@ -183,8 +182,6 @@ private final class UniversalVideoGalleryItemPictureInPictureNode: ASDisplayNode
|
|||
switch mode {
|
||||
case .pictureInPicture:
|
||||
text = strings.Embed_PlayingInPIP
|
||||
case .airplay:
|
||||
text = strings.Gallery_AirPlayPlaceholder
|
||||
}
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: placeholderFont, textColor: UIColor(rgb: 0x8e8e93))
|
||||
|
||||
|
|
@ -1868,7 +1865,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
var hasMoreButton = false
|
||||
var file: TelegramMediaFile?
|
||||
for m in message.media {
|
||||
if let m = m as? TelegramMediaFile, m.isVideo {
|
||||
if let _ = m as? TelegramMediaImage {
|
||||
hasMoreButton = true
|
||||
} else if let m = m as? TelegramMediaFile, m.isVideo {
|
||||
file = m
|
||||
break
|
||||
} else if let m = m as? TelegramMediaWebpage, case let .Loaded(content) = m.content, let f = content.file, f.isVideo {
|
||||
|
|
@ -2059,13 +2058,13 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
}
|
||||
|
||||
private func updateDisplayPlaceholder() {
|
||||
self.updateDisplayPlaceholder(!(self.videoNode?.ownsContentNode ?? true) || self.isAirPlayActive)
|
||||
self.updateDisplayPlaceholder(!(self.videoNode?.ownsContentNode ?? true))
|
||||
}
|
||||
|
||||
private func updateDisplayPlaceholder(_ displayPlaceholder: Bool) {
|
||||
if displayPlaceholder && !self.disablePictureInPicturePlaceholder {
|
||||
if self.pictureInPictureNode == nil {
|
||||
let pictureInPictureNode = UniversalVideoGalleryItemPictureInPictureNode(strings: self.presentationData.strings, mode: self.isAirPlayActive ? .airplay : .pictureInPicture)
|
||||
let pictureInPictureNode = UniversalVideoGalleryItemPictureInPictureNode(strings: self.presentationData.strings, mode: .pictureInPicture)
|
||||
pictureInPictureNode.isUserInteractionEnabled = false
|
||||
self.pictureInPictureNode = pictureInPictureNode
|
||||
self.insertSubnode(pictureInPictureNode, aboveSubnode: self.scrollNode)
|
||||
|
|
@ -3772,16 +3771,24 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
})))
|
||||
}
|
||||
|
||||
// if #available(iOS 11.0, *) {
|
||||
// items.append(.action(ContextMenuActionItem(text: "AirPlay", textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/AirPlay"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
// f(.default)
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.beginAirPlaySetup()
|
||||
// })))
|
||||
// }
|
||||
|
||||
if let (message, _, _) = strongSelf.contentInfo(), let image = message.media.first(where: { $0 is TelegramMediaImage }) as? TelegramMediaImage, !message.isCopyProtected() && !item.peerIsCopyProtected && message.paidContent == nil {
|
||||
let context = strongSelf.context
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Gallery_SaveImage, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
let _ = (SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .peer(message.id.peerId), mediaReference: .message(message: MessageReference(message), media: image))
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let controller = strongSelf.galleryController() else {
|
||||
return
|
||||
}
|
||||
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: strongSelf.presentationData.strings.Gallery_ImageSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if let (message, _, _) = strongSelf.contentInfo() {
|
||||
for media in message.media {
|
||||
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||
|
|
@ -3839,27 +3846,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
return (items, topItems)
|
||||
}
|
||||
}
|
||||
|
||||
private var isAirPlayActive = false
|
||||
private var externalVideoPlayer: ExternalVideoPlayer?
|
||||
func beginAirPlaySetup() {
|
||||
guard let content = self.item?.content as? NativeVideoContent else {
|
||||
return
|
||||
}
|
||||
if #available(iOS 11.0, *) {
|
||||
self.externalVideoPlayer = ExternalVideoPlayer(context: self.context, content: content)
|
||||
self.externalVideoPlayer?.openRouteSelection()
|
||||
self.externalVideoPlayer?.isActiveUpdated = { [weak self] isActive in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.isAirPlayActive && !isActive {
|
||||
strongSelf.externalVideoPlayer = nil
|
||||
}
|
||||
strongSelf.isAirPlayActive = isActive
|
||||
strongSelf.updateDisplayPlaceholder()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openStickersButtonPressed() {
|
||||
guard let content = self.item?.content as? NativeVideoContent else {
|
||||
|
|
|
|||
|
|
@ -207,7 +207,11 @@ static TGMediaLivePhotoMode TGMediaPickerGalleryResolvedLivePhotoMode(TGMediaEdi
|
|||
[super setItem:item synchronously:synchronously];
|
||||
|
||||
[self stopAndCleanupLivePhotoPlayback];
|
||||
_livePhotoMode = TGMediaPickerGalleryResolvedLivePhotoMode(item.editingContext, item.editableMediaItem, (TGMediaAsset *)item.asset);
|
||||
if ([item.asset isKindOfClass:[TGMediaAsset class]]) {
|
||||
_livePhotoMode = TGMediaPickerGalleryResolvedLivePhotoMode(item.editingContext, item.editableMediaItem, (TGMediaAsset *)item.asset);
|
||||
} else {
|
||||
_livePhotoMode = TGMediaLivePhotoModeOff;
|
||||
}
|
||||
[self updatePaintingImage:[item.editingContext adjustmentsForItem:item.editableMediaItem]];
|
||||
|
||||
if (_entitiesView == nil) {
|
||||
|
|
@ -722,7 +726,11 @@ static TGMediaLivePhotoMode TGMediaPickerGalleryResolvedLivePhotoMode(TGMediaEdi
|
|||
_paintingImageView.hidden = (_paintingImageView.image == nil) || _livePhotoVideoView.alpha <= FLT_EPSILON;
|
||||
_contentView.hidden = false;
|
||||
|
||||
_livePhotoMode = TGMediaPickerGalleryResolvedLivePhotoMode(self.item.editingContext, self.item.editableMediaItem, (TGMediaAsset *)self.item.asset);
|
||||
if ([self.item.asset isKindOfClass:[TGMediaAsset class]]) {
|
||||
_livePhotoMode = TGMediaPickerGalleryResolvedLivePhotoMode(self.item.editingContext, self.item.editableMediaItem, (TGMediaAsset *)self.item.asset);
|
||||
} else {
|
||||
_livePhotoMode = TGMediaLivePhotoModeOff;
|
||||
}
|
||||
[self updatePaintingImage:[self currentAdjustments]];
|
||||
[self layoutEditedSubviews];
|
||||
|
||||
|
|
|
|||
|
|
@ -110,13 +110,7 @@ public func saveToCameraRoll(context: AccountContext, postbox: Postbox, userLoca
|
|||
PHPhotoLibrary.shared().performChanges({
|
||||
if isImage {
|
||||
if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
PHAssetCreationRequest.forAsset().addResource(with: .photo, data: fileData, options: nil)
|
||||
} else {
|
||||
if let image = UIImage(data: fileData) {
|
||||
PHAssetChangeRequest.creationRequestForAsset(from: image)
|
||||
}
|
||||
}
|
||||
PHAssetCreationRequest.forAsset().addResource(with: .photo, data: fileData, options: nil)
|
||||
}
|
||||
} else {
|
||||
if let _ = try? FileManager.default.copyItem(atPath: data.path, toPath: tempVideoPath) {
|
||||
|
|
|
|||
|
|
@ -719,13 +719,21 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
|
|||
self.fetchDisposable.dispose()
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
var result = super.hitTest(point, with: event)
|
||||
if let mediaFrame = self.mediaFrame, mediaFrame.contains(point) {
|
||||
result = self.view
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.highlightedBackgroundNode.view.tag = 0x1bad
|
||||
self.separatorNode.view.tag = 0x3bad
|
||||
}
|
||||
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
guard !self.ignoreNextTap else {
|
||||
self.ignoreNextTap = false
|
||||
|
|
|
|||
|
|
@ -1419,10 +1419,10 @@ final class ComposePollScreenComponent: Component {
|
|||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let topInset: CGFloat = 24.0
|
||||
let topInset: CGFloat = 16.0
|
||||
let bottomInset: CGFloat = 8.0
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let sectionSpacing: CGFloat = 24.0
|
||||
let sectionSpacing: CGFloat = 30.0
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundColor = theme.list.blocksBackgroundColor
|
||||
|
|
@ -1900,6 +1900,7 @@ final class ComposePollScreenComponent: Component {
|
|||
)))
|
||||
], alignment: .left, spacing: 4.0)),
|
||||
verticalAlignment: .middle,
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
Image(image: self.cachedViewIcon, size: CGSize(width: 30.0, height: 30.0))
|
||||
)), false),
|
||||
|
|
@ -1939,6 +1940,7 @@ final class ComposePollScreenComponent: Component {
|
|||
)))
|
||||
], alignment: .left, spacing: 4.0)),
|
||||
verticalAlignment: .middle,
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
Image(image: self.cachedMultipleIcon, size: CGSize(width: 30.0, height: 30.0))
|
||||
)), false),
|
||||
|
|
@ -1958,7 +1960,7 @@ final class ComposePollScreenComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
self.state?.updated(transition: .spring(duration: 0.4).withUserData(MultilineTextComponent.CrossfadeTransition()))
|
||||
})),
|
||||
action: nil
|
||||
))))
|
||||
|
|
@ -1987,6 +1989,7 @@ final class ComposePollScreenComponent: Component {
|
|||
)))
|
||||
], alignment: .left, spacing: 4.0)),
|
||||
verticalAlignment: .middle,
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
Image(image: self.cachedAddIcon, size: CGSize(width: 30.0, height: 30.0))
|
||||
)), false),
|
||||
|
|
@ -2027,6 +2030,7 @@ final class ComposePollScreenComponent: Component {
|
|||
)))
|
||||
], alignment: .left, spacing: 4.0)),
|
||||
verticalAlignment: .middle,
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
Image(image: self.cachedRevoteIcon, size: CGSize(width: 30.0, height: 30.0))
|
||||
)), false),
|
||||
|
|
@ -2063,6 +2067,7 @@ final class ComposePollScreenComponent: Component {
|
|||
)))
|
||||
], alignment: .left, spacing: 4.0)),
|
||||
verticalAlignment: .middle,
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
Image(image: self.cachedShuffleIcon, size: CGSize(width: 30.0, height: 30.0))
|
||||
)), false),
|
||||
|
|
@ -2099,6 +2104,7 @@ final class ComposePollScreenComponent: Component {
|
|||
)))
|
||||
], alignment: .left, spacing: 4.0)),
|
||||
verticalAlignment: .middle,
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
Image(image: self.cachedQuizIcon, size: CGSize(width: 30.0, height: 30.0))
|
||||
)), false),
|
||||
|
|
@ -2141,6 +2147,7 @@ final class ComposePollScreenComponent: Component {
|
|||
)))
|
||||
], alignment: .left, spacing: 4.0)),
|
||||
verticalAlignment: .middle,
|
||||
contentInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0),
|
||||
leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(
|
||||
Image(image: self.cachedDurationIcon, size: CGSize(width: 30.0, height: 30.0))
|
||||
)), false),
|
||||
|
|
|
|||
|
|
@ -762,10 +762,10 @@ final class ComposeTodoScreenComponent: Component {
|
|||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let topInset: CGFloat = 24.0
|
||||
let topInset: CGFloat = 8.0
|
||||
let bottomInset: CGFloat = 8.0
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let sectionSpacing: CGFloat = 24.0
|
||||
let sectionSpacing: CGFloat = 30.0
|
||||
|
||||
if themeUpdated {
|
||||
self.backgroundColor = theme.list.blocksBackgroundColor
|
||||
|
|
|
|||
|
|
@ -496,17 +496,8 @@ public final class ListActionItemComponent: Component {
|
|||
contentRightInset = customAccessorySizeValue.width + customAccessory.insets.left + customAccessory.insets.right
|
||||
}
|
||||
|
||||
var contentInsets = component.contentInsets
|
||||
switch component.style {
|
||||
case .glass:
|
||||
if contentInsets.top == 12.0 && contentInsets.bottom == 12.0 {
|
||||
contentInsets.top = 16.0
|
||||
contentInsets.bottom = 16.0
|
||||
}
|
||||
case .legacy:
|
||||
break
|
||||
}
|
||||
|
||||
let contentInsets = component.contentInsets
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
contentHeight += contentInsets.top
|
||||
|
||||
|
|
@ -531,11 +522,15 @@ public final class ListActionItemComponent: Component {
|
|||
contentLeftInset = floor((availableSize.width - titleSize.width) / 2.0)
|
||||
}
|
||||
|
||||
let titleY = contentHeight
|
||||
contentHeight += titleSize.height
|
||||
|
||||
contentHeight += contentInsets.bottom
|
||||
|
||||
if case .glass = component.style, contentHeight < 52.0 {
|
||||
contentHeight = 52.0
|
||||
}
|
||||
|
||||
let titleY = floorToScreenPixels((contentHeight - titleSize.height) / 2.0)
|
||||
|
||||
if let iconValue = component.icon {
|
||||
if previousComponent?.icon?.component.id != iconValue.component.id, let icon = self.icon {
|
||||
self.icon = nil
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
import Foundation
|
||||
import AVFoundation
|
||||
import SwiftSignalKit
|
||||
import UniversalMediaPlayer
|
||||
import AccountContext
|
||||
import AVKit
|
||||
|
||||
public class ExternalVideoPlayer: NSObject, AVRoutePickerViewDelegate {
|
||||
private let context: AccountContext
|
||||
let content: NativeVideoContent
|
||||
|
||||
let player: AVPlayer?
|
||||
private var didPlayToEndTimeObserver: NSObjectProtocol?
|
||||
private var timeObserver: Any?
|
||||
|
||||
private var statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: false, progress: 0.0, display: true), soundEnabled: true)
|
||||
private let _status = ValuePromise<MediaPlayerStatus>()
|
||||
var status: Signal<MediaPlayerStatus, NoError> {
|
||||
return self._status.get()
|
||||
}
|
||||
private var seekId: Int = 0
|
||||
|
||||
private weak var routePickerView: UIView?
|
||||
|
||||
public var isActiveUpdated: (Bool) -> Void = { _ in }
|
||||
|
||||
public init(context: AccountContext, content: NativeVideoContent) {
|
||||
self.context = context
|
||||
self.content = content
|
||||
|
||||
if let path = context.account.postbox.mediaBox.completedResourcePath(content.fileReference.media.resource, pathExtension: "mp4") {
|
||||
let player = AVPlayer(url: URL(fileURLWithPath: path))
|
||||
self.player = player
|
||||
} else {
|
||||
self.player = nil
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
self.startObservingForAirPlayStatusChanges()
|
||||
self.isActiveUpdated(self.player?.isExternalPlaybackActive ?? false)
|
||||
|
||||
if let player = self.player {
|
||||
self.didPlayToEndTimeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: nil, using: { [weak self] notification in
|
||||
if let strongSelf = self {
|
||||
strongSelf.player?.seek(to: CMTime(seconds: 0.0, preferredTimescale: 30))
|
||||
strongSelf.play()
|
||||
}
|
||||
})
|
||||
|
||||
self.timeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 10), queue: DispatchQueue.main) { [weak self] time in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: strongSelf.statusValue.duration, dimensions: CGSize(), timestamp: CMTimeGetSeconds(time), baseRate: 1.0, seekId: strongSelf.seekId, status: strongSelf.statusValue.status, soundEnabled: true)
|
||||
strongSelf._status.set(strongSelf.statusValue)
|
||||
}
|
||||
}
|
||||
|
||||
self._status.set(self.statusValue)
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let timeObserver = self.timeObserver {
|
||||
self.player?.removeTimeObserver(timeObserver)
|
||||
}
|
||||
|
||||
if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver {
|
||||
NotificationCenter.default.removeObserver(didPlayToEndTimeObserver)
|
||||
}
|
||||
|
||||
self.stopObservingForAirPlayStatusChanges()
|
||||
}
|
||||
|
||||
public func play() {
|
||||
self.player?.play()
|
||||
}
|
||||
|
||||
public func openRouteSelection() {
|
||||
if #available(iOS 11.0, *) {
|
||||
let routePickerView = AVRoutePickerView()
|
||||
routePickerView.delegate = self
|
||||
if #available(iOS 13.0, *) {
|
||||
routePickerView.prioritizesVideoDevices = true
|
||||
}
|
||||
self.context.sharedContext.mainWindow?.viewController?.view.addSubview(routePickerView)
|
||||
|
||||
if let routePickerButton = routePickerView.subviews.first(where: { $0 is UIButton }) as? UIButton {
|
||||
routePickerButton.sendActions(for: .touchUpInside)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
public func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) {
|
||||
routePickerView.removeFromSuperview()
|
||||
|
||||
self.play()
|
||||
}
|
||||
|
||||
private var observerContextAirplay = 1
|
||||
|
||||
func startObservingForAirPlayStatusChanges()
|
||||
{
|
||||
self.player?.addObserver(self, forKeyPath: #keyPath(AVPlayer.isExternalPlaybackActive), options: .new, context: &observerContextAirplay)
|
||||
}
|
||||
|
||||
func stopObservingForAirPlayStatusChanges()
|
||||
{
|
||||
self.player?.removeObserver(self, forKeyPath: #keyPath(AVPlayer.isExternalPlaybackActive))
|
||||
}
|
||||
|
||||
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if context == &observerContextAirplay {
|
||||
self.isActiveUpdated(self.player?.isExternalPlaybackActive ?? false)
|
||||
}
|
||||
else {
|
||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue