From 5b45224f7bfabefb1ea564fc8ed5e48cbc083771 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 15 Mar 2021 17:53:06 +0800 Subject: [PATCH] feat: make media indicator view hide when playing video --- Mastodon.xcodeproj/project.pbxproj | 4 + .../Diffiable/Section/StatusSection.swift | 17 ++++ ...ContainerView+MediaTypeIndicotorView.swift | 93 ++++++++++++++++++- .../View/Container/PlayerContainerView.swift | 79 ++++++---------- 4 files changed, 139 insertions(+), 54 deletions(-) diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index d3bea6e88..9859676c1 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -143,6 +143,7 @@ DB45FB0F25CA87D0005A8AC7 /* AuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FB0E25CA87D0005A8AC7 /* AuthenticationService.swift */; }; DB45FB1D25CA9D23005A8AC7 /* APIService+HomeTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FB1C25CA9D23005A8AC7 /* APIService+HomeTimeline.swift */; }; DB47229725F9EFAD00DA7F53 /* NSManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB47229625F9EFAD00DA7F53 /* NSManagedObjectContext.swift */; }; + DB49A63D25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB49A63C25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift */; }; DB5086A525CC0B7000C2C187 /* AvatarBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086A425CC0B7000C2C187 /* AvatarBarButtonItem.swift */; }; DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */; }; DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DB5086B725CC0D6400C2C187 /* Kingfisher */; }; @@ -400,6 +401,7 @@ DB45FB0E25CA87D0005A8AC7 /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = ""; }; DB45FB1C25CA9D23005A8AC7 /* APIService+HomeTimeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+HomeTimeline.swift"; sourceTree = ""; }; DB47229625F9EFAD00DA7F53 /* NSManagedObjectContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSManagedObjectContext.swift; sourceTree = ""; }; + DB49A63C25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlayerContainerView+MediaTypeIndicotorView.swift"; sourceTree = ""; }; DB5086A425CC0B7000C2C187 /* AvatarBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarBarButtonItem.swift; sourceTree = ""; }; DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarConfigurableView.swift; sourceTree = ""; }; DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashPreference.swift; sourceTree = ""; }; @@ -1190,6 +1192,7 @@ DB9D6C0D25E4F9780051B173 /* MosaicImageViewContainer.swift */, 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */, 5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */, + DB49A63C25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift */, 5DF1057E25F88A4100D6C0D4 /* TouchBlockingView.swift */, ); path = Container; @@ -1675,6 +1678,7 @@ DB45FADD25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift in Sources */, 2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */, 2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */, + DB49A63D25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift in Sources */, DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */, DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */, 2D206B8625F5FB0900143C56 /* Double.swift in Sources */, diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 8f89cdd3f..fad4898d5 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -222,6 +222,23 @@ extension StatusSection { playerViewController.player = videoPlayerViewModel.player playerViewController.showsPlaybackControls = videoPlayerViewModel.videoKind != .gif playerContainerView.setMediaKind(kind: videoPlayerViewModel.videoKind) + if videoPlayerViewModel.videoKind == .gif { + playerContainerView.setMediaIndicator(isHidden: false) + } else { + videoPlayerViewModel.timeControlStatus.sink { timeControlStatus in + UIView.animate(withDuration: 0.33) { + switch timeControlStatus { + case .playing: + playerContainerView.setMediaIndicator(isHidden: true) + case .paused, .waitingToPlayAtSpecifiedRate: + playerContainerView.setMediaIndicator(isHidden: false) + @unknown default: + assertionFailure() + } + } + } + .store(in: &cell.disposeBag) + } playerContainerView.isHidden = false } else { diff --git a/Mastodon/Scene/Share/View/Container/PlayerContainerView+MediaTypeIndicotorView.swift b/Mastodon/Scene/Share/View/Container/PlayerContainerView+MediaTypeIndicotorView.swift index 0accc40b6..3bff6ef75 100644 --- a/Mastodon/Scene/Share/View/Container/PlayerContainerView+MediaTypeIndicotorView.swift +++ b/Mastodon/Scene/Share/View/Container/PlayerContainerView+MediaTypeIndicotorView.swift @@ -5,4 +5,95 @@ // Created by MainasuK Cirno on 2021-3-15. // -import Foundation +import UIKit + +extension PlayerContainerView { + + final class MediaTypeIndicotorView: UIView { + + static let indicatorViewSize = CGSize(width: 47, height: 25) + + let maskLayer = CAShapeLayer() + + let label: UILabel = { + let label = UILabel() + label.textColor = .white + label.textAlignment = .right + label.adjustsFontSizeToFitWidth = true + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + + override func layoutSubviews() { + super.layoutSubviews() + + let path = UIBezierPath() + path.move(to: CGPoint(x: bounds.width, y: bounds.height)) + path.addLine(to: CGPoint(x: bounds.width, y: 0)) + path.addLine(to: CGPoint(x: bounds.width * 0.5, y: 0)) + path.addCurve( + to: CGPoint(x: 0, y: bounds.height), + controlPoint1: CGPoint(x: bounds.width * 0.2, y: 0), + controlPoint2: CGPoint(x: 0, y: bounds.height * 0.3) + ) + path.close() + + maskLayer.frame = bounds + maskLayer.path = path.cgPath + layer.mask = maskLayer + } + } + +} + +extension PlayerContainerView.MediaTypeIndicotorView { + + private func _init() { + backgroundColor = Asset.Colors.Background.mediaTypeIndicotor.color + layoutMargins = UIEdgeInsets(top: 3, left: 13, bottom: 0, right: 6) + + addSubview(label) + NSLayoutConstraint.activate([ + label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor), + label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor), + label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor), + label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor), + ]) + } + + private static func roundedFont(weight: UIFont.Weight,fontSize: CGFloat) -> UIFont { + let systemFont = UIFont.systemFont(ofSize: fontSize, weight: weight) + guard let descriptor = systemFont.fontDescriptor.withDesign(.rounded) else { return systemFont } + let roundedFont = UIFont(descriptor: descriptor, size: fontSize) + return roundedFont + } + + func setMediaKind(kind: VideoPlayerViewModel.Kind) { + let fontSize: CGFloat = 18 + + switch kind { + case .gif: + label.font = PlayerContainerView.MediaTypeIndicotorView.roundedFont(weight: .heavy, fontSize: fontSize) + label.text = "GIF" + case .video: + let configuration = UIImage.SymbolConfiguration(font: PlayerContainerView.MediaTypeIndicotorView.roundedFont(weight: .regular, fontSize: fontSize)) + let image = UIImage(systemName: "video.fill", withConfiguration: configuration)! + let attachment = NSTextAttachment() + attachment.image = image.withTintColor(.white) + label.attributedText = NSAttributedString(attachment: attachment) + } + } + +} + + diff --git a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift index 3b4acc98c..cabed9560 100644 --- a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift +++ b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift @@ -26,26 +26,8 @@ final class PlayerContainerView: UIView { let playerViewController = AVPlayerViewController() - let mediaTypeIndicotorLabel: UILabel = { - let label = UILabel() - label.textColor = .white - label.textAlignment = .right - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - let mediaTypeIndicotorView: UIView = { - let view = UIView() - view.backgroundColor = Asset.Colors.Background.mediaTypeIndicotor.color - view.translatesAutoresizingMaskIntoConstraints = false - let rect = CGRect(x: 0, y: 0, width: 47, height: 50) - let path = UIBezierPath(roundedRect: rect, byRoundingCorners: [.topLeft], cornerRadii: CGSize(width: 50, height: 50)) - let maskLayer = CAShapeLayer() - maskLayer.frame = rect - maskLayer.path = path.cgPath - view.layer.mask = maskLayer - return view - }() + let mediaTypeIndicotorView = MediaTypeIndicotorView() + let mediaTypeIndicotorViewInContentWarningOverlay = MediaTypeIndicotorView() weak var delegate: PlayerContainerViewDelegate? @@ -78,6 +60,16 @@ extension PlayerContainerView { playerViewController.view.layer.cornerRadius = PlayerContainerView.cornerRadius playerViewController.view.layer.cornerCurve = .continuous + // mediaType + mediaTypeIndicotorView.translatesAutoresizingMaskIntoConstraints = false + playerViewController.contentOverlayView!.addSubview(mediaTypeIndicotorView) + NSLayoutConstraint.activate([ + mediaTypeIndicotorView.bottomAnchor.constraint(equalTo: playerViewController.contentOverlayView!.bottomAnchor), + mediaTypeIndicotorView.trailingAnchor.constraint(equalTo: playerViewController.contentOverlayView!.trailingAnchor), + mediaTypeIndicotorView.heightAnchor.constraint(equalToConstant: 25).priority(.defaultHigh), + mediaTypeIndicotorView.widthAnchor.constraint(equalToConstant: 47).priority(.defaultHigh), + ]) + addSubview(contentWarningOverlayView) NSLayoutConstraint.activate([ contentWarningOverlayView.topAnchor.constraint(equalTo: topAnchor), @@ -87,21 +79,13 @@ extension PlayerContainerView { ]) contentWarningOverlayView.delegate = self - // mediaType - addSubview(mediaTypeIndicotorView) + mediaTypeIndicotorViewInContentWarningOverlay.translatesAutoresizingMaskIntoConstraints = false + contentWarningOverlayView.addSubview(mediaTypeIndicotorViewInContentWarningOverlay) NSLayoutConstraint.activate([ - mediaTypeIndicotorView.bottomAnchor.constraint(equalTo: bottomAnchor), - mediaTypeIndicotorView.trailingAnchor.constraint(equalTo: trailingAnchor), - mediaTypeIndicotorView.heightAnchor.constraint(equalToConstant: 25), - mediaTypeIndicotorView.widthAnchor.constraint(equalToConstant: 47) - ]) - - mediaTypeIndicotorView.addSubview(mediaTypeIndicotorLabel) - NSLayoutConstraint.activate([ - mediaTypeIndicotorLabel.topAnchor.constraint(equalTo: mediaTypeIndicotorView.topAnchor), - mediaTypeIndicotorLabel.leadingAnchor.constraint(equalTo: mediaTypeIndicotorView.leadingAnchor), - mediaTypeIndicotorLabel.bottomAnchor.constraint(equalTo: mediaTypeIndicotorView.bottomAnchor), - mediaTypeIndicotorView.trailingAnchor.constraint(equalTo: mediaTypeIndicotorLabel.trailingAnchor, constant: 8) + mediaTypeIndicotorViewInContentWarningOverlay.bottomAnchor.constraint(equalTo: contentWarningOverlayView.bottomAnchor), + mediaTypeIndicotorViewInContentWarningOverlay.trailingAnchor.constraint(equalTo: contentWarningOverlayView.trailingAnchor), + mediaTypeIndicotorViewInContentWarningOverlay.heightAnchor.constraint(equalToConstant: 25).priority(.defaultHigh), + mediaTypeIndicotorViewInContentWarningOverlay.widthAnchor.constraint(equalToConstant: 47).priority(.defaultHigh), ]) } } @@ -161,25 +145,14 @@ extension PlayerContainerView { return playerViewController } - func roundedFont(weight: UIFont.Weight,fontSize: CGFloat) -> UIFont { - let systemFont = UIFont.systemFont(ofSize: fontSize, weight: weight) - guard let descriptor = systemFont.fontDescriptor.withDesign(.rounded) else { return systemFont } - let roundedFont = UIFont(descriptor: descriptor, size: fontSize) - return roundedFont - } func setMediaKind(kind: VideoPlayerViewModel.Kind) { - let fontSize: CGFloat = 18 - - switch kind { - case .gif: - mediaTypeIndicotorLabel.font = roundedFont(weight: .heavy, fontSize: fontSize) - mediaTypeIndicotorLabel.text = "GIF" - case .video: - let configuration = UIImage.SymbolConfiguration(font: roundedFont(weight: .regular, fontSize: fontSize)) - let image = UIImage(systemName: "video.fill", withConfiguration: configuration)! - let attachment = NSTextAttachment() - attachment.image = image.withTintColor(.white) - mediaTypeIndicotorLabel.attributedText = NSAttributedString(attachment: attachment) - } + mediaTypeIndicotorView.setMediaKind(kind: kind) + mediaTypeIndicotorViewInContentWarningOverlay.setMediaKind(kind: kind) } + + func setMediaIndicator(isHidden: Bool) { + mediaTypeIndicotorView.alpha = isHidden ? 0 : 1 + mediaTypeIndicotorViewInContentWarningOverlay.alpha = isHidden ? 0 : 1 + } + }