diff --git a/BeeReel.xcodeproj/project.pbxproj b/BeeReel.xcodeproj/project.pbxproj index 78a5cbb..ad3a536 100644 --- a/BeeReel.xcodeproj/project.pbxproj +++ b/BeeReel.xcodeproj/project.pbxproj @@ -8,6 +8,16 @@ /* Begin PBXBuildFile section */ 440A41A6E6A22A02807AE759 /* Pods_BeeReel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 899B3015B03D5E1A5A6507EB /* Pods_BeeReel.framework */; }; + BF02B7E12E2DE64200172177 /* BRVideoProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7E02E2DE64200172177 /* BRVideoProgressView.swift */; }; + BF02B7E32E2E08BD00172177 /* BRDetailEpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7E22E2E08BD00172177 /* BRDetailEpButton.swift */; }; + BF02B7E52E2E1E6100172177 /* BREpisodeSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7E42E2E1E6100172177 /* BREpisodeSelectorView.swift */; }; + BF02B7E72E2E1F0500172177 /* BRPanModalContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7E62E2E1F0500172177 /* BRPanModalContentView.swift */; }; + BF02B7E92E2E29E900172177 /* BREpisodeSelectorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7E82E2E29E900172177 /* BREpisodeSelectorCell.swift */; }; + BF02B7EB2E2E388800172177 /* BREpisodeMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7EA2E2E388800172177 /* BREpisodeMenuView.swift */; }; + BF02B7ED2E2E390500172177 /* BRScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7EC2E2E390500172177 /* BRScrollView.swift */; }; + BF02B7EF2E2E4BFD00172177 /* BRRateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7EE2E2E4BFD00172177 /* BRRateModel.swift */; }; + BF02B7F12E2E55E300172177 /* BRRateSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7F02E2E55E300172177 /* BRRateSelectorView.swift */; }; + BF02B7F32E2E571600172177 /* BRRateSelectorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B7F22E2E571600172177 /* BRRateSelectorCell.swift */; }; BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */; }; BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */; }; BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */; }; @@ -113,6 +123,16 @@ /* Begin PBXFileReference section */ 86290EBFA8B93C91B3BAD835 /* Pods-ShortBox.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShortBox.debug.xcconfig"; path = "Target Support Files/Pods-ShortBox/Pods-ShortBox.debug.xcconfig"; sourceTree = ""; }; 899B3015B03D5E1A5A6507EB /* Pods_BeeReel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BeeReel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BF02B7E02E2DE64200172177 /* BRVideoProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoProgressView.swift; sourceTree = ""; }; + BF02B7E22E2E08BD00172177 /* BRDetailEpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRDetailEpButton.swift; sourceTree = ""; }; + BF02B7E42E2E1E6100172177 /* BREpisodeSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BREpisodeSelectorView.swift; sourceTree = ""; }; + BF02B7E62E2E1F0500172177 /* BRPanModalContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPanModalContentView.swift; sourceTree = ""; }; + BF02B7E82E2E29E900172177 /* BREpisodeSelectorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BREpisodeSelectorCell.swift; sourceTree = ""; }; + BF02B7EA2E2E388800172177 /* BREpisodeMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BREpisodeMenuView.swift; sourceTree = ""; }; + BF02B7EC2E2E390500172177 /* BRScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRScrollView.swift; sourceTree = ""; }; + BF02B7EE2E2E4BFD00172177 /* BRRateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRRateModel.swift; sourceTree = ""; }; + BF02B7F02E2E55E300172177 /* BRRateSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRRateSelectorView.swift; sourceTree = ""; }; + BF02B7F22E2E571600172177 /* BRRateSelectorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRRateSelectorCell.swift; sourceTree = ""; }; BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBar.swift; sourceTree = ""; }; BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+BRAdd.swift"; sourceTree = ""; }; BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerCache.swift; sourceTree = ""; }; @@ -525,6 +545,13 @@ BF3338EB2E154BFE00B10F76 /* BRPlayerControlView.swift */, BF3338F62E16176900B10F76 /* BRDetailPlayerCell.swift */, BF3338F82E16178700B10F76 /* BRDetailControlView.swift */, + BF02B7E02E2DE64200172177 /* BRVideoProgressView.swift */, + BF02B7E22E2E08BD00172177 /* BRDetailEpButton.swift */, + BF02B7E42E2E1E6100172177 /* BREpisodeSelectorView.swift */, + BF02B7E82E2E29E900172177 /* BREpisodeSelectorCell.swift */, + BF02B7EA2E2E388800172177 /* BREpisodeMenuView.swift */, + BF02B7F02E2E55E300172177 /* BRRateSelectorView.swift */, + BF02B7F22E2E571600172177 /* BRRateSelectorCell.swift */, ); path = View; sourceTree = ""; @@ -535,6 +562,7 @@ BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */, BFC676802E122733006659E5 /* BRPlayerProtocol.swift */, BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */, + BF02B7EE2E2E4BFD00172177 /* BRRateModel.swift */, ); path = Model; sourceTree = ""; @@ -557,6 +585,8 @@ BFC6766C2E0E3A8D006659E5 /* BRImageView.swift */, BFC676722E0E938B006659E5 /* BRTableView.swift */, BFC676742E0E93B3006659E5 /* BRTableViewCell.swift */, + BF02B7E62E2E1F0500172177 /* BRPanModalContentView.swift */, + BF02B7EC2E2E390500172177 /* BRScrollView.swift */, ); path = View; sourceTree = ""; @@ -845,6 +875,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BF02B7F32E2E571600172177 /* BRRateSelectorCell.swift in Sources */, BFC676992E1280E3006659E5 /* BRSpotlightRecommandCell.swift in Sources */, BFC676A42E129D60006659E5 /* BRHomeTop10Cell.swift in Sources */, BF692B3C2E0A8D0200A5C2DA /* BRNavigationController.swift in Sources */, @@ -862,14 +893,17 @@ BF692B242E0A825B00A5C2DA /* BRCryptorService.swift in Sources */, BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */, BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */, + BF02B7E12E2DE64200172177 /* BRVideoProgressView.swift in Sources */, BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */, BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */, BF692B472E0A9B7900A5C2DA /* BRPlayer.swift in Sources */, BF692B6E2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift in Sources */, BF692AFA2E0A6F0900A5C2DA /* BRNetwork.swift in Sources */, + BF02B7E52E2E1E6100172177 /* BREpisodeSelectorView.swift in Sources */, BF692B6B2E0BC85300A5C2DA /* BRHomeViewController.swift in Sources */, BF692B7C2E0D3C1300A5C2DA /* BRVideoInfoModel.swift in Sources */, BFC6766D2E0E3A8D006659E5 /* BRImageView.swift in Sources */, + BF02B7ED2E2E390500172177 /* BRScrollView.swift in Sources */, BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */, BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */, BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */, @@ -901,7 +935,9 @@ BF3338FD2E1626B000B10F76 /* BRPlayerControlProtocol.swift in Sources */, BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */, BF692B1F2E0A804600A5C2DA /* BRLocalizedManager.swift in Sources */, + BF02B7E92E2E29E900172177 /* BREpisodeSelectorCell.swift in Sources */, BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */, + BF02B7F12E2E55E300172177 /* BRRateSelectorView.swift in Sources */, BF692B012E0A74A200A5C2DA /* BRDefine.swift in Sources */, BFC6767B2E0E973B006659E5 /* UIStackView+BRAdd.swift in Sources */, BF3338F32E16169A00B10F76 /* BRExplorePlayerCell.swift in Sources */, @@ -910,6 +946,7 @@ BFC676892E122FDD006659E5 /* BRVideoAPI.swift in Sources */, BFC6769B2E1285C5006659E5 /* BRPagerViewTransformer.swift in Sources */, BFC676832E122CC5006659E5 /* BRPlayerViewModel.swift in Sources */, + BF02B7EF2E2E4BFD00172177 /* BRRateModel.swift in Sources */, BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */, BF3338FB2E161CF900B10F76 /* NSNumber+BRAdd.swift in Sources */, BF692B222E0A820D00A5C2DA /* String+BRAdd.swift in Sources */, @@ -924,7 +961,9 @@ BFC676972E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift in Sources */, BFC6767F2E121A72006659E5 /* BRSpotlightHotCell.swift in Sources */, BFC6767D2E0E9809006659E5 /* BRSpotlightHotMainCell.swift in Sources */, + BF02B7EB2E2E388800172177 /* BREpisodeMenuView.swift in Sources */, BFC676662E0E2C8E006659E5 /* WMZBannerFadeLayout.m in Sources */, + BF02B7E72E2E1F0500172177 /* BRPanModalContentView.swift in Sources */, BFC676872E122E36006659E5 /* BRVideoDetailModel.swift in Sources */, BFC676672E0E2C8E006659E5 /* WMZBannerParam.m in Sources */, BF692B732E0D397700A5C2DA /* BRHomeAPI.swift in Sources */, @@ -933,6 +972,7 @@ BF3338F02E15569600B10F76 /* BRExploreViewController.swift in Sources */, BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */, BF692B562E0AA92100A5C2DA /* BRCollectionViewCell.swift in Sources */, + BF02B7E32E2E08BD00172177 /* BRDetailEpButton.swift in Sources */, BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */, BF692B752E0D39D000A5C2DA /* BRListModel.swift in Sources */, BFC676B92E1385FC006659E5 /* BRPopularPicksSmallCell.swift in Sources */, diff --git a/BeeReel/Base/Extension/UIColor+BRAdd.swift b/BeeReel/Base/Extension/UIColor+BRAdd.swift index 29833ca..7fb2629 100644 --- a/BeeReel/Base/Extension/UIColor+BRAdd.swift +++ b/BeeReel/Base/Extension/UIColor+BRAdd.swift @@ -66,4 +66,12 @@ extension UIColor { static func colorFFFDF9(alpha: CGFloat = 1) -> UIColor { return UIColor(rgb: 0xFFFDF9, alpha: alpha) } + + static func colorDDDDDD(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xDDDDDD, alpha: alpha) + } + + static func color747474(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0x747474, alpha: alpha) + } } diff --git a/BeeReel/Base/Extension/UIView+BRAdd.swift b/BeeReel/Base/Extension/UIView+BRAdd.swift index 5b31f6a..136b95d 100644 --- a/BeeReel/Base/Extension/UIView+BRAdd.swift +++ b/BeeReel/Base/Extension/UIView+BRAdd.swift @@ -42,7 +42,7 @@ extension UIView { } } ///设置圆角 - func setRoundedCorner(topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat) { + func br_setRoundedCorner(topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat) { //清空其它设置方法 self.roundedCorner = BRRoundedCorner(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight) _updateRoundedCorner() diff --git a/BeeReel/Base/Network/Base/BRURLPath.swift b/BeeReel/Base/Network/Base/BRURLPath.swift index 5e3b49a..014249f 100644 --- a/BeeReel/Base/Network/Base/BRURLPath.swift +++ b/BeeReel/Base/Network/Base/BRURLPath.swift @@ -7,11 +7,11 @@ -let BRBaseURL = "https://api-qjwl168.qjwl168.com" -let BRURLPathPrefix = "/velo" +let BRBaseURL = "https://api-breeltv.breeltv.com" +let BRURLPathPrefix = "/reel" -let BRWebBaseURL = "https://www.qjwl168.com" -let BRCampaignWebURL = "https://campaign.qjwl168.com" +let BRWebBaseURL = "https://www.breeltv.com" +let BRCampaignWebURL = "https://campaign.breeltv.com" ///用户协议 diff --git a/BeeReel/Base/View/BRPanModalContentView.swift b/BeeReel/Base/View/BRPanModalContentView.swift new file mode 100644 index 0000000..53c6954 --- /dev/null +++ b/BeeReel/Base/View/BRPanModalContentView.swift @@ -0,0 +1,90 @@ +// +// BRPanModalContentView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit +import HWPanModal + +class BRPanModalContentView: HWPanModalContentView { + + var contentHeight = UIScreen.height * (2 / 3) + + var mainScrollView: UIScrollView? + + ///更新UI contentSize发生变化时调用 + func setNeedsLayoutUpdate() { + self.panModalSetNeedsLayoutUpdate() + } + + private(set) lazy var topImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "highlight_top_image")) + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .color1C1C1C() + + addSubview(topImageView) + + topImageView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.centerX.equalToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //MARK: HWPanModalPresentable + override func panScrollable() -> UIScrollView? { + return mainScrollView + } + + override func longFormHeight() -> PanModalHeight { + return PanModalHeightMake(.content, contentHeight) + } + + override func showDragIndicator() -> Bool { + return false + } + + override func backgroundConfig() -> HWBackgroundConfig { + let config = HWBackgroundConfig() + config.backgroundAlpha = 0.0 + return config + } + + override func allowsDragToDismiss() -> Bool { + return false + } + + ///点击空白处关闭 + override func allowsTapBackgroundToDismiss() -> Bool { + return true + } + + override func allowsPullDownWhenShortState() -> Bool { + return false + } + + override func minVerticalVelocityToTriggerDismiss() -> CGFloat { + return 0 + } + + override func showsScrollableVerticalScrollIndicator() -> Bool { + return false + } + + override func springDamping() -> CGFloat { + return 1 + } + + override func cornerRadius() -> CGFloat { + return 24 + } +} diff --git a/BeeReel/Base/View/BRScrollView.swift b/BeeReel/Base/View/BRScrollView.swift new file mode 100644 index 0000000..9ae0857 --- /dev/null +++ b/BeeReel/Base/View/BRScrollView.swift @@ -0,0 +1,21 @@ +// +// BRScrollView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BRScrollView: UIScrollView { + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentInsetAdjustmentBehavior = .never + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/BeeReel/Base/View/TabBar/BRTabBar.swift b/BeeReel/Base/View/TabBar/BRTabBar.swift index 9e4b461..c5301e1 100644 --- a/BeeReel/Base/View/TabBar/BRTabBar.swift +++ b/BeeReel/Base/View/TabBar/BRTabBar.swift @@ -32,7 +32,7 @@ class BRTabBar: UITabBar { override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .color1C1C1C() - self.setRoundedCorner(topLeft: 30, topRight: 30, bottomLeft: 0, bottomRight: 0) + self.br_setRoundedCorner(topLeft: 30, topRight: 30, bottomLeft: 0, bottomRight: 0) addSubview(topImageView) diff --git a/BeeReel/Class/Explore/Controller/BRExploreViewController.swift b/BeeReel/Class/Explore/Controller/BRExploreViewController.swift index d5f9800..0c6d9d3 100644 --- a/BeeReel/Class/Explore/Controller/BRExploreViewController.swift +++ b/BeeReel/Class/Explore/Controller/BRExploreViewController.swift @@ -47,14 +47,18 @@ class BRExploreViewController: BRPlayerListViewController { button.setImage(UIImage(named: "episode_icon_01"), for: .normal) button.setContentHuggingPriority(.required, for: .horizontal) button.setContentCompressionResistancePriority(.required, for: .horizontal) + button.addTarget(self, action: #selector(handleEpisodeButton), for: .touchUpInside) return button }() private lazy var favoriteButton: UIButton = { let button = UIButton(type: .custom) button.setImage(UIImage(named: "favorite_icon_02"), for: .normal) + button.setImage(UIImage(named: "favorite_icon_02_selected"), for: .selected) + button.setImage(UIImage(named: "favorite_icon_02_selected"), for: [.selected, .highlighted]) button.setContentHuggingPriority(.required, for: .horizontal) button.setContentCompressionResistancePriority(.required, for: .horizontal) + button.addTarget(self, action: #selector(handleFavoriteButton), for: .touchUpInside) return button }() @@ -143,18 +147,42 @@ extension BRExploreViewController { } +extension BRExploreViewController { + + @objc private func handleFavoriteButton() { + let shortModel = self.viewModel.currentPlayer?.shortModel + guard let shortPlayId = shortModel?.short_play_id else { return } + + let isFavorite = !(shortModel?.is_collect ?? false) + + BRVideoAPI.requestFavorite(isFavorite: isFavorite, shortPlayId: shortPlayId, videoId: shortModel?.short_play_video_id) { + + } + + } + + @objc private func handleEpisodeButton() { + let vc = BRVideoDetailViewController() + vc.shortPlayId = self.viewModel.currentPlayer?.shortModel?.short_play_id + self.navigationController?.pushViewController(vc, animated: true) + } + +} + //MARK: -------------- BRPlayerListViewControllerDelegate BRPlayerListViewControllerDataSource -------------- extension BRExploreViewController: BRPlayerListViewControllerDelegate, BRPlayerListViewControllerDataSource { - func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell { + func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let model = dataArr[indexPath.row] - if let cell = oldCell as? BRPlayerListCell { - cell.shortModel = model - cell.videoInfo = model.video_info - } - return oldCell + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRExplorePlayerCell + cell.videoInfo = model.video_info + cell.shortModel = model + cell.didChangeFavoriteState = { [weak self] in + self?.favoriteButton.isSelected = self?.viewModel.currentPlayer?.shortModel?.is_collect ?? true + } + return cell } func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { @@ -163,6 +191,7 @@ extension BRExploreViewController: BRPlayerListViewControllerDelegate, BRPlayerL func br_playerListViewController(_ viewController: BRPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) { videoNameLabel.text = self.viewModel.currentPlayer?.shortModel?.name + favoriteButton.isSelected = self.viewModel.currentPlayer?.shortModel?.is_collect ?? true } } @@ -176,9 +205,11 @@ extension BRExploreViewController { self.collectionView.isHidden = false if let listModel = listModel, let list = listModel.list { if page == 1 { - self.dataArr = list self.clearData() - self.reloadData() + self.dataArr = list + self.reloadData { [weak self] in + self?.play() + } } else { self.addDataArr(dataArr: list) } diff --git a/BeeReel/Class/Explore/View/BRExplorePlayerCell.swift b/BeeReel/Class/Explore/View/BRExplorePlayerCell.swift index e908931..0e93c49 100644 --- a/BeeReel/Class/Explore/View/BRExplorePlayerCell.swift +++ b/BeeReel/Class/Explore/View/BRExplorePlayerCell.swift @@ -13,5 +13,30 @@ class BRExplorePlayerCell: BRPlayerListCell { return BRExploreControlView.self } + var didChangeFavoriteState: (() -> Void)? + + deinit { + NotificationCenter.default.removeObserver(self) + } + + override init(frame: CGRect) { + super.init(frame: frame) + NotificationCenter.default.addObserver(self, selector: #selector(updateShortFavoriteStateNotification), name: BRVideoAPI.updateShortFavoriteStateNotification, object: nil) + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func updateShortFavoriteStateNotification(sender: Notification) { + guard let userInfo = sender.userInfo else { return } + guard let shortPlayId = userInfo["id"] as? String else { return } + guard let state = userInfo["state"] as? Bool else { return } + guard shortPlayId == self.shortModel?.short_play_id else { return } + + self.shortModel?.is_collect = state + self.didChangeFavoriteState?() + + } } diff --git a/BeeReel/Class/Player/Controller/BRPlayerListViewController.swift b/BeeReel/Class/Player/Controller/BRPlayerListViewController.swift index 1fc25c9..a64ef70 100644 --- a/BeeReel/Class/Player/Controller/BRPlayerListViewController.swift +++ b/BeeReel/Class/Player/Controller/BRPlayerListViewController.swift @@ -29,10 +29,12 @@ import SJMediaCacheServer @objc protocol BRPlayerListViewControllerDataSource { - func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell + func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int + @objc optional func br_numberOfSections(in viewController: BRPlayerListViewController) -> Int + } class BRPlayerListViewController: BRViewController { @@ -75,7 +77,7 @@ class BRPlayerListViewController: BRViewController { collectionView.showsHorizontalScrollIndicator = false collectionView.bounces = false collectionView.scrollsToTop = false - collectionView.register(CellClass.self, forCellWithReuseIdentifier: "playerCell") + collectionView.register(CellClass.self, forCellWithReuseIdentifier: "cell") return collectionView }() @@ -88,6 +90,7 @@ class BRPlayerListViewController: BRViewController { override func viewDidLoad() { super.viewDidLoad() self.statusBarStyle = .lightContent + self.view.backgroundColor = .color1C1C1C() NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willResignActiveNotification), name: UIApplication.willResignActiveNotification, object: nil) @@ -201,9 +204,11 @@ extension BRPlayerListViewController { extension BRPlayerListViewController: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "playerCell", for: indexPath) - if let newCell = self.dataSource?.br_playerListViewController(self, collectionView, cellForItemAt: indexPath, oldCell: cell) { + var cell: UICollectionViewCell + if let newCell = self.dataSource?.br_playerListViewController(self, collectionView, cellForItemAt: indexPath) { cell = newCell + } else { + cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) } if let cell = cell as? BRPlayerListCell { if cell.viewModel == nil { @@ -238,6 +243,14 @@ extension BRPlayerListViewController: UICollectionViewDelegate, UICollectionView } } + func numberOfSections(in collectionView: UICollectionView) -> Int { + if let num = self.dataSource?.br_numberOfSections?(in: self) { + return num + } else { + return 1 + } + } + //滑动停止 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { scrollDidEnd(scrollView) @@ -293,6 +306,14 @@ extension BRPlayerListViewController: BRPlayerViewModelDelegate { } } + func br_onEpisodeView(viewModel: BRPlayerViewModel) { + + } + + func br_clickRateButton(viewModel: BRPlayerViewModel) { + + } + } extension BRPlayerListViewController { diff --git a/BeeReel/Class/Player/Controller/BRVideoDetailViewController.swift b/BeeReel/Class/Player/Controller/BRVideoDetailViewController.swift index 50cdaa6..15f6548 100644 --- a/BeeReel/Class/Player/Controller/BRVideoDetailViewController.swift +++ b/BeeReel/Class/Player/Controller/BRVideoDetailViewController.swift @@ -21,8 +21,7 @@ class BRVideoDetailViewController: BRPlayerListViewController { var shortPlayId: String? var activityId: String? - private var detailModel: BRVideoDetailModel? - + private var detailArr: [BRVideoDetailModel] = [] //MARK: UI属性 private lazy var backButton: UIButton = { @@ -51,13 +50,15 @@ class BRVideoDetailViewController: BRPlayerListViewController { override var previousVideoUrl: String? { let index = self.viewModel.currentIndexPath.row - 1 guard index > 0 else { return nil } - return detailModel?.episodeList?[index].video_url + let model = self.detailArr[self.viewModel.currentIndexPath.section] + return model.episodeList?[index].video_url } override var nextVideoUrl: String? { let index = self.viewModel.currentIndexPath.row + 1 - guard index < (detailModel?.episodeList?.count ?? 0) else { return nil } - return detailModel?.episodeList?[index].video_url + let model = self.detailArr[self.viewModel.currentIndexPath.section] + guard index < (model.episodeList?.count ?? 0) else { return nil } + return model.episodeList?[index].video_url } } @@ -75,25 +76,56 @@ extension BRVideoDetailViewController { } - +//MARK: -------------- BRPlayerViewModelDelegate -------------- +//重写父类的代理 +extension BRVideoDetailViewController { + ///打开选集 + override func br_onEpisodeView(viewModel: BRPlayerViewModel) { + let indexPath = self.viewModel.currentIndexPath + let model = self.detailArr[indexPath.section] + + let view = BREpisodeSelectorView() + view.shortModel = model.shortPlayInfo + view.epList = model.episodeList ?? [] + view.index = viewModel.currentIndexPath.row + view.didSelectedIndex = { [weak self] index in + self?.scrollToItem(indexPath: IndexPath(row: index, section: indexPath.section), animated: false) + } + view.present(in: nil) + } + + override func br_clickRateButton(viewModel: BRPlayerViewModel) { + + let view = BRRateSelectorView() + view.didSelectedRate = { [weak self] model in + guard let self = self else { return } + self.viewModel.rateModel = model + } + view.show() + } +} //MARK: -------------- BRPlayerListViewControllerDataSource BRPlayerListViewControllerDelegate -------------- extension BRVideoDetailViewController: BRPlayerListViewControllerDataSource, BRPlayerListViewControllerDelegate { - func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell { + func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - if let cell = oldCell as? BRPlayerListCell { - cell.videoInfo = self.detailModel?.episodeList?[indexPath.row] - cell.shortModel = self.detailModel?.shortPlayInfo - } + let model = self.detailArr[indexPath.section] - return oldCell + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRDetailPlayerCell + cell.videoInfo = model.episodeList?[indexPath.row] + cell.shortModel = model.shortPlayInfo + + return cell } func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return self.detailModel?.episodeList?.count ?? 0 + return self.detailArr[section].episodeList?.count ?? 0 } + func br_numberOfSections(in viewController: BRPlayerListViewController) -> Int { + return self.detailArr.count + } } extension BRVideoDetailViewController { @@ -110,7 +142,8 @@ extension BRVideoDetailViewController { guard let self = self else { return } guard let model = model else { return } - self.detailModel = model + self.detailArr.removeAll() + self.detailArr.append(model) self.reloadData { [weak self] in guard let self = self else { return } diff --git a/BeeReel/Class/Player/Model/BRRateModel.swift b/BeeReel/Class/Player/Model/BRRateModel.swift new file mode 100644 index 0000000..b1e41c2 --- /dev/null +++ b/BeeReel/Class/Player/Model/BRRateModel.swift @@ -0,0 +1,74 @@ +// +// BRRateModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BRRateModel: NSObject { + + enum Rate: String { + case x0_25 = "0.25" + case x0_5 = "0.5" + case x0_75 = "0.75" + case x1 = "1.0" + case x1_25 = "1.25" + case x1_5 = "1.5" + case x1_75 = "1.75" + case x2 = "2.0" + + func getRate() -> Float { + switch self { + case .x0_25: + return 0.25 + + case .x0_5: + return 0.5 + + case .x0_75: + return 0.75 + + case .x1: + return 1 + + case .x1_25: + return 1.25 + + case .x1_5: + return 1.5 + + case .x1_75: + return 1.75 + + case .x2: + return 2 + } + } + } + + static func getAllRate() -> [BRRateModel] { + return [ + BRRateModel(rate: .x0_25), + BRRateModel(rate: .x0_5), + BRRateModel(rate: .x0_75), + BRRateModel(rate: .x1), + BRRateModel(rate: .x1_25), + BRRateModel(rate: .x1_5), + BRRateModel(rate: .x1_75), + BRRateModel(rate: .x2) + ] + } + + private(set) var rate: Rate = .x1 + + init(rate: Rate) { + super.init() + self.rate = rate + } + + func formatString() -> String { + return self.rate.rawValue + } +} diff --git a/BeeReel/Class/Player/View/BRDetailControlView.swift b/BeeReel/Class/Player/View/BRDetailControlView.swift index d833cea..dcedcc1 100644 --- a/BeeReel/Class/Player/View/BRDetailControlView.swift +++ b/BeeReel/Class/Player/View/BRDetailControlView.swift @@ -8,13 +8,228 @@ import UIKit class BRDetailControlView: BRPlayerControlView { - - /* - // Only override draw() if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - override func draw(_ rect: CGRect) { - // Drawing code + + override var viewModel: BRPlayerViewModel? { + didSet { + viewModel?.addObserver(self, forKeyPath: "rateModel", context: nil) + rateButton.setTitle("\(self.viewModel?.rateModel.formatString() ?? "")x", for: .normal) + } } - */ + override var videoInfo: BRVideoInfoModel? { + didSet { + epButton.videoInfo = videoInfo + } + } + + override var shortModel: BRShortModel? { + didSet { + epButton.shortModel = shortModel + nameLabel.text = shortModel?.name + + favoriteButton.isSelected = self.shortModel?.is_collect ?? false + } + } + + override var progress: CGFloat { + didSet { + progressView.progress = progress + } + } + + ///加载中状态 + override var isLoading: Bool { + didSet { + progressView.isLoading = isLoading + } + } + + override var currentTime: TimeInterval { + didSet { + updateProgressLabel() + } + } + + override var durationTime: TimeInterval { + didSet { + updateProgressLabel() + } + } + + + //MARK: UI属性 + private lazy var progressView: BRVideoProgressView = { + let view = BRVideoProgressView() + view.insets = .init(top: 18, left: 15, bottom: 0, right: 18) + return view + }() + + private lazy var progressLabel: UILabel = { + let label = UILabel() + label.isUserInteractionEnabled = false + label.font = .fontRegular(ofSize: 12) + return label + }() + + private lazy var epButton: BRDetailEpButton = { + let button = BRDetailEpButton() + button.addTarget(self, action: #selector(handleEpButton), for: .touchUpInside) + return button + }() + + private lazy var rateButton: UIButton = { + let button = UIButton(type: .custom) + button.backgroundColor = epButton.backgroundColor + button.layer.cornerRadius = 15 + button.layer.masksToBounds = true + button.titleLabel?.font = .fontRegular(ofSize: 13) + button.setTitleColor(.colorFFFFFF(), for: .normal) + button.setTitle("1.0x", for: .normal) + button.addTarget(self, action: #selector(handleRateButton), for: .touchUpInside) + return button + }() + + private lazy var nameLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 14) + label.textColor = .colorFFFDF9() + return label + }() + + private lazy var favoriteButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "favorite_icon_03"), for: .normal) + button.setImage(UIImage(named: "favorite_icon_03_selected"), for: .selected) + button.setImage(UIImage(named: "favorite_icon_03_selected"), for: [.selected, .highlighted]) + button.addTarget(self, action: #selector(handleFavoriteButton), for: .touchUpInside) + return button + }() + + deinit { + NotificationCenter.default.removeObserver(self) + + self.viewModel?.removeObserver(self, forKeyPath: "rateModel") + } + + override init(frame: CGRect) { + super.init(frame: frame) + NotificationCenter.default.addObserver(self, selector: #selector(updateShortFavoriteStateNotification), name: BRVideoAPI.updateShortFavoriteStateNotification, object: nil) + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) + if keyPath == "rateModel" { + rateButton.setTitle("\(self.viewModel?.rateModel.formatString() ?? "")x", for: .normal) + } + } + + +} + +extension BRDetailControlView { + + private func updateProgressLabel() { + let currentTime = Int(self.currentTime).br_formatTimeGroup() + let durationTime = Int(self.durationTime).br_formatTimeGroup() + + let string = NSMutableAttributedString() + let str1 = NSMutableAttributedString(string: "\(currentTime.1):\(currentTime.2)/") + str1.yy_color = .colorFFFFFF(alpha: 0.9) + string.append(str1) + + let str2 = NSMutableAttributedString(string: "\(durationTime.1):\(durationTime.2)") + str2.yy_color = .colorDDDDDD(alpha: 0.9) + string.append(str2) + + progressLabel.attributedText = string + } + + + @objc private func handleFavoriteButton() { + + guard let shortPlayId = self.shortModel?.short_play_id else { return } + + let isFavorite = !(self.shortModel?.is_collect ?? false) + let videoId = self.videoInfo?.short_play_video_id + + BRVideoAPI.requestFavorite(isFavorite: isFavorite, shortPlayId: shortPlayId, videoId: videoId) { + + } + } + + @objc private func updateShortFavoriteStateNotification(sender: Notification) { + guard let userInfo = sender.userInfo else { return } + guard let shortPlayId = userInfo["id"] as? String else { return } + guard let state = userInfo["state"] as? Bool else { return } + guard shortPlayId == self.shortModel?.short_play_id else { return } + + self.shortModel?.is_collect = state + + favoriteButton.isSelected = self.shortModel?.is_collect ?? false + } + + @objc private func handleEpButton() { + self.viewModel?.clickEpButton() + } + + @objc private func handleRateButton() { + self.viewModel?.clickRateButton() + } + +} + + +extension BRDetailControlView { + + private func br_setupUI() { + addSubview(progressView) + progressView.addSubview(progressLabel) + addSubview(epButton) + addSubview(rateButton) + addSubview(nameLabel) + addSubview(favoriteButton) + + progressView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.right.equalToSuperview() + make.bottom.equalTo(epButton.snp.top).offset(-10) + } + + progressLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview() + } + + epButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 10)) + make.height.equalTo(30) + } + + rateButton.snp.makeConstraints { make in + make.centerY.height.equalTo(epButton) + make.right.equalToSuperview().offset(-15) + make.left.equalTo(epButton.snp.right).offset(10) + make.width.equalTo(50) + } + + nameLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.right.lessThanOrEqualToSuperview().offset(-70) + make.bottom.equalTo(progressView.snp.top).offset(-18) + } + + favoriteButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-7) + make.bottom.equalTo(progressView.snp.top).offset(-10) + } + } + } diff --git a/BeeReel/Class/Player/View/BRDetailEpButton.swift b/BeeReel/Class/Player/View/BRDetailEpButton.swift new file mode 100644 index 0000000..bff568e --- /dev/null +++ b/BeeReel/Class/Player/View/BRDetailEpButton.swift @@ -0,0 +1,93 @@ +// +// BRDetailEpButton.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BRDetailEpButton: UIControl { + + + var videoInfo: BRVideoInfoModel? { + didSet { + currentEpLabel.text = "EP.##".localizedReplace(text: videoInfo?.episode ?? "0") + } + } + + var shortModel: BRShortModel? { + didSet { + totalEpLabel.text = "All ## Episodes".localizedReplace(text: "\(shortModel?.episode_total ?? 0)") + } + } + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "ep_icon_01")) + return imageView + }() + + private lazy var currentEpLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 13) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var totalEpLabel: UILabel = { + let label = UILabel() + label.textColor = .colorD3D3D3() + label.font = .fontRegular(ofSize: 13) + return label + }() + + private lazy var indicatorImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "arrow_top_icon_01")) + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .color1C1C1C(alpha: 0.6) + layer.cornerRadius = 15 + layer.masksToBounds = true + + br_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BRDetailEpButton { + + private func br_setupUI() { + addSubview(iconImageView) + addSubview(currentEpLabel) + addSubview(indicatorImageView) + addSubview(totalEpLabel) + + iconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(10) + } + + currentEpLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(iconImageView.snp.right).offset(6) + } + + indicatorImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-10) + } + + totalEpLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalTo(indicatorImageView.snp.left).offset(-4) + } + } + +} diff --git a/BeeReel/Class/Player/View/BRDetailPlayerCell.swift b/BeeReel/Class/Player/View/BRDetailPlayerCell.swift index 8975b8d..d9b9ea9 100644 --- a/BeeReel/Class/Player/View/BRDetailPlayerCell.swift +++ b/BeeReel/Class/Player/View/BRDetailPlayerCell.swift @@ -9,6 +9,7 @@ import UIKit class BRDetailPlayerCell: BRPlayerListCell { + override var ControlViewClass: BRPlayerControlView.Type { return BRDetailControlView.self } diff --git a/BeeReel/Class/Player/View/BREpisodeMenuView.swift b/BeeReel/Class/Player/View/BREpisodeMenuView.swift new file mode 100644 index 0000000..28f4c6e --- /dev/null +++ b/BeeReel/Class/Player/View/BREpisodeMenuView.swift @@ -0,0 +1,138 @@ +// +// BREpisodeMenuView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BREpisodeMenuView: UIView { + + override var intrinsicContentSize: CGSize { + return CGSize(width: UIScreen.width, height: 35) + } + + var didSelectedIndex: ((_ index: Int) -> Void)? + + var dataArr: [String] = [] { + didSet { + self.reloadData() + } + } + + var selectedIndex: Int = 0 { + didSet { + self.buttonArr.forEach { + $0.isSelected = $0.tag == selectedIndex + } + } + } + + private lazy var buttonArr: [UIButton] = [] + + //MARK: UI属性 + private lazy var scrollView: BRScrollView = { + let scrollView = BRScrollView() + scrollView.showsVerticalScrollIndicator = false + scrollView.showsHorizontalScrollIndicator = false + return scrollView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + br_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func reloadData() { + buttonArr.forEach { + $0.removeFromSuperview() + } + buttonArr.removeAll() + + let count = self.dataArr.count + + var previousButton: UIButton? + + dataArr.enumerated().forEach { + let normalStrig = NSMutableAttributedString(string: $1) + normalStrig.yy_color = .colorD3D3D3() + normalStrig.yy_font = .fontRegular(ofSize: 14) + + let selectedString = NSMutableAttributedString(string: $1) + selectedString.yy_color = .colorE3FC37() + selectedString.yy_font = .fontMedium(ofSize: 14) + + + let button = UIButton(type: .custom) + button.tag = $0 + button.setAttributedTitle(normalStrig, for: .normal) + button.setAttributedTitle(selectedString, for: .selected) + button.setAttributedTitle(selectedString, for: [.selected, .highlighted]) + button.addTarget(self, action: #selector(handleButton), for: .touchUpInside) + button.isSelected = $0 == selectedIndex + + self.scrollView.addSubview(button) + self.buttonArr.append(button) + + if previousButton == nil { + button.snp.makeConstraints { make in + make.top.left.equalToSuperview() + make.height.equalTo(35) + } + } else if let previousButton = previousButton, count - 1 == $0 { + button.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalTo(previousButton.snp.right).offset(41) + make.height.equalTo(35) + make.right.equalToSuperview() + } + } else if let previousButton = previousButton { + button.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalTo(previousButton.snp.right).offset(41) + make.height.equalTo(35) + } + } + + if let previousButton = previousButton { + let lineView = UIView() + lineView.backgroundColor = .colorD3D3D3() + self.scrollView.addSubview(lineView) + lineView.snp.makeConstraints { make in + make.height.equalTo(12) + make.width.equalTo(1) + make.centerY.equalTo(previousButton) + make.left.equalTo(previousButton.snp.right).offset(20) + } + + } + + previousButton = button + } + + } + + + + @objc private func handleButton(sender: UIButton) { + self.selectedIndex = sender.tag + self.didSelectedIndex?(self.selectedIndex) + } + +} + +extension BREpisodeMenuView { + private func br_setupUI() { + addSubview(scrollView) + + scrollView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview() + } + } +} diff --git a/BeeReel/Class/Player/View/BREpisodeSelectorCell.swift b/BeeReel/Class/Player/View/BREpisodeSelectorCell.swift new file mode 100644 index 0000000..ecf4284 --- /dev/null +++ b/BeeReel/Class/Player/View/BREpisodeSelectorCell.swift @@ -0,0 +1,69 @@ +// +// BREpisodeSelectorCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BREpisodeSelectorCell: BRCollectionViewCell { + + var model: BRVideoInfoModel? { + didSet { + epLabel.text = model?.episode + } + } + + var br_isSelected: Bool = false { + didSet { + if br_isSelected { + self.contentView.layer.borderColor = UIColor.colorE3FC37().cgColor + lightImageView.isHidden = false + } else { + self.contentView.layer.borderColor = UIColor.clear.cgColor + lightImageView.isHidden = true + } + } + } + + private lazy var epLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 14) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var lightImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "light_icon_01")) + return imageView + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + + contentView.backgroundColor = .colorFFFFFF(alpha: 0.1) + contentView.layer.cornerRadius = 6 + contentView.layer.masksToBounds = true + contentView.layer.borderWidth = 1 + + + contentView.addSubview(epLabel) + contentView.addSubview(lightImageView) + + epLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + lightImageView.snp.makeConstraints { make in + make.right.top.equalToSuperview() + } + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/BeeReel/Class/Player/View/BREpisodeSelectorView.swift b/BeeReel/Class/Player/View/BREpisodeSelectorView.swift new file mode 100644 index 0000000..22e627e --- /dev/null +++ b/BeeReel/Class/Player/View/BREpisodeSelectorView.swift @@ -0,0 +1,263 @@ +// +// BREpisodeSelectorView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BREpisodeSelectorView: BRPanModalContentView { + + + var shortModel: BRShortModel? { + didSet { + coverImageView.br_setImage(url: shortModel?.image_url) + nameLabel.text = shortModel?.name + tagLabel.text = shortModel?.category?.first + contentLabel.text = shortModel?.br_description + } + } + + var epList: [BRVideoInfoModel] = [] { + didSet { + self.collectionView.reloadData() + + var menuDataArr = [String]() + let totalEpisode = epList.count + var index = 0 + var remainingEpisodes = totalEpisode + + while remainingEpisodes > 0 { + let minIndex = index * 30 + var maxIndex = minIndex + 29 + if maxIndex >= epList.count { + maxIndex = epList.count - 1 + } + + let minEpisode = epList[minIndex].episode ?? "0" + let maxEpisode = epList[maxIndex].episode ?? "0" + + if minEpisode == maxEpisode { + menuDataArr.append("\(minEpisode)") + } else { + menuDataArr.append("\(minEpisode)-\(maxEpisode)") + } + + remainingEpisodes -= 30 + index += 1 + } + + self.menuView.dataArr = menuDataArr + } + } + + var index: Int = 0 { + didSet { + self.collectionView.reloadData() + } + } + + var didSelectedIndex: ((_ index: Int) -> Void)? + + private var isDecelerating = false + private var isDragging = false + + private lazy var coverImageView: BRImageView = { + let imageView = BRImageView() + imageView.layer.cornerRadius = 6 + imageView.layer.masksToBounds = true + return imageView + }() + + private lazy var nameLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 14) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var tagLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 12) + label.textColor = .color899D00() + return label + }() + + private lazy var contentLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 10) + label.textColor = .colorD3D3D3() + label.numberOfLines = 3 + return label + }() + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let cellWidth = floor((UIScreen.width - 9 * 4 - 30) / 5) + + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: cellWidth, height: 54) + layout.minimumLineSpacing = 9 + layout.minimumInteritemSpacing = 9 + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) + return layout + }() + + private lazy var collectionView: BRCollectionView = { + let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.contentInset = .init(top: 0, left: 0, bottom: UIScreen.tabbarSafeBottomMargin, right: 0) + collectionView.register(BREpisodeSelectorCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + private lazy var menuView: BREpisodeMenuView = { + let view = BREpisodeMenuView() + view.didSelectedIndex = { [weak self] index in + guard let self = self else { return } + var row = 0 + if index > 0 { + row = index * 30 + 10 + let count = self.epList.count + if row >= count { + row = count - 1 + } + } + let indexPath = IndexPath.init(row: row, section: 0) + self.collectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: true) + } + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.mainScrollView = collectionView + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BREpisodeSelectorView { + + private func br_setupUI() { + addSubview(coverImageView) + addSubview(nameLabel) + addSubview(tagLabel) + addSubview(contentLabel) + addSubview(collectionView) + addSubview(menuView) + + coverImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview().offset(30) + make.width.equalTo(64) + make.height.equalTo(85) + } + + nameLabel.snp.makeConstraints { make in + make.top.equalTo(coverImageView) + make.left.equalTo(coverImageView.snp.right).offset(10) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + + tagLabel.snp.makeConstraints { make in + make.left.equalTo(nameLabel) + make.top.equalTo(nameLabel.snp.bottom).offset(11) + } + + contentLabel.snp.makeConstraints { make in + make.left.equalTo(nameLabel) + make.top.equalTo(tagLabel.snp.bottom).offset(10) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + + collectionView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalToSuperview().offset(167) + } + + menuView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.right.equalToSuperview().offset(-15) + make.top.equalTo(coverImageView.snp.bottom).offset(13) + } + } + +} + +//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource -------------- +extension BREpisodeSelectorView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return epList.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BREpisodeSelectorCell + cell.model = epList[indexPath.row] + cell.br_isSelected = indexPath.row == index + return cell + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + if indexPath.row == index { return } + + self.didSelectedIndex?(indexPath.row) + + self.dismiss(animated: true) { + + } + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if isDragging || isDecelerating { + updateMuneSelectedIndex() + } + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + isDecelerating = false + updateMuneSelectedIndex() + } + + func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { + isDecelerating = true + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + isDragging = true + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + isDragging = false + } + + func updateMuneSelectedIndex() { + let indexPathArr = collectionView.indexPathsForVisibleItems + + var minRow = epList.count - 1 + var maxRow = 0 + + for indexPath in indexPathArr { + if indexPath.row < minRow { + minRow = indexPath.row + } + if indexPath.row > maxRow { + maxRow = indexPath.row + } + } + + let selectedIndex = maxRow / 30 + if menuView.selectedIndex != selectedIndex { + menuView.selectedIndex = selectedIndex + } + } + +} diff --git a/BeeReel/Class/Player/View/BRPlayerControlView.swift b/BeeReel/Class/Player/View/BRPlayerControlView.swift index f3b4df7..309b97d 100644 --- a/BeeReel/Class/Player/View/BRPlayerControlView.swift +++ b/BeeReel/Class/Player/View/BRPlayerControlView.swift @@ -11,9 +11,6 @@ import UIKit class BRPlayerControlView: UIView, BRPlayerControlProtocol { - - - weak var viewModel: BRPlayerViewModel? { didSet { self.viewModel?.addObserver(self, forKeyPath: "isPlaying", context: nil) @@ -32,6 +29,24 @@ class BRPlayerControlView: UIView, BRPlayerControlProtocol { } } + ///滑动进度条 + var panProgressFinishBlock: ((_ progress: CGFloat) -> Void)? + + ///0-1 + var progress: CGFloat = 0 { + didSet { + } + } + + ///加载中状态 + var isLoading = false { + didSet { + } + } + + var durationTime: TimeInterval = 0 + var currentTime: TimeInterval = 0 + private lazy var playIconImageView: UIImageView = { let imageView = UIImageView(image: UIImage(named: "play_icon_05")) return imageView diff --git a/BeeReel/Class/Player/View/BRPlayerListCell.swift b/BeeReel/Class/Player/View/BRPlayerListCell.swift index f1e2ef4..36eaaf2 100644 --- a/BeeReel/Class/Player/View/BRPlayerListCell.swift +++ b/BeeReel/Class/Player/View/BRPlayerListCell.swift @@ -40,7 +40,11 @@ class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol { var currentPosition: Int = 0 - var rate: Float = 1 + var rate: Float = 1 { + didSet { + self.player.rate = rate + } + } func prepare() { @@ -122,4 +126,18 @@ extension BRPlayerListCell: BRPlayerDelegate { self.viewModel?.playFinish(player: self) } + func br_playerDurationDidChange(_ player: BRPlayer, duration: TimeInterval) { + self.controlView.durationTime = duration + + } + + func br_playerCurrentTimeDidChange(_ player: BRPlayer, time: TimeInterval) { + self.controlView.currentTime = time + + if player.duration <= 0 { + self.controlView.progress = 0 + } else { + self.controlView.progress = time / player.duration + } + } } diff --git a/BeeReel/Class/Player/View/BRRateSelectorCell.swift b/BeeReel/Class/Player/View/BRRateSelectorCell.swift new file mode 100644 index 0000000..b5d20b3 --- /dev/null +++ b/BeeReel/Class/Player/View/BRRateSelectorCell.swift @@ -0,0 +1,43 @@ +// +// BRRateSelectorCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BRRateSelectorCell: BRTableViewCell { + + private(set) lazy var titleLabel: UILabel = { + let label = UILabel() + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var bgView: UIView = { + let view = UIView() + view.br_addEffectView(style: .dark) + return view + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) +// self.contentView.br_addEffectView(style: .dark) + contentView.addSubview(bgView) + contentView.addSubview(titleLabel) + + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/BeeReel/Class/Player/View/BRRateSelectorView.swift b/BeeReel/Class/Player/View/BRRateSelectorView.swift new file mode 100644 index 0000000..13b3aac --- /dev/null +++ b/BeeReel/Class/Player/View/BRRateSelectorView.swift @@ -0,0 +1,109 @@ +// +// BRRateSelectorView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BRRateSelectorView: UIView { + + + var didSelectedRate: ((_ model: BRRateModel) -> Void)? + + private lazy var arr = BRRateModel.getAllRate() + + private lazy var tableView: BRTableView = { + let tableView = BRTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 44 + tableView.layer.cornerRadius = 10 + tableView.layer.masksToBounds = true + tableView.isScrollEnabled = false + tableView.separatorColor = .color747474() + tableView.separatorInset = .init(top: 0, left: 0, bottom: 0, right: 0) + tableView.register(BRRateSelectorCell.self, forCellReuseIdentifier: "cell") + return tableView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + br_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + super.touchesBegan(touches, with: event) + + self.dismiss() + } + + + func show() { + BRAppTool.keyWindow?.addSubview(self) + + self.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + func dismiss() { + self.removeFromSuperview() + } + +} + +extension BRRateSelectorView { + + private func br_setupUI() { + addSubview(tableView) + + + tableView.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-15) + make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 45)) + make.width.equalTo(100) + make.height.equalTo((arr.count + 1) * 44) + } + } + + +} + +//MARK: -------------- UITableViewDelegate & UITableViewDataSource -------------- +extension BRRateSelectorView: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BRRateSelectorCell + + if indexPath.row == 0 { + cell.titleLabel.text = "Speed".localized + cell.titleLabel.font = .fontMedium(ofSize: 14) + } else { + cell.titleLabel.text = arr[indexPath.row - 1].formatString() + cell.titleLabel.font = .fontRegular(ofSize: 14) + } + + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return arr.count + 1 + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard indexPath.row > 0 else { return } + let model = arr[indexPath.row - 1] + + self.didSelectedRate?(model) + + self.dismiss() + } + +} diff --git a/BeeReel/Class/Player/View/BRVideoProgressView.swift b/BeeReel/Class/Player/View/BRVideoProgressView.swift new file mode 100644 index 0000000..b4e5ac3 --- /dev/null +++ b/BeeReel/Class/Player/View/BRVideoProgressView.swift @@ -0,0 +1,209 @@ +// +// BRVideoProgressView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/7/21. +// + +import UIKit + +class BRVideoProgressView: UIView { + + ///滑动开始 + var panStart: (() -> Void)? + + ///滑动中 + var panChange: ((_ progress: CGFloat) -> Void)? + + ///滑动完成回调 + var panFinish: ((_ progress: CGFloat) -> Void)? + + var progress: CGFloat = 0 { + didSet { + if !isPaning { + setNeedsDisplay() + } + } + } + + ///用来记录滑动时的当前进度 + private var tempProgress: CGFloat = 0 + + ///滑动进度 + private var panProgress: CGFloat = 0 + + var progressColor: UIColor = .colorFFFFFF(alpha: 0.2) + var currentProgress: UIColor = .colorFFFFFF() + + var lineWidth: CGFloat = 3 + + ///加载中状态 + var isLoading = false { + didSet { + if isLoading { + if gradientTimer == nil { + gradientTimer = Timer.scheduledTimer(timeInterval: 0.05, target: YYTextWeakProxy(target: self), selector: #selector(handleGradientTimer), userInfo: nil, repeats: true) + } + } else { + gradientTimer?.invalidate() + gradientTimer = nil + } + } + } + + var insets: UIEdgeInsets = .init(top: 0, left: 15, bottom: 0, right: 15) { + didSet { + self.invalidateIntrinsicContentSize() + setNeedsDisplay() + } + } + + private(set) lazy var panGesture: UIPanGestureRecognizer = { + let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(sender:))) + return pan + }() + + private(set) lazy var tagGesture: UITapGestureRecognizer = { + let tap = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(sender:))) + return tap + }() + + ///是否在滑动中 + private var isPaning: Bool = false + + private var gradientTimer: Timer? + + private var gradientValue: CGFloat = 0 + + override var intrinsicContentSize: CGSize { + return .init(width: UIScreen.width, height: lineWidth + insets.top + insets.bottom) + } + + override init(frame: CGRect) { + super.init(frame: frame) +// self.backgroundColor = progressColor + self.backgroundColor = .clear + + self.addGestureRecognizer(panGesture) + self.addGestureRecognizer(tagGesture) + + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + setNeedsDisplay() + } + + @objc private func handleGradientTimer() { + gradientValue += 0.1 + if gradientValue > 1 { + gradientValue = 0 + } + setNeedsDisplay() + } + + override func draw(_ rect: CGRect) { + super.draw(rect) + guard let context = UIGraphicsGetCurrentContext() else { return } + let width = rect.width + + let progressX = insets.left + let progressY = insets.top + let progressWidth = width - insets.left - insets.right + + if isLoading, !isPaning { + // 定义颜色空间 + let colorSpace = CGColorSpaceCreateDeviceRGB() + let colors: [CGColor] = [ + UIColor.clear.cgColor, + UIColor.white.cgColor, + UIColor.clear.cgColor + ] + let locations: [CGFloat] = [0.0, gradientValue, 1.0] + + guard let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations) else { + return + } + + let gradientRect = CGRect(x: progressX, + y: progressY, + width: progressWidth, + height: lineWidth) + + // 定义渐变的起点和终点 + let startPoint = CGPoint(x: rect.minX, y: rect.minY) + let endPoint = CGPoint(x: rect.maxX, y: rect.maxY) + + // 裁剪到渐变区域 + context.saveGState() + context.clip(to: gradientRect) + + // 绘制渐变 + context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: []) + } else { + var progress = self.progress + if self.isPaning { + progress = self.panProgress + } + + + ///绘制进度 + let progressPath = UIBezierPath(roundedRect: CGRect(x: progressX, y: progressY, width: progressWidth, height: lineWidth), cornerRadius: lineWidth / 2) + context.addPath(progressPath.cgPath) + context.setFillColor(progressColor.cgColor) + context.fillPath() + + ///绘制当前进度 + let currentPath = UIBezierPath(roundedRect: CGRect(x: progressX, y: progressY, width: progressWidth * progress, height: lineWidth), cornerRadius: lineWidth / 2) + context.addPath(currentPath.cgPath) + context.setFillColor(currentProgress.cgColor) + context.fillPath() + } + + + } + +} + + +extension BRVideoProgressView { + + @objc func handlePanGesture(sender: UIPanGestureRecognizer) { + + switch sender.state { + case .began: + self.isPaning = true + self.tempProgress = self.progress + sender.setTranslation(CGPoint(x: 0, y: 0), in: self) + self.panStart?() + + case .changed: + let point = sender.translation(in: self) + let offsetX = point.x / (self.width - self.insets.left - self.insets.right) + self.panProgress = self.tempProgress + offsetX + if self.panProgress < 0 { + self.panProgress = 0 + } + self.panChange?(self.panProgress) + setNeedsDisplay() + + default: + self.isPaning = false + self.panFinish?(self.panProgress) + + self.panProgress = 0 + } + } + + @objc func handleTapGesture(sender: UITapGestureRecognizer) { + let point = sender.location(in: self) + let offsetX = (point.x - self.insets.left) / (self.width - self.insets.left - self.insets.right) + self.panFinish?(offsetX) + } +} diff --git a/BeeReel/Class/Player/ViewModel/BRPlayerViewModel.swift b/BeeReel/Class/Player/ViewModel/BRPlayerViewModel.swift index 5e9562a..70ff869 100644 --- a/BeeReel/Class/Player/ViewModel/BRPlayerViewModel.swift +++ b/BeeReel/Class/Player/ViewModel/BRPlayerViewModel.swift @@ -10,9 +10,13 @@ import UIKit @objc protocol BRPlayerViewModelDelegate { - @objc optional func br_currentVideoPlayFinish(viewModel: BRPlayerViewModel) + @objc func br_currentVideoPlayFinish(viewModel: BRPlayerViewModel) - @objc optional func br_switchPlayAndPause(viewModel: BRPlayerViewModel) + @objc func br_switchPlayAndPause(viewModel: BRPlayerViewModel) + + @objc func br_onEpisodeView(viewModel: BRPlayerViewModel) + + @objc func br_clickRateButton(viewModel: BRPlayerViewModel) } @@ -30,7 +34,14 @@ class BRPlayerViewModel: NSObject { oldValue?.pause() self.currentPlayer?.isCurrent = true -// self.currentPlayer?.rate = rateModel.rate.getRate() + self.currentPlayer?.rate = rateModel.rate.getRate() + } + } + + ///倍速播放 + @objc dynamic lazy var rateModel = BRRateModel(rate: .x1) { + didSet { + self.currentPlayer?.rate = rateModel.rate.getRate() } } } @@ -41,13 +52,22 @@ extension BRPlayerViewModel { func playFinish(player: BRPlayerProtocol) { guard (player as? UICollectionViewCell) == (currentPlayer as? UICollectionViewCell) else { return } - self.delegate?.br_currentVideoPlayFinish?(viewModel: self) + self.delegate?.br_currentVideoPlayFinish(viewModel: self) } ///切换播放跟暂停 func switchPlayAndPause() { - self.delegate?.br_switchPlayAndPause?(viewModel: self) + self.delegate?.br_switchPlayAndPause(viewModel: self) } + ///点击选集 + func clickEpButton() { + self.delegate?.br_onEpisodeView(viewModel: self) + } + + ///点击速率按钮 + func clickRateButton() { + self.delegate?.br_clickRateButton(viewModel: self) + } } diff --git a/BeeReel/Lib/Player/BRPlayer.swift b/BeeReel/Lib/Player/BRPlayer.swift index 8e9308f..6d4b0b3 100644 --- a/BeeReel/Lib/Player/BRPlayer.swift +++ b/BeeReel/Lib/Player/BRPlayer.swift @@ -77,6 +77,15 @@ class BRPlayer: NSObject { return self.player.currentTime } + var rate: Float { + get { + return self.player.rate + } + set { + self.player.rate = newValue + } + } + deinit { brLog(message: "播放器销毁") } diff --git a/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json b/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json index 2305880..e971063 100644 --- a/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,28 +1,7 @@ { "images" : [ { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], + "filename" : "GleeStream 1.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/GleeStream 1.png b/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/GleeStream 1.png new file mode 100644 index 0000000..00b0ce3 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/GleeStream 1.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@2x.png b/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@2x.png new file mode 100644 index 0000000..a02b38f Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@3x.png b/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@3x.png new file mode 100644 index 0000000..160ce78 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@2x.png b/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@2x.png new file mode 100644 index 0000000..e08778d Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@3x.png b/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@3x.png new file mode 100644 index 0000000..ec413cc Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@2x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@2x.png new file mode 100644 index 0000000..0d62d62 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@3x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@3x.png new file mode 100644 index 0000000..7fac648 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/Contents.json new file mode 100644 index 0000000..7b1b5d4 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "未选中收藏@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "未选中收藏@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@2x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@2x.png new file mode 100644 index 0000000..c432864 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@3x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@3x.png new file mode 100644 index 0000000..34ad8eb Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/Contents.json new file mode 100644 index 0000000..d57a8d5 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "收藏1 2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "收藏1 2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@2x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@2x.png new file mode 100644 index 0000000..2466142 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@3x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@3x.png new file mode 100644 index 0000000..7fbb166 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5ba1486 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "选中光效@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "选中光效@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@2x.png b/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@2x.png new file mode 100644 index 0000000..a12e27b Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@3x.png b/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@3x.png new file mode 100644 index 0000000..c4afbe4 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Contents.json new file mode 100644 index 0000000..dc4afb3 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Ellipse 13@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Ellipse 13@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@2x.png b/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@2x.png new file mode 100644 index 0000000..ee7bc5a Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@3x.png b/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@3x.png new file mode 100644 index 0000000..ea94ed7 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@3x.png differ diff --git a/BeeReel/Sources/Localizable.xcstrings b/BeeReel/Sources/Localizable.xcstrings index 793624e..69e41c3 100644 --- a/BeeReel/Sources/Localizable.xcstrings +++ b/BeeReel/Sources/Localizable.xcstrings @@ -1,6 +1,17 @@ { "sourceLanguage" : "en", "strings" : { + "All ## Episodes" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "All ## Episodes" + } + } + } + }, "Browse Genres" : { "extractionState" : "manual", "localizations" : { @@ -23,6 +34,17 @@ } } }, + "EP.##" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "EP.##" + } + } + } + }, "Fresh Stories" : { "extractionState" : "manual", "localizations" : { @@ -56,6 +78,17 @@ } } }, + "Speed" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speed" + } + } + } + }, "Spotlight" : { "extractionState" : "manual", "localizations" : { diff --git a/Podfile b/Podfile index 4178169..6897410 100644 --- a/Podfile +++ b/Podfile @@ -29,5 +29,6 @@ target 'BeeReel' do pod 'YYText' pod 'FSPagerView' #banner pod 'MJRefresh' #刷新控件 + pod 'HWPanModal' #底部弹出控制器 end