From 2657dde18496981d0ec3b6344e238aeb1d3d03c9 Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Wed, 10 Mar 2021 21:19:56 +0800 Subject: [PATCH 01/11] chore: the play interrupts event could be sent with the notification --- Mastodon/Diffiable/Section/StatusSection.swift | 14 +++++++++++--- .../HomeTimelineViewController.swift | 7 +++++-- .../PublicTimelineViewController.swift | 10 +++++++++- .../ViewModel/AudioContainerViewModel.swift | 6 +----- .../Share/ViewModel/VideoPlayerViewModel.swift | 3 ++- Mastodon/Service/AudioPlayer.swift | 16 +++++++++++++++- Mastodon/Service/ViedeoPlaybackService.swift | 7 +++++++ 7 files changed, 50 insertions(+), 13 deletions(-) diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 1d0169ab..53f7aec8 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -37,7 +37,11 @@ extension StatusSection { StatusSection.configure( cell: cell, dependency: dependency, - readableLayoutFrame: tableView.readableContentGuide.layoutFrame, timestampUpdatePublisher: timestampUpdatePublisher, toot: timelineIndex.toot, requestUserID: timelineIndex.userID, statusItemAttribute: attribute + readableLayoutFrame: tableView.readableContentGuide.layoutFrame, + timestampUpdatePublisher: timestampUpdatePublisher, + toot: timelineIndex.toot, + requestUserID: timelineIndex.userID, + statusItemAttribute: attribute ) } cell.delegate = statusTableViewCellDelegate @@ -52,7 +56,11 @@ extension StatusSection { StatusSection.configure( cell: cell, dependency: dependency, - readableLayoutFrame: tableView.readableContentGuide.layoutFrame, timestampUpdatePublisher: timestampUpdatePublisher, toot: toot, requestUserID: requestUserID, statusItemAttribute: attribute + readableLayoutFrame: tableView.readableContentGuide.layoutFrame, + timestampUpdatePublisher: timestampUpdatePublisher, + toot: toot, + requestUserID: requestUserID, + statusItemAttribute: attribute ) } cell.delegate = statusTableViewCellDelegate @@ -168,7 +176,7 @@ extension StatusSection { // set audio if let audioAttachment = mediaAttachments.filter({ $0.type == .audio }).first { cell.statusView.audioView.isHidden = false - AudioContainerViewModel.configure(cell: cell, audioAttachment: audioAttachment, videoPlaybackService: dependency.context.videoPlaybackService) + AudioContainerViewModel.configure(cell: cell, audioAttachment: audioAttachment ) } else { cell.statusView.audioView.isHidden = true } diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift index c9498dbe..01b860da 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift @@ -141,7 +141,7 @@ extension HomeTimelineViewController { override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) - + context.videoPlaybackService.viewDidDisappear(from: self) } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -236,7 +236,10 @@ extension HomeTimelineViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { handleTableView(tableView, willDisplay: cell, forRowAt: indexPath) } - + + func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { + handleTableView(tableView, didEndDisplaying: cell, forRowAt: indexPath) + } } // MARK: - ContentOffsetAdjustableTimelineViewControllerDelegate diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift index 50d36d29..820094b1 100644 --- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift +++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift @@ -81,6 +81,10 @@ extension PublicTimelineViewController { ) } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + context.videoPlaybackService.viewDidDisappear(from: self) + } } // MARK: - UIScrollViewDelegate @@ -103,6 +107,7 @@ extension PublicTimelineViewController { // MARK: - UITableViewDelegate extension PublicTimelineViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + guard let diffableDataSource = viewModel.diffableDataSource else { return 100 } guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return 100 } @@ -114,8 +119,11 @@ extension PublicTimelineViewController: UITableViewDelegate { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {} - + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + handleTableView(tableView, willDisplay: cell, forRowAt: indexPath) + } func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { + handleTableView(tableView, didEndDisplaying: cell, forRowAt: indexPath) guard let diffableDataSource = viewModel.diffableDataSource else { return } guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return } diff --git a/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift b/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift index 7370c4fe..de804a47 100644 --- a/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift +++ b/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift @@ -12,8 +12,7 @@ import UIKit class AudioContainerViewModel { static func configure( cell: StatusTableViewCell, - audioAttachment: Attachment, - videoPlaybackService: VideoPlaybackService + audioAttachment: Attachment ) { guard let duration = audioAttachment.meta?.original?.duration else { return } let audioView = cell.statusView.audioView @@ -26,15 +25,12 @@ class AudioContainerViewModel { AudioPlayer.shared.pause() } else { AudioPlayer.shared.resume() - videoPlaybackService.pauseWhenPlayAudio() } if AudioPlayer.shared.currentTimeSubject.value == 0 { AudioPlayer.shared.playAudio(audioAttachment: audioAttachment) - videoPlaybackService.pauseWhenPlayAudio() } } else { AudioPlayer.shared.playAudio(audioAttachment: audioAttachment) - videoPlaybackService.pauseWhenPlayAudio() } } .store(in: &cell.disposeBag) diff --git a/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift b/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift index e0a2f5ef..c3f2cf36 100644 --- a/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift +++ b/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift @@ -14,6 +14,7 @@ import UIKit final class VideoPlayerViewModel { var disposeBag = Set() + static let appWillPlayVideoNotification = NSNotification.Name(rawValue: "appWillPlayVideoNotification") // input let previewImageURL: URL? let videoURL: URL @@ -63,7 +64,7 @@ final class VideoPlayerViewModel { .sink { [weak self] timeControlStatus in guard let _ = self else { return } guard timeControlStatus == .playing else { return } - AudioPlayer.shared.pauseIfNeed() + NotificationCenter.default.post(name: VideoPlayerViewModel.appWillPlayVideoNotification, object: nil) switch videoKind { case .gif: break diff --git a/Mastodon/Service/AudioPlayer.swift b/Mastodon/Service/AudioPlayer.swift index 02c948c2..646207ed 100644 --- a/Mastodon/Service/AudioPlayer.swift +++ b/Mastodon/Service/AudioPlayer.swift @@ -12,6 +12,9 @@ import Foundation import UIKit final class AudioPlayer: NSObject { + + static let appWillPlayAudioNotification = NSNotification.Name(rawValue: "appWillPlayAudioNotification") + var disposeBag = Set() var player = AVPlayer() @@ -45,6 +48,7 @@ extension AudioPlayer { return } + pushWillPlayAudioNotification() if audioAttachment == attachment { if self.playbackState.value == .stopped { self.seekToTime(time: .zero) @@ -83,6 +87,12 @@ extension AudioPlayer { } } .store(in: &disposeBag) + NotificationCenter.default.publisher(for: VideoPlayerViewModel.appWillPlayVideoNotification) + .sink { [weak self] _ in + guard let self = self else { return } + self.pauseIfNeed() + } + .store(in: &disposeBag) timeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main, using: { [weak self] time in guard let self = self else { return } @@ -119,10 +129,14 @@ extension AudioPlayer { .store(in: &disposeBag) } + func pushWillPlayAudioNotification() { + NotificationCenter.default.post(name: AudioPlayer.appWillPlayAudioNotification, object: nil) + } func isPlaying() -> Bool { - return self.playbackState.value == .readyToPlay || self.playbackState.value == .playing + return playbackState.value == .readyToPlay || playbackState.value == .playing } func resume() { + pushWillPlayAudioNotification() player.play() playbackState.value = .playing } diff --git a/Mastodon/Service/ViedeoPlaybackService.swift b/Mastodon/Service/ViedeoPlaybackService.swift index 724026fd..49ac3b0d 100644 --- a/Mastodon/Service/ViedeoPlaybackService.swift +++ b/Mastodon/Service/ViedeoPlaybackService.swift @@ -90,6 +90,13 @@ extension VideoPlaybackService { self.playerViewModel(viewModel, didUpdateTimeControlStatus: timeControlStatus) } .store(in: &disposeBag) + + NotificationCenter.default.publisher(for: AudioPlayer.appWillPlayAudioNotification) + .sink { [weak self] _ in + guard let self = self else { return } + self.pauseWhenPlayAudio() + } + .store(in: &disposeBag) } } From 6c0a767435ee0aa3bac7f2b6bd830a1503f57e7a Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Thu, 11 Mar 2021 13:11:13 +0800 Subject: [PATCH 02/11] chore: auto-pause when audio cell disappeared --- Mastodon.xcodeproj/project.pbxproj | 8 ++-- .../Diffiable/Section/StatusSection.swift | 2 +- .../StatusProvider+UITableViewDelegate.swift | 10 +++-- .../HomeTimelineViewController.swift | 1 + .../PublicTimelineViewController.swift | 2 +- .../ViewModel/AudioContainerViewModel.swift | 42 ++++++++++--------- .../ViewModel/VideoPlayerViewModel.swift | 2 +- ...layer.swift => AudioPlaybackService.swift} | 27 +++++++----- Mastodon/Service/ViedeoPlaybackService.swift | 2 +- Mastodon/State/AppContext.swift | 1 + 10 files changed, 56 insertions(+), 41 deletions(-) rename Mastodon/Service/{AudioPlayer.swift => AudioPlaybackService.swift} (87%) diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 69f43190..98bfe207 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -25,7 +25,7 @@ 2D206B7225F5D27F00143C56 /* AudioContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */; }; 2D206B8025F5F45E00143C56 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B7F25F5F45E00143C56 /* UIImage.swift */; }; 2D206B8625F5FB0900143C56 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B8525F5FB0900143C56 /* Double.swift */; }; - 2D206B8C25F6015000143C56 /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B8B25F6015000143C56 /* AudioPlayer.swift */; }; + 2D206B8C25F6015000143C56 /* AudioPlaybackService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B8B25F6015000143C56 /* AudioPlaybackService.swift */; }; 2D206B9225F60EA700143C56 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B9125F60EA700143C56 /* UIControl.swift */; }; 2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */; }; 2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAB925CB9B0500C9ED86 /* UIView.swift */; }; @@ -269,7 +269,7 @@ 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioContainerView.swift; sourceTree = ""; }; 2D206B7F25F5F45E00143C56 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; 2D206B8525F5FB0900143C56 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = ""; }; - 2D206B8B25F6015000143C56 /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; + 2D206B8B25F6015000143C56 /* AudioPlaybackService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlaybackService.swift; sourceTree = ""; }; 2D206B9125F60EA700143C56 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMiddleLoaderTableViewCell.swift; sourceTree = ""; }; 2D32EAB925CB9B0500C9ED86 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; @@ -659,7 +659,7 @@ DB45FB0425CA87B4005A8AC7 /* APIService */, DB45FB0E25CA87D0005A8AC7 /* AuthenticationService.swift */, DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */, - 2D206B8B25F6015000143C56 /* AudioPlayer.swift */, + 2D206B8B25F6015000143C56 /* AudioPlaybackService.swift */, 2DA6054625F716A2006356F9 /* PlaybackState.swift */, 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */, ); @@ -1569,7 +1569,7 @@ DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */, 5DF1057F25F88A4100D6C0D4 /* TouchBlockingView.swift in Sources */, 0FAA0FDF25E0B57E0017CCDE /* WelcomeViewController.swift in Sources */, - 2D206B8C25F6015000143C56 /* AudioPlayer.swift in Sources */, + 2D206B8C25F6015000143C56 /* AudioPlaybackService.swift in Sources */, 2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */, DB45FB1D25CA9D23005A8AC7 /* APIService+HomeTimeline.swift in Sources */, 2D7631B325C159F700929FB9 /* Item.swift in Sources */, diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 53f7aec8..daaa9852 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -176,7 +176,7 @@ extension StatusSection { // set audio if let audioAttachment = mediaAttachments.filter({ $0.type == .audio }).first { cell.statusView.audioView.isHidden = false - AudioContainerViewModel.configure(cell: cell, audioAttachment: audioAttachment ) + AudioContainerViewModel.configure(cell: cell, audioAttachment: audioAttachment, audioService: dependency.context.audioPlaybackService) } else { cell.statusView.audioView.isHidden = true } diff --git a/Mastodon/Protocol/StatusProvider/StatusProvider+UITableViewDelegate.swift b/Mastodon/Protocol/StatusProvider/StatusProvider+UITableViewDelegate.swift index c68ff6e7..1157b799 100644 --- a/Mastodon/Protocol/StatusProvider/StatusProvider+UITableViewDelegate.swift +++ b/Mastodon/Protocol/StatusProvider/StatusProvider+UITableViewDelegate.swift @@ -87,10 +87,14 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { .sink { [weak self] toot in guard let self = self else { return } guard let media = (toot?.mediaAttachments ?? Set()).first else { return } - guard let videoPlayerViewModel = self.context.videoPlaybackService.dequeueVideoPlayerViewModel(for: media) else { return } - DispatchQueue.main.async { - videoPlayerViewModel.didEndDisplaying() + if let videoPlayerViewModel = self.context.videoPlaybackService.dequeueVideoPlayerViewModel(for: media) { + DispatchQueue.main.async { + videoPlayerViewModel.didEndDisplaying() + } + } + if let currentAudioAttachment = self.context.audioPlaybackService.attachment, let _ = toot?.mediaAttachments?.contains(currentAudioAttachment) { + self.context.audioPlaybackService.pause() } } .store(in: &disposeBag) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift index 01b860da..9db551f6 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift @@ -142,6 +142,7 @@ extension HomeTimelineViewController { override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) context.videoPlaybackService.viewDidDisappear(from: self) + context.audioPlaybackService.viewDidDisappear(from: self) } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift index 820094b1..1fc0978e 100644 --- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift +++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift @@ -84,6 +84,7 @@ extension PublicTimelineViewController { override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) context.videoPlaybackService.viewDidDisappear(from: self) + context.audioPlaybackService.viewDidDisappear(from: self) } } @@ -107,7 +108,6 @@ extension PublicTimelineViewController { // MARK: - UITableViewDelegate extension PublicTimelineViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - guard let diffableDataSource = viewModel.diffableDataSource else { return 100 } guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return 100 } diff --git a/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift b/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift index de804a47..56bf0cbc 100644 --- a/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift +++ b/Mastodon/Scene/Share/ViewModel/AudioContainerViewModel.swift @@ -12,54 +12,58 @@ import UIKit class AudioContainerViewModel { static func configure( cell: StatusTableViewCell, - audioAttachment: Attachment + audioAttachment: Attachment, + audioService: AudioPlaybackService ) { guard let duration = audioAttachment.meta?.original?.duration else { return } let audioView = cell.statusView.audioView audioView.timeLabel.text = duration.asString(style: .positional) audioView.playButton.publisher(for: .touchUpInside) - .sink { _ in - if audioAttachment === AudioPlayer.shared.attachment { - if AudioPlayer.shared.isPlaying() { - AudioPlayer.shared.pause() + .sink { [weak audioService] _ in + guard let audioService = audioService else { return } + if audioAttachment === audioService.attachment { + if audioService.isPlaying() { + audioService.pause() } else { - AudioPlayer.shared.resume() + audioService.resume() } - if AudioPlayer.shared.currentTimeSubject.value == 0 { - AudioPlayer.shared.playAudio(audioAttachment: audioAttachment) + if audioService.currentTimeSubject.value == 0 { + audioService.playAudio(audioAttachment: audioAttachment) } } else { - AudioPlayer.shared.playAudio(audioAttachment: audioAttachment) + audioService.playAudio(audioAttachment: audioAttachment) } } .store(in: &cell.disposeBag) audioView.slider.publisher(for: .valueChanged) - .sink { slider in + .sink { [weak audioService] slider in + guard let audioService = audioService else { return } let slider = slider as! UISlider let time = Double(slider.value) * duration - AudioPlayer.shared.seekToTime(time: time) + audioService.seekToTime(time: time) } .store(in: &cell.disposeBag) - observePlayer(cell: cell, audioAttachment: audioAttachment) - if audioAttachment != AudioPlayer.shared.attachment { + observePlayer(cell: cell, audioAttachment: audioAttachment, audioService: audioService) + if audioAttachment != audioService.attachment { configureAudioView(audioView: audioView, audioAttachment: audioAttachment, playbackState: .stopped) } } static func observePlayer( cell: StatusTableViewCell, - audioAttachment: Attachment + audioAttachment: Attachment, + audioService: AudioPlaybackService ) { let audioView = cell.statusView.audioView var lastCurrentTimeSubject: TimeInterval? - AudioPlayer.shared.currentTimeSubject + audioService.currentTimeSubject .throttle(for: 0.33, scheduler: DispatchQueue.main, latest: true) - .compactMap { time -> (TimeInterval, Float)? in + .compactMap { [weak audioService] time -> (TimeInterval, Float)? in defer { lastCurrentTimeSubject = time } - guard audioAttachment === AudioPlayer.shared.attachment else { return nil } + guard audioAttachment === audioService?.attachment else { return nil } guard let duration = audioAttachment.meta?.original?.duration else { return nil } if let lastCurrentTimeSubject = lastCurrentTimeSubject, time != 0.0 { @@ -74,10 +78,10 @@ class AudioContainerViewModel { audioView.slider.setValue(progress, animated: true) }) .store(in: &cell.disposeBag) - AudioPlayer.shared.playbackState + audioService.playbackState .receive(on: DispatchQueue.main) .sink(receiveValue: { playbackState in - if audioAttachment === AudioPlayer.shared.attachment { + if audioAttachment === audioService.attachment { configureAudioView(audioView: audioView, audioAttachment: audioAttachment, playbackState: playbackState) } else { configureAudioView(audioView: audioView, audioAttachment: audioAttachment, playbackState: .stopped) diff --git a/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift b/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift index c3f2cf36..3fa24148 100644 --- a/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift +++ b/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift @@ -14,7 +14,7 @@ import UIKit final class VideoPlayerViewModel { var disposeBag = Set() - static let appWillPlayVideoNotification = NSNotification.Name(rawValue: "appWillPlayVideoNotification") + static let appWillPlayVideoNotification = NSNotification.Name(rawValue: "org.joinmastodon.Mastodon.VideoPlayerViewModel.appWillPlayVideo") // input let previewImageURL: URL? let videoURL: URL diff --git a/Mastodon/Service/AudioPlayer.swift b/Mastodon/Service/AudioPlaybackService.swift similarity index 87% rename from Mastodon/Service/AudioPlayer.swift rename to Mastodon/Service/AudioPlaybackService.swift index 646207ed..314f3964 100644 --- a/Mastodon/Service/AudioPlayer.swift +++ b/Mastodon/Service/AudioPlaybackService.swift @@ -10,10 +10,11 @@ import Combine import CoreDataStack import Foundation import UIKit +import os.log -final class AudioPlayer: NSObject { +final class AudioPlaybackService: NSObject { - static let appWillPlayAudioNotification = NSNotification.Name(rawValue: "appWillPlayAudioNotification") + static let appWillPlayAudioNotification = NSNotification.Name(rawValue: "org.joinmastodon.Mastodon.AudioPlayer.appWillPlayAudio") var disposeBag = Set() @@ -24,19 +25,16 @@ final class AudioPlayer: NSObject { let session = AVAudioSession.sharedInstance() let playbackState = CurrentValueSubject(PlaybackState.unknown) - - // MARK: - singleton - public static let shared = AudioPlayer() let currentTimeSubject = CurrentValueSubject(0) - private override init() { + override init() { super.init() addObserver() } } -extension AudioPlayer { +extension AudioPlaybackService { func playAudio(audioAttachment: Attachment) { guard let url = URL(string: audioAttachment.url) else { return @@ -48,7 +46,7 @@ extension AudioPlayer { return } - pushWillPlayAudioNotification() + notifyWillPlayAudioNotification() if audioAttachment == attachment { if self.playbackState.value == .stopped { self.seekToTime(time: .zero) @@ -129,14 +127,14 @@ extension AudioPlayer { .store(in: &disposeBag) } - func pushWillPlayAudioNotification() { - NotificationCenter.default.post(name: AudioPlayer.appWillPlayAudioNotification, object: nil) + func notifyWillPlayAudioNotification() { + NotificationCenter.default.post(name: AudioPlaybackService.appWillPlayAudioNotification, object: nil) } func isPlaying() -> Bool { return playbackState.value == .readyToPlay || playbackState.value == .playing } func resume() { - pushWillPlayAudioNotification() + notifyWillPlayAudioNotification() player.play() playbackState.value = .playing } @@ -154,3 +152,10 @@ extension AudioPlayer { player.seek(to: CMTimeMake(value:Int64(time), timescale: 1)) } } + +extension AudioPlaybackService { + func viewDidDisappear(from viewController: UIViewController?) { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", (#file as NSString).lastPathComponent, #line, #function) + pause() + } +} diff --git a/Mastodon/Service/ViedeoPlaybackService.swift b/Mastodon/Service/ViedeoPlaybackService.swift index 49ac3b0d..24ea6e6c 100644 --- a/Mastodon/Service/ViedeoPlaybackService.swift +++ b/Mastodon/Service/ViedeoPlaybackService.swift @@ -91,7 +91,7 @@ extension VideoPlaybackService { } .store(in: &disposeBag) - NotificationCenter.default.publisher(for: AudioPlayer.appWillPlayAudioNotification) + NotificationCenter.default.publisher(for: AudioPlaybackService.appWillPlayAudioNotification) .sink { [weak self] _ in guard let self = self else { return } self.pauseWhenPlayAudio() diff --git a/Mastodon/State/AppContext.swift b/Mastodon/State/AppContext.swift index 30069ec3..c5330fc0 100644 --- a/Mastodon/State/AppContext.swift +++ b/Mastodon/State/AppContext.swift @@ -28,6 +28,7 @@ class AppContext: ObservableObject { private var documentStoreSubscription: AnyCancellable! let videoPlaybackService = VideoPlaybackService() + let audioPlaybackService = AudioPlaybackService() let overrideTraitCollection = CurrentValueSubject(nil) From 6b9ae8d05df30d6b7d8939f4d6855491d7359eb2 Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Thu, 11 Mar 2021 15:10:41 +0800 Subject: [PATCH 03/11] chore: add mosaicView --- Mastodon.xcodeproj/project.pbxproj | 4 + .../Diffiable/Section/StatusSection.swift | 8 ++ ...Provider+StatusTableViewCellDelegate.swift | 23 +++++- .../View/Container/MosaicPlayerView.swift | 13 ++++ .../Share/View/Container/MosaicView.swift | 74 +++++++++++++++++++ .../TableviewCell/StatusTableViewCell.swift | 1 + 6 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 Mastodon/Scene/Share/View/Container/MosaicView.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 98bfe207..84aade87 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -60,6 +60,7 @@ 2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; }; 2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; }; 2D650FAB25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */; }; + 2D694A7425F9EB4E0038ADDC /* MosaicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D694A7325F9EB4E0038ADDC /* MosaicView.swift */; }; 2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */; }; 2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Toot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */; }; 2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; }; @@ -301,6 +302,7 @@ 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+Timeline.swift"; sourceTree = ""; }; 2D61335D25C1894B00CAE157 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; 2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error+Detail.swift"; sourceTree = ""; }; + 2D694A7325F9EB4E0038ADDC /* MosaicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MosaicView.swift; sourceTree = ""; }; 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = ""; }; 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Toot.swift"; sourceTree = ""; }; 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewController.swift; sourceTree = ""; }; @@ -1177,6 +1179,7 @@ isa = PBXGroup; children = ( DB9D6C0D25E4F9780051B173 /* MosaicImageViewContainer.swift */, + 2D694A7325F9EB4E0038ADDC /* MosaicView.swift */, 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */, 5DF1057825F88A1D00D6C0D4 /* MosaicPlayerView.swift */, 5DF1057E25F88A4100D6C0D4 /* TouchBlockingView.swift */, @@ -1632,6 +1635,7 @@ 2D45E5BF25C9549700A6D639 /* PublicTimelineViewModel+State.swift in Sources */, DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */, 2D3F9E0425DFA133004262D9 /* UITapGestureRecognizer.swift in Sources */, + 2D694A7425F9EB4E0038ADDC /* MosaicView.swift in Sources */, DB4481CC25EE2AFE00BEFB67 /* PollItem.swift in Sources */, DB4563BD25E11A24004DA0B9 /* KeyboardResponderService.swift in Sources */, DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */, diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index daaa9852..f742e473 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -192,6 +192,14 @@ extension StatusSection { let scale: CGFloat = 1.3 return CGSize(width: maxWidth, height: maxWidth * scale) }() + cell.statusView.mosaicPlayerView.mosaicView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil + cell.statusView.mosaicPlayerView.mosaicView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 + cell.statusView.mosaicPlayerView.mosaicView.mosaicButton.publisher(for: .touchUpInside) + .sink { [weak cell] _ in + guard let cell = cell else { return } + cell.delegate?.statusTableViewCell(cell, mosaicView: cell.statusView.mosaicPlayerView.mosaicView, didTapContentWarningVisualEffectView: cell.statusView.mosaicPlayerView.mosaicView.blurVisualEffectView) + } + .store(in: &cell.disposeBag) if let videoAttachment = mediaAttachments.filter({ $0.type == .gifv || $0.type == .video }).first, let videoPlayerViewModel = dependency.context.videoPlaybackService.dequeueVideoPlayerViewModel(for: videoAttachment) diff --git a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift index cd4e5160..db26930c 100644 --- a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift @@ -45,7 +45,28 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) { } - + func statusTableViewCell(_ cell: StatusTableViewCell, mosaicView: MosaicView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { + guard let diffableDataSource = self.tableViewDiffableDataSource else { return } + guard let item = item(for: cell, indexPath: nil) else { return } + + switch item { + case .homeTimelineIndex(_, let attribute): + attribute.isStatusSensitive = false + case .toot(_, let attribute): + attribute.isStatusSensitive = false + default: + return + } + + var snapshot = diffableDataSource.snapshot() + snapshot.reloadItems([item]) + UIView.animate(withDuration: 0.33) { + mosaicView.blurVisualEffectView.effect = nil + mosaicView.vibrancyVisualEffectView.alpha = 0.0 + } completion: { _ in + diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil) + } + } func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { guard let diffableDataSource = self.tableViewDiffableDataSource else { return } guard let item = item(for: cell, indexPath: nil) else { return } diff --git a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift b/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift index e7c478ce..52596162 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift +++ b/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift @@ -15,6 +15,11 @@ final class MosaicPlayerView: UIView { private let touchBlockingView = TouchBlockingView() private var containerHeightLayoutConstraint: NSLayoutConstraint! + let mosaicView: MosaicView = { + let mosaicView = MosaicView() + return mosaicView + }() + let playerViewController = AVPlayerViewController() let gifIndicatorLabel: UILabel = { @@ -38,6 +43,14 @@ final class MosaicPlayerView: UIView { extension MosaicPlayerView { private func _init() { + addSubview(mosaicView) + NSLayoutConstraint.activate([ + mosaicView.topAnchor.constraint(equalTo: topAnchor), + mosaicView.leadingAnchor.constraint(equalTo: leadingAnchor), + mosaicView.trailingAnchor.constraint(equalTo: trailingAnchor), + mosaicView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) containerHeightLayoutConstraint = container.heightAnchor.constraint(equalToConstant: 162).priority(.required - 1) diff --git a/Mastodon/Scene/Share/View/Container/MosaicView.swift b/Mastodon/Scene/Share/View/Container/MosaicView.swift new file mode 100644 index 00000000..10adf3ab --- /dev/null +++ b/Mastodon/Scene/Share/View/Container/MosaicView.swift @@ -0,0 +1,74 @@ +// +// MosaicView.swift +// Mastodon +// +// Created by sxiaojian on 2021/3/11. +// + +import Foundation +import UIKit + +class MosaicView: UIView { + static let cornerRadius: CGFloat = 4 + static let blurVisualEffect = UIBlurEffect(style: .systemUltraThinMaterial) + let blurVisualEffectView = UIVisualEffectView(effect: MosaicView.blurVisualEffect) + let vibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: MosaicView.blurVisualEffect)) + + let mosaicButton: UIButton = { + let button = UIButton(type: .custom) + button.backgroundColor = .clear + return button + }() + + let contentWarningLabel: UILabel = { + let label = UILabel() + label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15)) + label.text = L10n.Common.Controls.Status.mediaContentWarning + label.textAlignment = .center + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } +} + +extension MosaicView { + private func _init() { + translatesAutoresizingMaskIntoConstraints = false + addSubview(mosaicButton) + NSLayoutConstraint.activate([ + mosaicButton.topAnchor.constraint(equalTo: topAnchor), + mosaicButton.trailingAnchor.constraint(equalTo: trailingAnchor), + mosaicButton.bottomAnchor.constraint(equalTo: bottomAnchor), + mosaicButton.leadingAnchor.constraint(equalTo: leadingAnchor), + ]) + // add blur visual effect view in the setup method + blurVisualEffectView.layer.masksToBounds = true + blurVisualEffectView.layer.cornerRadius = MosaicView.cornerRadius + blurVisualEffectView.layer.cornerCurve = .continuous + + vibrancyVisualEffectView.translatesAutoresizingMaskIntoConstraints = false + blurVisualEffectView.contentView.addSubview(vibrancyVisualEffectView) + NSLayoutConstraint.activate([ + vibrancyVisualEffectView.topAnchor.constraint(equalTo: blurVisualEffectView.topAnchor), + vibrancyVisualEffectView.leadingAnchor.constraint(equalTo: blurVisualEffectView.leadingAnchor), + vibrancyVisualEffectView.trailingAnchor.constraint(equalTo: blurVisualEffectView.trailingAnchor), + vibrancyVisualEffectView.bottomAnchor.constraint(equalTo: blurVisualEffectView.bottomAnchor), + ]) + + contentWarningLabel.translatesAutoresizingMaskIntoConstraints = false + vibrancyVisualEffectView.contentView.addSubview(contentWarningLabel) + NSLayoutConstraint.activate([ + contentWarningLabel.leadingAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.layoutMarginsGuide.leadingAnchor), + contentWarningLabel.trailingAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.layoutMarginsGuide.trailingAnchor), + contentWarningLabel.centerYAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.centerYAnchor), + ]) + } +} diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift index 2f4000b9..d1b5a5c2 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift @@ -23,6 +23,7 @@ protocol StatusTableViewCellDelegate: class { func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, contentWarningActionButtonPressed button: UIButton) func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) + func statusTableViewCell(_ cell: StatusTableViewCell, mosaicView: MosaicView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton) From 1342471c5dc9c4d3ac56006bd241168c1bfd4ff2 Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Thu, 11 Mar 2021 15:34:30 +0800 Subject: [PATCH 04/11] chore: handle Video&Gif sensitive situation --- .../Diffiable/Section/StatusSection.swift | 2 ++ ...Provider+StatusTableViewCellDelegate.swift | 2 +- .../View/Container/MosaicPlayerView.swift | 16 +++++------ .../Share/View/Container/MosaicView.swift | 27 ++++++++++++++----- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index f742e473..53a2207a 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -192,8 +192,10 @@ extension StatusSection { let scale: CGFloat = 1.3 return CGSize(width: maxWidth, height: maxWidth * scale) }() + cell.statusView.mosaicPlayerView.mosaicView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil cell.statusView.mosaicPlayerView.mosaicView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 + cell.statusView.mosaicPlayerView.mosaicView.isUserInteractionEnabled = isStatusSensitive cell.statusView.mosaicPlayerView.mosaicView.mosaicButton.publisher(for: .touchUpInside) .sink { [weak cell] _ in guard let cell = cell else { return } diff --git a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift index db26930c..9e2797f2 100644 --- a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift @@ -57,7 +57,7 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { default: return } - + mosaicView.isUserInteractionEnabled = false var snapshot = diffableDataSource.snapshot() snapshot.reloadItems([item]) UIView.animate(withDuration: 0.33) { diff --git a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift b/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift index 52596162..1851154b 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift +++ b/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift @@ -43,14 +43,6 @@ final class MosaicPlayerView: UIView { extension MosaicPlayerView { private func _init() { - addSubview(mosaicView) - NSLayoutConstraint.activate([ - mosaicView.topAnchor.constraint(equalTo: topAnchor), - mosaicView.leadingAnchor.constraint(equalTo: leadingAnchor), - mosaicView.trailingAnchor.constraint(equalTo: trailingAnchor), - mosaicView.bottomAnchor.constraint(equalTo: bottomAnchor) - ]) - container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) containerHeightLayoutConstraint = container.heightAnchor.constraint(equalToConstant: 162).priority(.required - 1) @@ -74,6 +66,14 @@ extension MosaicPlayerView { playerViewController.view.layer.masksToBounds = true playerViewController.view.layer.cornerRadius = MosaicPlayerView.cornerRadius playerViewController.view.layer.cornerCurve = .continuous + + addSubview(mosaicView) + NSLayoutConstraint.activate([ + mosaicView.topAnchor.constraint(equalTo: topAnchor), + mosaicView.leadingAnchor.constraint(equalTo: leadingAnchor), + mosaicView.trailingAnchor.constraint(equalTo: trailingAnchor), + mosaicView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) } } diff --git a/Mastodon/Scene/Share/View/Container/MosaicView.swift b/Mastodon/Scene/Share/View/Container/MosaicView.swift index 10adf3ab..82049dbb 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicView.swift +++ b/Mastodon/Scene/Share/View/Container/MosaicView.swift @@ -17,6 +17,7 @@ class MosaicView: UIView { let mosaicButton: UIButton = { let button = UIButton(type: .custom) button.backgroundColor = .clear + button.translatesAutoresizingMaskIntoConstraints = false return button }() @@ -41,14 +42,9 @@ class MosaicView: UIView { extension MosaicView { private func _init() { + backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false - addSubview(mosaicButton) - NSLayoutConstraint.activate([ - mosaicButton.topAnchor.constraint(equalTo: topAnchor), - mosaicButton.trailingAnchor.constraint(equalTo: trailingAnchor), - mosaicButton.bottomAnchor.constraint(equalTo: bottomAnchor), - mosaicButton.leadingAnchor.constraint(equalTo: leadingAnchor), - ]) + // add blur visual effect view in the setup method blurVisualEffectView.layer.masksToBounds = true blurVisualEffectView.layer.cornerRadius = MosaicView.cornerRadius @@ -70,5 +66,22 @@ extension MosaicView { contentWarningLabel.trailingAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.layoutMarginsGuide.trailingAnchor), contentWarningLabel.centerYAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.centerYAnchor), ]) + + blurVisualEffectView.translatesAutoresizingMaskIntoConstraints = false + addSubview(blurVisualEffectView) + NSLayoutConstraint.activate([ + blurVisualEffectView.topAnchor.constraint(equalTo: topAnchor), + blurVisualEffectView.leadingAnchor.constraint(equalTo: leadingAnchor), + blurVisualEffectView.trailingAnchor.constraint(equalTo: trailingAnchor), + blurVisualEffectView.bottomAnchor.constraint(equalTo: bottomAnchor), + ]) + + addSubview(mosaicButton) + NSLayoutConstraint.activate([ + mosaicButton.topAnchor.constraint(equalTo: topAnchor), + mosaicButton.trailingAnchor.constraint(equalTo: trailingAnchor), + mosaicButton.bottomAnchor.constraint(equalTo: bottomAnchor), + mosaicButton.leadingAnchor.constraint(equalTo: leadingAnchor), + ]) } } From bbdd6926d64cf840e644105662fb2bdd54cb63cf Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Thu, 11 Mar 2021 17:24:00 +0800 Subject: [PATCH 05/11] chore: rename MosaicView to MosaicBlurView --- Mastodon.xcodeproj/project.pbxproj | 8 +++--- .../Diffiable/Section/StatusSection.swift | 10 +++---- ...Provider+StatusTableViewCellDelegate.swift | 10 ++++--- ...{MosaicView.swift => MosaicBlurView.swift} | 26 +++++++++---------- .../View/Container/MosaicPlayerView.swift | 16 ++++++------ .../TableviewCell/StatusTableViewCell.swift | 2 +- 6 files changed, 37 insertions(+), 35 deletions(-) rename Mastodon/Scene/Share/View/Container/{MosaicView.swift => MosaicBlurView.swift} (84%) diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 84aade87..5524314b 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -60,7 +60,7 @@ 2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; }; 2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; }; 2D650FAB25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */; }; - 2D694A7425F9EB4E0038ADDC /* MosaicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D694A7325F9EB4E0038ADDC /* MosaicView.swift */; }; + 2D694A7425F9EB4E0038ADDC /* MosaicBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D694A7325F9EB4E0038ADDC /* MosaicBlurView.swift */; }; 2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */; }; 2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Toot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */; }; 2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; }; @@ -302,7 +302,7 @@ 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+Timeline.swift"; sourceTree = ""; }; 2D61335D25C1894B00CAE157 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; 2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error+Detail.swift"; sourceTree = ""; }; - 2D694A7325F9EB4E0038ADDC /* MosaicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MosaicView.swift; sourceTree = ""; }; + 2D694A7325F9EB4E0038ADDC /* MosaicBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MosaicBlurView.swift; sourceTree = ""; }; 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = ""; }; 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Toot.swift"; sourceTree = ""; }; 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewController.swift; sourceTree = ""; }; @@ -1179,7 +1179,7 @@ isa = PBXGroup; children = ( DB9D6C0D25E4F9780051B173 /* MosaicImageViewContainer.swift */, - 2D694A7325F9EB4E0038ADDC /* MosaicView.swift */, + 2D694A7325F9EB4E0038ADDC /* MosaicBlurView.swift */, 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */, 5DF1057825F88A1D00D6C0D4 /* MosaicPlayerView.swift */, 5DF1057E25F88A4100D6C0D4 /* TouchBlockingView.swift */, @@ -1635,7 +1635,7 @@ 2D45E5BF25C9549700A6D639 /* PublicTimelineViewModel+State.swift in Sources */, DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */, 2D3F9E0425DFA133004262D9 /* UITapGestureRecognizer.swift in Sources */, - 2D694A7425F9EB4E0038ADDC /* MosaicView.swift in Sources */, + 2D694A7425F9EB4E0038ADDC /* MosaicBlurView.swift in Sources */, DB4481CC25EE2AFE00BEFB67 /* PollItem.swift in Sources */, DB4563BD25E11A24004DA0B9 /* KeyboardResponderService.swift in Sources */, DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */, diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 53a2207a..31ea87cd 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -193,13 +193,13 @@ extension StatusSection { return CGSize(width: maxWidth, height: maxWidth * scale) }() - cell.statusView.mosaicPlayerView.mosaicView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil - cell.statusView.mosaicPlayerView.mosaicView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 - cell.statusView.mosaicPlayerView.mosaicView.isUserInteractionEnabled = isStatusSensitive - cell.statusView.mosaicPlayerView.mosaicView.mosaicButton.publisher(for: .touchUpInside) + cell.statusView.mosaicPlayerView.mosaicBlurView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil + cell.statusView.mosaicPlayerView.mosaicBlurView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 + cell.statusView.mosaicPlayerView.mosaicBlurView.isUserInteractionEnabled = isStatusSensitive + cell.statusView.mosaicPlayerView.mosaicBlurView.tapButton.publisher(for: .touchUpInside) .sink { [weak cell] _ in guard let cell = cell else { return } - cell.delegate?.statusTableViewCell(cell, mosaicView: cell.statusView.mosaicPlayerView.mosaicView, didTapContentWarningVisualEffectView: cell.statusView.mosaicPlayerView.mosaicView.blurVisualEffectView) + cell.delegate?.statusTableViewCell(cell, mosaicBlurView: cell.statusView.mosaicPlayerView.mosaicBlurView, didTapContentWarningVisualEffectView: cell.statusView.mosaicPlayerView.mosaicBlurView.blurVisualEffectView) } .store(in: &cell.disposeBag) diff --git a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift index 9e2797f2..67753e88 100644 --- a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift @@ -45,7 +45,8 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) { } - func statusTableViewCell(_ cell: StatusTableViewCell, mosaicView: MosaicView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { + + func statusTableViewCell(_ cell: StatusTableViewCell, mosaicBlurView: MosaicBlurView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { guard let diffableDataSource = self.tableViewDiffableDataSource else { return } guard let item = item(for: cell, indexPath: nil) else { return } @@ -57,16 +58,17 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { default: return } - mosaicView.isUserInteractionEnabled = false + mosaicBlurView.isUserInteractionEnabled = false var snapshot = diffableDataSource.snapshot() snapshot.reloadItems([item]) UIView.animate(withDuration: 0.33) { - mosaicView.blurVisualEffectView.effect = nil - mosaicView.vibrancyVisualEffectView.alpha = 0.0 + mosaicBlurView.blurVisualEffectView.effect = nil + mosaicBlurView.vibrancyVisualEffectView.alpha = 0.0 } completion: { _ in diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil) } } + func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { guard let diffableDataSource = self.tableViewDiffableDataSource else { return } guard let item = item(for: cell, indexPath: nil) else { return } diff --git a/Mastodon/Scene/Share/View/Container/MosaicView.swift b/Mastodon/Scene/Share/View/Container/MosaicBlurView.swift similarity index 84% rename from Mastodon/Scene/Share/View/Container/MosaicView.swift rename to Mastodon/Scene/Share/View/Container/MosaicBlurView.swift index 82049dbb..72f03ab6 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicView.swift +++ b/Mastodon/Scene/Share/View/Container/MosaicBlurView.swift @@ -8,13 +8,13 @@ import Foundation import UIKit -class MosaicView: UIView { +class MosaicBlurView: UIView { static let cornerRadius: CGFloat = 4 static let blurVisualEffect = UIBlurEffect(style: .systemUltraThinMaterial) - let blurVisualEffectView = UIVisualEffectView(effect: MosaicView.blurVisualEffect) - let vibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: MosaicView.blurVisualEffect)) + let blurVisualEffectView = UIVisualEffectView(effect: MosaicBlurView.blurVisualEffect) + let vibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: MosaicBlurView.blurVisualEffect)) - let mosaicButton: UIButton = { + let tapButton: UIButton = { let button = UIButton(type: .custom) button.backgroundColor = .clear button.translatesAutoresizingMaskIntoConstraints = false @@ -40,14 +40,14 @@ class MosaicView: UIView { } } -extension MosaicView { +extension MosaicBlurView { private func _init() { backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false // add blur visual effect view in the setup method blurVisualEffectView.layer.masksToBounds = true - blurVisualEffectView.layer.cornerRadius = MosaicView.cornerRadius + blurVisualEffectView.layer.cornerRadius = MosaicBlurView.cornerRadius blurVisualEffectView.layer.cornerCurve = .continuous vibrancyVisualEffectView.translatesAutoresizingMaskIntoConstraints = false @@ -66,7 +66,7 @@ extension MosaicView { contentWarningLabel.trailingAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.layoutMarginsGuide.trailingAnchor), contentWarningLabel.centerYAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.centerYAnchor), ]) - + blurVisualEffectView.translatesAutoresizingMaskIntoConstraints = false addSubview(blurVisualEffectView) NSLayoutConstraint.activate([ @@ -75,13 +75,13 @@ extension MosaicView { blurVisualEffectView.trailingAnchor.constraint(equalTo: trailingAnchor), blurVisualEffectView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) - - addSubview(mosaicButton) + + addSubview(tapButton) NSLayoutConstraint.activate([ - mosaicButton.topAnchor.constraint(equalTo: topAnchor), - mosaicButton.trailingAnchor.constraint(equalTo: trailingAnchor), - mosaicButton.bottomAnchor.constraint(equalTo: bottomAnchor), - mosaicButton.leadingAnchor.constraint(equalTo: leadingAnchor), + tapButton.topAnchor.constraint(equalTo: topAnchor), + tapButton.trailingAnchor.constraint(equalTo: trailingAnchor), + tapButton.bottomAnchor.constraint(equalTo: bottomAnchor), + tapButton.leadingAnchor.constraint(equalTo: leadingAnchor), ]) } } diff --git a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift b/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift index 1851154b..49ccca5b 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift +++ b/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift @@ -15,9 +15,9 @@ final class MosaicPlayerView: UIView { private let touchBlockingView = TouchBlockingView() private var containerHeightLayoutConstraint: NSLayoutConstraint! - let mosaicView: MosaicView = { - let mosaicView = MosaicView() - return mosaicView + let mosaicBlurView: MosaicBlurView = { + let mosaicBlurView = MosaicBlurView() + return mosaicBlurView }() let playerViewController = AVPlayerViewController() @@ -67,12 +67,12 @@ extension MosaicPlayerView { playerViewController.view.layer.cornerRadius = MosaicPlayerView.cornerRadius playerViewController.view.layer.cornerCurve = .continuous - addSubview(mosaicView) + addSubview(mosaicBlurView) NSLayoutConstraint.activate([ - mosaicView.topAnchor.constraint(equalTo: topAnchor), - mosaicView.leadingAnchor.constraint(equalTo: leadingAnchor), - mosaicView.trailingAnchor.constraint(equalTo: trailingAnchor), - mosaicView.bottomAnchor.constraint(equalTo: bottomAnchor) + mosaicBlurView.topAnchor.constraint(equalTo: topAnchor), + mosaicBlurView.leadingAnchor.constraint(equalTo: leadingAnchor), + mosaicBlurView.trailingAnchor.constraint(equalTo: trailingAnchor), + mosaicBlurView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } } diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift index d1b5a5c2..b6af9826 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift @@ -23,7 +23,7 @@ protocol StatusTableViewCellDelegate: class { func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, contentWarningActionButtonPressed button: UIButton) func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) - func statusTableViewCell(_ cell: StatusTableViewCell, mosaicView: MosaicView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) + func statusTableViewCell(_ cell: StatusTableViewCell, mosaicBlurView: MosaicBlurView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton) From 19a14b7761d6fa868a1d34a31b82e275202f87f0 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 11 Mar 2021 19:06:15 +0800 Subject: [PATCH 06/11] chore: patch for delegate chain --- Mastodon.xcodeproj/project.pbxproj | 16 +++---- .../Diffiable/Section/StatusSection.swift | 18 +++---- ...Provider+StatusTableViewCellDelegate.swift | 46 +++++++++--------- ...erView.swift => PlayerContainerView.swift} | 41 +++++++++++----- .../ContentWarningOverlayView.swift} | 47 ++++++++++--------- .../Scene/Share/View/Content/StatusView.swift | 16 +++++-- .../TableviewCell/StatusTableViewCell.swift | 10 ++-- 7 files changed, 111 insertions(+), 83 deletions(-) rename Mastodon/Scene/Share/View/Container/{MosaicPlayerView.swift => PlayerContainerView.swift} (76%) rename Mastodon/Scene/Share/View/{Container/MosaicBlurView.swift => Content/ContentWarningOverlayView.swift} (69%) diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 5524314b..368a15f3 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -60,7 +60,7 @@ 2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; }; 2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; }; 2D650FAB25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */; }; - 2D694A7425F9EB4E0038ADDC /* MosaicBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D694A7325F9EB4E0038ADDC /* MosaicBlurView.swift */; }; + 2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D694A7325F9EB4E0038ADDC /* ContentWarningOverlayView.swift */; }; 2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */; }; 2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Toot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */; }; 2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; }; @@ -94,7 +94,7 @@ 5DF1054125F886D400D6C0D4 /* ViedeoPlaybackService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */; }; 5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */; }; 5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */; }; - 5DF1057925F88A1D00D6C0D4 /* MosaicPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1057825F88A1D00D6C0D4 /* MosaicPlayerView.swift */; }; + 5DF1057925F88A1D00D6C0D4 /* PlayerContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */; }; 5DF1057F25F88A4100D6C0D4 /* TouchBlockingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1057E25F88A4100D6C0D4 /* TouchBlockingView.swift */; }; 5DF1058525F88AE500D6C0D4 /* NeedsDependency+AVPlayerViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1058425F88AE500D6C0D4 /* NeedsDependency+AVPlayerViewControllerDelegate.swift */; }; 5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; }; @@ -302,7 +302,7 @@ 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+Timeline.swift"; sourceTree = ""; }; 2D61335D25C1894B00CAE157 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; 2D650FAA25ECDC9300851B58 /* Mastodon+Entity+Error+Detail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error+Detail.swift"; sourceTree = ""; }; - 2D694A7325F9EB4E0038ADDC /* MosaicBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MosaicBlurView.swift; sourceTree = ""; }; + 2D694A7325F9EB4E0038ADDC /* ContentWarningOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningOverlayView.swift; sourceTree = ""; }; 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = ""; }; 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Toot.swift"; sourceTree = ""; }; 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewController.swift; sourceTree = ""; }; @@ -339,7 +339,7 @@ 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViedeoPlaybackService.swift; sourceTree = ""; }; 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = ""; }; 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayer.swift; sourceTree = ""; }; - 5DF1057825F88A1D00D6C0D4 /* MosaicPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MosaicPlayerView.swift; sourceTree = ""; }; + 5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerContainerView.swift; sourceTree = ""; }; 5DF1057E25F88A4100D6C0D4 /* TouchBlockingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBlockingView.swift; sourceTree = ""; }; 5DF1058425F88AE500D6C0D4 /* NeedsDependency+AVPlayerViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NeedsDependency+AVPlayerViewControllerDelegate.swift"; sourceTree = ""; }; 75E3471C898DDD9631729B6E /* Pods-Mastodon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.release.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.release.xcconfig"; sourceTree = ""; }; @@ -578,6 +578,7 @@ isa = PBXGroup; children = ( 2D152A8B25C295CC009AA50C /* StatusView.swift */, + 2D694A7325F9EB4E0038ADDC /* ContentWarningOverlayView.swift */, ); path = Content; sourceTree = ""; @@ -1179,9 +1180,8 @@ isa = PBXGroup; children = ( DB9D6C0D25E4F9780051B173 /* MosaicImageViewContainer.swift */, - 2D694A7325F9EB4E0038ADDC /* MosaicBlurView.swift */, 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */, - 5DF1057825F88A1D00D6C0D4 /* MosaicPlayerView.swift */, + 5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */, 5DF1057E25F88A4100D6C0D4 /* TouchBlockingView.swift */, ); path = Container; @@ -1635,7 +1635,7 @@ 2D45E5BF25C9549700A6D639 /* PublicTimelineViewModel+State.swift in Sources */, DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */, 2D3F9E0425DFA133004262D9 /* UITapGestureRecognizer.swift in Sources */, - 2D694A7425F9EB4E0038ADDC /* MosaicBlurView.swift in Sources */, + 2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */, DB4481CC25EE2AFE00BEFB67 /* PollItem.swift in Sources */, DB4563BD25E11A24004DA0B9 /* KeyboardResponderService.swift in Sources */, DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */, @@ -1711,7 +1711,7 @@ 0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */, 2D38F1FE25CD481700561493 /* StatusProvider.swift in Sources */, 0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */, - 5DF1057925F88A1D00D6C0D4 /* MosaicPlayerView.swift in Sources */, + 5DF1057925F88A1D00D6C0D4 /* PlayerContainerView.swift in Sources */, DB45FB0F25CA87D0005A8AC7 /* AuthenticationService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 31ea87cd..3fc1d28a 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -193,21 +193,15 @@ extension StatusSection { return CGSize(width: maxWidth, height: maxWidth * scale) }() - cell.statusView.mosaicPlayerView.mosaicBlurView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil - cell.statusView.mosaicPlayerView.mosaicBlurView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 - cell.statusView.mosaicPlayerView.mosaicBlurView.isUserInteractionEnabled = isStatusSensitive - cell.statusView.mosaicPlayerView.mosaicBlurView.tapButton.publisher(for: .touchUpInside) - .sink { [weak cell] _ in - guard let cell = cell else { return } - cell.delegate?.statusTableViewCell(cell, mosaicBlurView: cell.statusView.mosaicPlayerView.mosaicBlurView, didTapContentWarningVisualEffectView: cell.statusView.mosaicPlayerView.mosaicBlurView.blurVisualEffectView) - } - .store(in: &cell.disposeBag) + cell.statusView.playerContainerView.contentWarningOverlayView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil + cell.statusView.playerContainerView.contentWarningOverlayView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 + cell.statusView.playerContainerView.contentWarningOverlayView.isUserInteractionEnabled = isStatusSensitive if let videoAttachment = mediaAttachments.filter({ $0.type == .gifv || $0.type == .video }).first, let videoPlayerViewModel = dependency.context.videoPlaybackService.dequeueVideoPlayerViewModel(for: videoAttachment) { let parent = cell.delegate?.parent() - let mosaicPlayerView = cell.statusView.mosaicPlayerView + let mosaicPlayerView = cell.statusView.playerContainerView let playerViewController = mosaicPlayerView.setupPlayer( aspectRatio: videoPlayerViewModel.videoSize, maxSize: playerViewMaxSize, @@ -221,8 +215,8 @@ extension StatusSection { mosaicPlayerView.isHidden = false } else { - cell.statusView.mosaicPlayerView.playerViewController.player?.pause() - cell.statusView.mosaicPlayerView.playerViewController.player = nil + cell.statusView.playerContainerView.playerViewController.player?.pause() + cell.statusView.playerContainerView.playerViewController.player = nil } // set poll let poll = (toot.reblog ?? toot).poll diff --git a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift index 67753e88..cebc845a 100644 --- a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift @@ -46,29 +46,6 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { } - func statusTableViewCell(_ cell: StatusTableViewCell, mosaicBlurView: MosaicBlurView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { - guard let diffableDataSource = self.tableViewDiffableDataSource else { return } - guard let item = item(for: cell, indexPath: nil) else { return } - - switch item { - case .homeTimelineIndex(_, let attribute): - attribute.isStatusSensitive = false - case .toot(_, let attribute): - attribute.isStatusSensitive = false - default: - return - } - mosaicBlurView.isUserInteractionEnabled = false - var snapshot = diffableDataSource.snapshot() - snapshot.reloadItems([item]) - UIView.animate(withDuration: 0.33) { - mosaicBlurView.blurVisualEffectView.effect = nil - mosaicBlurView.vibrancyVisualEffectView.alpha = 0.0 - } completion: { _ in - diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil) - } - } - func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { guard let diffableDataSource = self.tableViewDiffableDataSource else { return } guard let item = item(for: cell, indexPath: nil) else { return } @@ -92,6 +69,29 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { } } + func statusTableViewCell(_ cell: StatusTableViewCell, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { + guard let diffableDataSource = self.tableViewDiffableDataSource else { return } + guard let item = item(for: cell, indexPath: nil) else { return } + + switch item { + case .homeTimelineIndex(_, let attribute): + attribute.isStatusSensitive = false + case .toot(_, let attribute): + attribute.isStatusSensitive = false + default: + return + } + contentWarningOverlayView.isUserInteractionEnabled = false + var snapshot = diffableDataSource.snapshot() + snapshot.reloadItems([item]) + UIView.animate(withDuration: 0.33) { + contentWarningOverlayView.blurVisualEffectView.effect = nil + contentWarningOverlayView.vibrancyVisualEffectView.alpha = 0.0 + } completion: { _ in + diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil) + } + } + } // MARK: - PollTableView diff --git a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift similarity index 76% rename from Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift rename to Mastodon/Scene/Share/View/Container/PlayerContainerView.swift index 49ccca5b..aa60bacd 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicPlayerView.swift +++ b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift @@ -1,5 +1,5 @@ // -// MosaicPlayerView.swift +// PlayerContainerView.swift // Mastodon // // Created by xiaojian sun on 2021/3/10. @@ -8,16 +8,20 @@ import AVKit import UIKit -final class MosaicPlayerView: UIView { +protocol PlayerContainerViewDelegate: class { + func playerContainerView(_ playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) +} + +final class PlayerContainerView: UIView { static let cornerRadius: CGFloat = 8 private let container = UIView() private let touchBlockingView = TouchBlockingView() private var containerHeightLayoutConstraint: NSLayoutConstraint! - let mosaicBlurView: MosaicBlurView = { - let mosaicBlurView = MosaicBlurView() - return mosaicBlurView + let contentWarningOverlayView: ContentWarningOverlayView = { + let contentWarningOverlayView = ContentWarningOverlayView() + return contentWarningOverlayView }() let playerViewController = AVPlayerViewController() @@ -30,6 +34,8 @@ final class MosaicPlayerView: UIView { return label }() + weak var delegate: PlayerContainerViewDelegate? + override init(frame: CGRect) { super.init(frame: frame) _init() @@ -41,7 +47,7 @@ final class MosaicPlayerView: UIView { } } -extension MosaicPlayerView { +extension PlayerContainerView { private func _init() { container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) @@ -64,20 +70,29 @@ extension MosaicPlayerView { // will not influence full-screen playback playerViewController.view.layer.masksToBounds = true - playerViewController.view.layer.cornerRadius = MosaicPlayerView.cornerRadius + playerViewController.view.layer.cornerRadius = PlayerContainerView.cornerRadius playerViewController.view.layer.cornerCurve = .continuous - addSubview(mosaicBlurView) + addSubview(contentWarningOverlayView) NSLayoutConstraint.activate([ - mosaicBlurView.topAnchor.constraint(equalTo: topAnchor), - mosaicBlurView.leadingAnchor.constraint(equalTo: leadingAnchor), - mosaicBlurView.trailingAnchor.constraint(equalTo: trailingAnchor), - mosaicBlurView.bottomAnchor.constraint(equalTo: bottomAnchor) + contentWarningOverlayView.topAnchor.constraint(equalTo: topAnchor), + contentWarningOverlayView.leadingAnchor.constraint(equalTo: leadingAnchor), + contentWarningOverlayView.trailingAnchor.constraint(equalTo: trailingAnchor), + contentWarningOverlayView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) + + contentWarningOverlayView.delegate = self } } -extension MosaicPlayerView { +// MARK: - ContentWarningOverlayViewDelegate +extension PlayerContainerView: ContentWarningOverlayViewDelegate { + func contentWarningOverlayViewDidPressed(_ contentWarningOverlayView: ContentWarningOverlayView) { + delegate?.playerContainerView(self, contentWarningOverlayViewDidPressed: contentWarningOverlayView) + } +} + +extension PlayerContainerView { func reset() { // note: set playerViewController.player pause() and nil in data source configuration process make reloadData not break playing diff --git a/Mastodon/Scene/Share/View/Container/MosaicBlurView.swift b/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift similarity index 69% rename from Mastodon/Scene/Share/View/Container/MosaicBlurView.swift rename to Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift index 72f03ab6..ec2607e2 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicBlurView.swift +++ b/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift @@ -1,25 +1,24 @@ // -// MosaicView.swift +// ContentWarningOverlayView.swift // Mastodon // // Created by sxiaojian on 2021/3/11. // +import os.log import Foundation import UIKit -class MosaicBlurView: UIView { +protocol ContentWarningOverlayViewDelegate: class { + func contentWarningOverlayViewDidPressed(_ contentWarningOverlayView: ContentWarningOverlayView) +} + +class ContentWarningOverlayView: UIView { + static let cornerRadius: CGFloat = 4 static let blurVisualEffect = UIBlurEffect(style: .systemUltraThinMaterial) - let blurVisualEffectView = UIVisualEffectView(effect: MosaicBlurView.blurVisualEffect) - let vibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: MosaicBlurView.blurVisualEffect)) - - let tapButton: UIButton = { - let button = UIButton(type: .custom) - button.backgroundColor = .clear - button.translatesAutoresizingMaskIntoConstraints = false - return button - }() + let blurVisualEffectView = UIVisualEffectView(effect: ContentWarningOverlayView.blurVisualEffect) + let vibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: ContentWarningOverlayView.blurVisualEffect)) let contentWarningLabel: UILabel = { let label = UILabel() @@ -28,6 +27,10 @@ class MosaicBlurView: UIView { label.textAlignment = .center return label }() + + let tapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer + + weak var delegate: ContentWarningOverlayViewDelegate? override init(frame: CGRect) { super.init(frame: frame) @@ -40,14 +43,14 @@ class MosaicBlurView: UIView { } } -extension MosaicBlurView { +extension ContentWarningOverlayView { private func _init() { backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false // add blur visual effect view in the setup method blurVisualEffectView.layer.masksToBounds = true - blurVisualEffectView.layer.cornerRadius = MosaicBlurView.cornerRadius + blurVisualEffectView.layer.cornerRadius = ContentWarningOverlayView.cornerRadius blurVisualEffectView.layer.cornerCurve = .continuous vibrancyVisualEffectView.translatesAutoresizingMaskIntoConstraints = false @@ -75,13 +78,15 @@ extension MosaicBlurView { blurVisualEffectView.trailingAnchor.constraint(equalTo: trailingAnchor), blurVisualEffectView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) - - addSubview(tapButton) - NSLayoutConstraint.activate([ - tapButton.topAnchor.constraint(equalTo: topAnchor), - tapButton.trailingAnchor.constraint(equalTo: trailingAnchor), - tapButton.bottomAnchor.constraint(equalTo: bottomAnchor), - tapButton.leadingAnchor.constraint(equalTo: leadingAnchor), - ]) + + tapGestureRecognizer.addTarget(self, action: #selector(ContentWarningOverlayView.tapGestureRecognizerHandler(_:))) + addGestureRecognizer(tapGestureRecognizer) + } +} + +extension ContentWarningOverlayView { + @objc private func tapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + delegate?.contentWarningOverlayViewDidPressed(self) } } diff --git a/Mastodon/Scene/Share/View/Content/StatusView.swift b/Mastodon/Scene/Share/View/Content/StatusView.swift index ad773478..4f6f5d43 100644 --- a/Mastodon/Scene/Share/View/Content/StatusView.swift +++ b/Mastodon/Scene/Share/View/Content/StatusView.swift @@ -13,6 +13,7 @@ import AlamofireImage protocol StatusViewDelegate: class { func statusView(_ statusView: StatusView, contentWarningActionButtonPressed button: UIButton) + func statusView(_ statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) func statusView(_ statusView: StatusView, pollVoteButtonPressed button: UIButton) } @@ -156,7 +157,7 @@ final class StatusView: UIView { return imageView }() - let mosaicPlayerView = MosaicPlayerView() + let playerContainerView = PlayerContainerView() let audioView: AudioContainerView = { let audioView = AudioContainerView() @@ -353,7 +354,7 @@ extension StatusView { audioView.heightAnchor.constraint(equalToConstant: 44) ]) // video gif - statusContainerStackView.addArrangedSubview(mosaicPlayerView) + statusContainerStackView.addArrangedSubview(playerContainerView) // action toolbar container containerStackView.addArrangedSubview(actionToolbarContainer) @@ -364,12 +365,14 @@ extension StatusView { pollTableView.isHidden = true pollStatusStackView.isHidden = true audioView.isHidden = true - mosaicPlayerView.isHidden = true + playerContainerView.isHidden = true contentWarningBlurContentImageView.isHidden = true statusContentWarningContainerStackView.isHidden = true statusContentWarningContainerStackViewBottomLayoutConstraint.isActive = false + playerContainerView.delegate = self + contentWarningActionButton.addTarget(self, action: #selector(StatusView.contentWarningActionButtonPressed(_:)), for: .touchUpInside) pollVoteButton.addTarget(self, action: #selector(StatusView.pollVoteButtonPressed(_:)), for: .touchUpInside) } @@ -420,6 +423,13 @@ extension StatusView { } +// MARK: - PlayerContainerViewDelegate +extension StatusView: PlayerContainerViewDelegate { + func playerContainerView(_ playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { + delegate?.statusView(self, playerContainerView: playerContainerView, contentWarningOverlayViewDidPressed: contentWarningOverlayView) + } +} + // MARK: - AvatarConfigurableView extension StatusView: AvatarConfigurableView { static var configurableAvatarImageSize: CGSize { return Self.avatarImageSize } diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift index b6af9826..e8d986bd 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift @@ -23,8 +23,8 @@ protocol StatusTableViewCellDelegate: class { func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, contentWarningActionButtonPressed button: UIButton) func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) - func statusTableViewCell(_ cell: StatusTableViewCell, mosaicBlurView: MosaicBlurView, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) + func statusTableViewCell(_ cell: StatusTableViewCell, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton) func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, pollVoteButtonPressed button: UIButton) @@ -55,8 +55,8 @@ final class StatusTableViewCell: UITableViewCell { statusView.isStatusTextSensitive = false statusView.cleanUpContentWarning() statusView.pollTableView.dataSource = nil - statusView.mosaicPlayerView.reset() - statusView.mosaicPlayerView.isHidden = true + statusView.playerContainerView.reset() + statusView.playerContainerView.isHidden = true disposeBag.removeAll() observations.removeAll() } @@ -198,6 +198,10 @@ extension StatusTableViewCell: StatusViewDelegate { delegate?.statusTableViewCell(self, statusView: statusView, contentWarningActionButtonPressed: button) } + func statusView(_ statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { + delegate?.statusTableViewCell(self, playerContainerView: playerContainerView, contentWarningOverlayViewDidPressed: contentWarningOverlayView) + } + func statusView(_ statusView: StatusView, pollVoteButtonPressed button: UIButton) { delegate?.statusTableViewCell(self, statusView: statusView, pollVoteButtonPressed: button) } From 03496a4e97866c4d30e8585bcbff0bd73788ae4b Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 11 Mar 2021 19:23:44 +0800 Subject: [PATCH 07/11] chore: add missing renaming --- Mastodon/Diffiable/Section/StatusSection.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 3fc1d28a..00ca16cc 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -201,8 +201,8 @@ extension StatusSection { let videoPlayerViewModel = dependency.context.videoPlaybackService.dequeueVideoPlayerViewModel(for: videoAttachment) { let parent = cell.delegate?.parent() - let mosaicPlayerView = cell.statusView.playerContainerView - let playerViewController = mosaicPlayerView.setupPlayer( + let playerContainerView = cell.statusView.playerContainerView + let playerViewController = playerContainerView.setupPlayer( aspectRatio: videoPlayerViewModel.videoSize, maxSize: playerViewMaxSize, parent: parent @@ -211,8 +211,8 @@ extension StatusSection { playerViewController.player = videoPlayerViewModel.player playerViewController.showsPlaybackControls = videoPlayerViewModel.videoKind != .gif - mosaicPlayerView.gifIndicatorLabel.isHidden = videoPlayerViewModel.videoKind != .gif - mosaicPlayerView.isHidden = false + playerContainerView.gifIndicatorLabel.isHidden = videoPlayerViewModel.videoKind != .gif + playerContainerView.isHidden = false } else { cell.statusView.playerContainerView.playerViewController.player?.pause() From 2e31280819ffdc8b381247cc893cd204cf8d5763 Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Fri, 12 Mar 2021 12:17:07 +0800 Subject: [PATCH 08/11] chore: extract the common blur effect part from MosaicImageViewContainer --- .../Diffiable/Section/StatusSection.swift | 6 +- ...Provider+StatusTableViewCellDelegate.swift | 28 ++---- .../Container/MosaicImageViewContainer.swift | 87 +++++++------------ .../TableviewCell/StatusTableViewCell.swift | 6 +- 4 files changed, 46 insertions(+), 81 deletions(-) diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 00ca16cc..ef313477 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -170,8 +170,8 @@ extension StatusSection { } cell.statusView.statusMosaicImageViewContainer.isHidden = mosiacImageViewModel.metas.isEmpty let isStatusSensitive = statusItemAttribute.isStatusSensitive - cell.statusView.statusMosaicImageViewContainer.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil - cell.statusView.statusMosaicImageViewContainer.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 + cell.statusView.statusMosaicImageViewContainer.contentWarningOverlayView.blurVisualEffectView.effect = isStatusSensitive ? ContentWarningOverlayView.blurVisualEffect : nil + cell.statusView.statusMosaicImageViewContainer.contentWarningOverlayView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 // set audio if let audioAttachment = mediaAttachments.filter({ $0.type == .audio }).first { @@ -193,7 +193,7 @@ extension StatusSection { return CGSize(width: maxWidth, height: maxWidth * scale) }() - cell.statusView.playerContainerView.contentWarningOverlayView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil + cell.statusView.playerContainerView.contentWarningOverlayView.blurVisualEffectView.effect = isStatusSensitive ? ContentWarningOverlayView.blurVisualEffect : nil cell.statusView.playerContainerView.contentWarningOverlayView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0 cell.statusView.playerContainerView.contentWarningOverlayView.isUserInteractionEnabled = isStatusSensitive diff --git a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift index cebc845a..cce031c8 100644 --- a/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/StatusProvider/StatusProvider+StatusTableViewCellDelegate.swift @@ -46,30 +46,16 @@ extension StatusTableViewCellDelegate where Self: StatusProvider { } - func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { - guard let diffableDataSource = self.tableViewDiffableDataSource else { return } - guard let item = item(for: cell, indexPath: nil) else { return } - - switch item { - case .homeTimelineIndex(_, let attribute): - attribute.isStatusSensitive = false - case .toot(_, let attribute): - attribute.isStatusSensitive = false - default: - return - } - - var snapshot = diffableDataSource.snapshot() - snapshot.reloadItems([item]) - UIView.animate(withDuration: 0.33) { - cell.statusView.statusMosaicImageViewContainer.blurVisualEffectView.effect = nil - cell.statusView.statusMosaicImageViewContainer.vibrancyVisualEffectView.alpha = 0.0 - } completion: { _ in - diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil) - } + func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { + statusTableViewCell(cell, contentWarningOverlayViewDidPressed: contentWarningOverlayView) } func statusTableViewCell(_ cell: StatusTableViewCell, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { + contentWarningOverlayView.isUserInteractionEnabled = false + statusTableViewCell(cell, contentWarningOverlayViewDidPressed: contentWarningOverlayView) + } + + func statusTableViewCell(_ cell: StatusTableViewCell, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { guard let diffableDataSource = self.tableViewDiffableDataSource else { return } guard let item = item(for: cell, indexPath: nil) else { return } diff --git a/Mastodon/Scene/Share/View/Container/MosaicImageViewContainer.swift b/Mastodon/Scene/Share/View/Container/MosaicImageViewContainer.swift index 5240d4e2..8e688446 100644 --- a/Mastodon/Scene/Share/View/Container/MosaicImageViewContainer.swift +++ b/Mastodon/Scene/Share/View/Container/MosaicImageViewContainer.swift @@ -15,15 +15,12 @@ protocol MosaicImageViewContainerPresentable: class { protocol MosaicImageViewContainerDelegate: class { func mosaicImageViewContainer(_ mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) - func mosaicImageViewContainer(_ mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) + func mosaicImageViewContainer(_ mosaicImageViewContainer: MosaicImageViewContainer, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) } final class MosaicImageViewContainer: UIView { - static let cornerRadius: CGFloat = 4 - static let blurVisualEffect = UIBlurEffect(style: .systemUltraThinMaterial) - weak var delegate: MosaicImageViewContainerDelegate? let container = UIStackView() @@ -37,14 +34,10 @@ final class MosaicImageViewContainer: UIView { } } } - let blurVisualEffectView = UIVisualEffectView(effect: MosaicImageViewContainer.blurVisualEffect) - let vibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: MosaicImageViewContainer.blurVisualEffect)) - let contentWarningLabel: UILabel = { - let label = UILabel() - label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15)) - label.text = L10n.Common.Controls.Status.mediaContentWarning - label.textAlignment = .center - return label + + let contentWarningOverlayView: ContentWarningOverlayView = { + let contentWarningOverlayView = ContentWarningOverlayView() + return contentWarningOverlayView }() private var containerHeightLayoutConstraint: NSLayoutConstraint! @@ -61,9 +54,16 @@ final class MosaicImageViewContainer: UIView { } +extension MosaicImageViewContainer: ContentWarningOverlayViewDelegate { + func contentWarningOverlayViewDidPressed(_ contentWarningOverlayView: ContentWarningOverlayView) { + self.delegate?.mosaicImageViewContainer(self, contentWarningOverlayViewDidPressed: contentWarningOverlayView) + } +} + extension MosaicImageViewContainer { private func _init() { + contentWarningOverlayView.delegate = self container.translatesAutoresizingMaskIntoConstraints = false container.axis = .horizontal container.distribution = .fillEqually @@ -77,32 +77,13 @@ extension MosaicImageViewContainer { containerHeightLayoutConstraint ]) - // add blur visual effect view in the setup method - blurVisualEffectView.layer.masksToBounds = true - blurVisualEffectView.layer.cornerRadius = MosaicImageViewContainer.cornerRadius - blurVisualEffectView.layer.cornerCurve = .continuous - - vibrancyVisualEffectView.translatesAutoresizingMaskIntoConstraints = false - blurVisualEffectView.contentView.addSubview(vibrancyVisualEffectView) + addSubview(contentWarningOverlayView) NSLayoutConstraint.activate([ - vibrancyVisualEffectView.topAnchor.constraint(equalTo: blurVisualEffectView.topAnchor), - vibrancyVisualEffectView.leadingAnchor.constraint(equalTo: blurVisualEffectView.leadingAnchor), - vibrancyVisualEffectView.trailingAnchor.constraint(equalTo: blurVisualEffectView.trailingAnchor), - vibrancyVisualEffectView.bottomAnchor.constraint(equalTo: blurVisualEffectView.bottomAnchor), + contentWarningOverlayView.topAnchor.constraint(equalTo: container.topAnchor), + contentWarningOverlayView.leadingAnchor.constraint(equalTo: container.leadingAnchor), + contentWarningOverlayView.bottomAnchor.constraint(equalTo: container.bottomAnchor), + contentWarningOverlayView.trailingAnchor.constraint(equalTo: container.trailingAnchor), ]) - - contentWarningLabel.translatesAutoresizingMaskIntoConstraints = false - vibrancyVisualEffectView.contentView.addSubview(contentWarningLabel) - NSLayoutConstraint.activate([ - contentWarningLabel.leadingAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.layoutMarginsGuide.leadingAnchor), - contentWarningLabel.trailingAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.layoutMarginsGuide.trailingAnchor), - contentWarningLabel.centerYAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.centerYAnchor), - ]) - - blurVisualEffectView.isUserInteractionEnabled = true - let tapGesture = UITapGestureRecognizer.singleTapGestureRecognizer - tapGesture.addTarget(self, action: #selector(MosaicImageViewContainer.visualEffectViewTapGestureRecognizerHandler(_:))) - blurVisualEffectView.addGestureRecognizer(tapGesture) } } @@ -117,9 +98,9 @@ extension MosaicImageViewContainer { container.subviews.forEach { subview in subview.removeFromSuperview() } - blurVisualEffectView.removeFromSuperview() - blurVisualEffectView.effect = MosaicImageViewContainer.blurVisualEffect - vibrancyVisualEffectView.alpha = 1.0 + contentWarningOverlayView.removeFromSuperview() + contentWarningOverlayView.blurVisualEffectView.effect = ContentWarningOverlayView.blurVisualEffect + contentWarningOverlayView.vibrancyVisualEffectView.alpha = 1.0 imageViews = [] container.spacing = 1 @@ -140,7 +121,7 @@ extension MosaicImageViewContainer { let imageView = UIImageView() imageViews.append(imageView) imageView.layer.masksToBounds = true - imageView.layer.cornerRadius = MosaicImageViewContainer.cornerRadius + imageView.layer.cornerRadius = ContentWarningOverlayView.cornerRadius imageView.layer.cornerCurve = .continuous imageView.contentMode = .scaleAspectFill @@ -155,13 +136,12 @@ extension MosaicImageViewContainer { containerHeightLayoutConstraint.constant = floor(rect.height) containerHeightLayoutConstraint.isActive = true - blurVisualEffectView.translatesAutoresizingMaskIntoConstraints = false - addSubview(blurVisualEffectView) + addSubview(contentWarningOverlayView) NSLayoutConstraint.activate([ - blurVisualEffectView.topAnchor.constraint(equalTo: imageView.topAnchor), - blurVisualEffectView.leadingAnchor.constraint(equalTo: imageView.leadingAnchor), - blurVisualEffectView.trailingAnchor.constraint(equalTo: imageView.trailingAnchor), - blurVisualEffectView.bottomAnchor.constraint(equalTo: imageView.bottomAnchor), + contentWarningOverlayView.topAnchor.constraint(equalTo: imageView.topAnchor), + contentWarningOverlayView.leadingAnchor.constraint(equalTo: imageView.leadingAnchor), + contentWarningOverlayView.trailingAnchor.constraint(equalTo: imageView.trailingAnchor), + contentWarningOverlayView.bottomAnchor.constraint(equalTo: imageView.bottomAnchor), ]) return imageView @@ -193,7 +173,7 @@ extension MosaicImageViewContainer { self.imageViews.append(contentsOf: imageViews) imageViews.forEach { imageView in imageView.layer.masksToBounds = true - imageView.layer.cornerRadius = MosaicImageViewContainer.cornerRadius + imageView.layer.cornerRadius = ContentWarningOverlayView.cornerRadius imageView.layer.cornerCurve = .continuous imageView.contentMode = .scaleAspectFill } @@ -242,13 +222,12 @@ extension MosaicImageViewContainer { } } - blurVisualEffectView.translatesAutoresizingMaskIntoConstraints = false - addSubview(blurVisualEffectView) + addSubview(contentWarningOverlayView) NSLayoutConstraint.activate([ - blurVisualEffectView.topAnchor.constraint(equalTo: container.topAnchor), - blurVisualEffectView.leadingAnchor.constraint(equalTo: container.leadingAnchor), - blurVisualEffectView.trailingAnchor.constraint(equalTo: container.trailingAnchor), - blurVisualEffectView.bottomAnchor.constraint(equalTo: container.bottomAnchor), + contentWarningOverlayView.topAnchor.constraint(equalTo: container.topAnchor), + contentWarningOverlayView.leadingAnchor.constraint(equalTo: container.leadingAnchor), + contentWarningOverlayView.trailingAnchor.constraint(equalTo: container.trailingAnchor), + contentWarningOverlayView.bottomAnchor.constraint(equalTo: container.bottomAnchor), ]) return imageViews @@ -260,7 +239,7 @@ extension MosaicImageViewContainer { @objc private func visualEffectViewTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) { os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) - delegate?.mosaicImageViewContainer(self, didTapContentWarningVisualEffectView: blurVisualEffectView) + delegate?.mosaicImageViewContainer(self, contentWarningOverlayViewDidPressed: contentWarningOverlayView) } @objc private func photoTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) { diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift index e8d986bd..bc301b71 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift @@ -22,7 +22,7 @@ protocol StatusTableViewCellDelegate: class { func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, contentWarningActionButtonPressed button: UIButton) - func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) + func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) func statusTableViewCell(_ cell: StatusTableViewCell, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton) @@ -215,8 +215,8 @@ extension StatusTableViewCell: MosaicImageViewContainerDelegate { delegate?.statusTableViewCell(self, mosaicImageViewContainer: mosaicImageViewContainer, didTapImageView: imageView, atIndex: index) } - func mosaicImageViewContainer(_ mosaicImageViewContainer: MosaicImageViewContainer, didTapContentWarningVisualEffectView visualEffectView: UIVisualEffectView) { - delegate?.statusTableViewCell(self, mosaicImageViewContainer: mosaicImageViewContainer, didTapContentWarningVisualEffectView: visualEffectView) + func mosaicImageViewContainer(_ mosaicImageViewContainer: MosaicImageViewContainer, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { + delegate?.statusTableViewCell(self, mosaicImageViewContainer: mosaicImageViewContainer, contentWarningOverlayViewDidPressed: contentWarningOverlayView) } } From 6b5edff677aa13ebb396ad071184ff80f876a8bb Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Fri, 12 Mar 2021 15:41:57 +0800 Subject: [PATCH 09/11] chore: add media type with gif and video --- .../Diffiable/Section/StatusSection.swift | 3 +- Mastodon/Generated/Assets.swift | 1 + .../mediaTypeIndicotor.colorset/Contents.json | 20 ++++++ .../View/Container/PlayerContainerView.swift | 68 +++++++++++++------ 4 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 Mastodon/Resources/Assets.xcassets/Colors/Background/mediaTypeIndicotor.colorset/Contents.json diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index ef313477..aa6a89b9 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -210,8 +210,7 @@ extension StatusSection { playerViewController.delegate = cell.delegate?.playerViewControllerDelegate playerViewController.player = videoPlayerViewModel.player playerViewController.showsPlaybackControls = videoPlayerViewModel.videoKind != .gif - - playerContainerView.gifIndicatorLabel.isHidden = videoPlayerViewModel.videoKind != .gif + playerContainerView.setMediaKind(kind: videoPlayerViewModel.videoKind) playerContainerView.isHidden = false } else { diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift index f6817046..a760c40b 100644 --- a/Mastodon/Generated/Assets.swift +++ b/Mastodon/Generated/Assets.swift @@ -37,6 +37,7 @@ internal enum Asset { internal static let disabled = ColorAsset(name: "Colors/Background/Poll/disabled") internal static let highlight = ColorAsset(name: "Colors/Background/Poll/highlight") } + internal static let mediaTypeIndicotor = ColorAsset(name: "Colors/Background/mediaTypeIndicotor") internal static let onboardingBackground = ColorAsset(name: "Colors/Background/onboarding.background") internal static let secondaryGroupedSystemBackground = ColorAsset(name: "Colors/Background/secondary.grouped.system.background") internal static let secondarySystemBackground = ColorAsset(name: "Colors/Background/secondary.system.background") diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Background/mediaTypeIndicotor.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Background/mediaTypeIndicotor.colorset/Contents.json new file mode 100644 index 00000000..e9c583c0 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Colors/Background/mediaTypeIndicotor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.600", + "blue" : "0", + "green" : "0", + "red" : "0" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift index aa60bacd..d002e13a 100644 --- a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift +++ b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift @@ -26,14 +26,28 @@ final class PlayerContainerView: UIView { let playerViewController = AVPlayerViewController() - let gifIndicatorLabel: UILabel = { + let mediaTypeIndicotorLabel: UILabel = { let label = UILabel() - label.font = .systemFont(ofSize: 16, weight: .heavy) - label.text = "GIF" + label.font = .systemFont(ofSize: 18, weight: .heavy) 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 + }() + weak var delegate: PlayerContainerViewDelegate? override init(frame: CGRect) { @@ -60,14 +74,6 @@ extension PlayerContainerView { containerHeightLayoutConstraint, ]) - addSubview(gifIndicatorLabel) - gifIndicatorLabel.translatesAutoresizingMaskIntoConstraints = false - - NSLayoutConstraint.activate([ - gifIndicatorLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 4), - gifIndicatorLabel.trailingAnchor.constraint(equalTo: trailingAnchor) - ]) - // will not influence full-screen playback playerViewController.view.layer.masksToBounds = true playerViewController.view.layer.cornerRadius = PlayerContainerView.cornerRadius @@ -80,8 +86,24 @@ extension PlayerContainerView { contentWarningOverlayView.trailingAnchor.constraint(equalTo: trailingAnchor), contentWarningOverlayView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) - contentWarningOverlayView.delegate = self + + // mediaType + addSubview(mediaTypeIndicotorView) + 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) + ]) } } @@ -96,8 +118,6 @@ extension PlayerContainerView { func reset() { // note: set playerViewController.player pause() and nil in data source configuration process make reloadData not break playing - gifIndicatorLabel.removeFromSuperview() - playerViewController.willMove(toParent: nil) playerViewController.view.removeFromSuperview() playerViewController.removeFromParent() @@ -137,13 +157,21 @@ extension PlayerContainerView { containerHeightLayoutConstraint.constant = floor(rect.height) containerHeightLayoutConstraint.isActive = true - gifIndicatorLabel.translatesAutoresizingMaskIntoConstraints = false - touchBlockingView.addSubview(gifIndicatorLabel) - NSLayoutConstraint.activate([ - touchBlockingView.trailingAnchor.constraint(equalTo: gifIndicatorLabel.trailingAnchor, constant: 8), - touchBlockingView.bottomAnchor.constraint(equalTo: gifIndicatorLabel.bottomAnchor, constant: 8), - ]) + bringSubviewToFront(mediaTypeIndicotorView) return playerViewController } + + func setMediaKind(kind: VideoPlayerViewModel.Kind) { + switch kind { + case .gif: + mediaTypeIndicotorLabel.text = "GIF" + case .video: + let configuration = UIImage.SymbolConfiguration(font: .systemFont(ofSize: 18, weight: .regular)) + let image = UIImage(systemName: "video.fill", withConfiguration: configuration)! + let attachment = NSTextAttachment() + attachment.image = image.withTintColor(.white) + mediaTypeIndicotorLabel.attributedText = NSAttributedString(attachment: attachment) + } + } } From 0c164a170cfb81c94374871c2edcb626a2e21a54 Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Fri, 12 Mar 2021 15:53:19 +0800 Subject: [PATCH 10/11] chore: use rounded font --- .../Share/View/Container/PlayerContainerView.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift index d002e13a..3b4acc98 100644 --- a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift +++ b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift @@ -28,7 +28,6 @@ final class PlayerContainerView: UIView { let mediaTypeIndicotorLabel: UILabel = { let label = UILabel() - label.font = .systemFont(ofSize: 18, weight: .heavy) label.textColor = .white label.textAlignment = .right label.translatesAutoresizingMaskIntoConstraints = false @@ -162,12 +161,21 @@ 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: .systemFont(ofSize: 18, weight: .regular)) + 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) From 20283d187879a428f6ebb11d75fa2f0c2dfc7936 Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Mon, 15 Mar 2021 15:08:58 +0800 Subject: [PATCH 11/11] chore: change video and audio notification string --- Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift | 2 +- Mastodon/Service/AudioPlaybackService.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift b/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift index 3fa24148..d34e73eb 100644 --- a/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift +++ b/Mastodon/Scene/Share/ViewModel/VideoPlayerViewModel.swift @@ -14,7 +14,7 @@ import UIKit final class VideoPlayerViewModel { var disposeBag = Set() - static let appWillPlayVideoNotification = NSNotification.Name(rawValue: "org.joinmastodon.Mastodon.VideoPlayerViewModel.appWillPlayVideo") + static let appWillPlayVideoNotification = NSNotification.Name(rawValue: "org.joinmastodon.Mastodon.video-playback-service.appWillPlayVideo") // input let previewImageURL: URL? let videoURL: URL diff --git a/Mastodon/Service/AudioPlaybackService.swift b/Mastodon/Service/AudioPlaybackService.swift index 314f3964..34ceb3bb 100644 --- a/Mastodon/Service/AudioPlaybackService.swift +++ b/Mastodon/Service/AudioPlaybackService.swift @@ -14,7 +14,7 @@ import os.log final class AudioPlaybackService: NSObject { - static let appWillPlayAudioNotification = NSNotification.Name(rawValue: "org.joinmastodon.Mastodon.AudioPlayer.appWillPlayAudio") + static let appWillPlayAudioNotification = NSNotification.Name(rawValue: "org.joinmastodon.Mastodon.audio-playback-service.appWillPlayAudio") var disposeBag = Set()