播放器页面完成
@ -8,6 +8,16 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
440A41A6E6A22A02807AE759 /* Pods_BeeReel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 899B3015B03D5E1A5A6507EB /* Pods_BeeReel.framework */; };
|
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 */; };
|
BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */; };
|
||||||
BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.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 */; };
|
BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */; };
|
||||||
@ -113,6 +123,16 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* 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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
899B3015B03D5E1A5A6507EB /* Pods_BeeReel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BeeReel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
||||||
|
BF02B7E22E2E08BD00172177 /* BRDetailEpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRDetailEpButton.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7E42E2E1E6100172177 /* BREpisodeSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BREpisodeSelectorView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7E62E2E1F0500172177 /* BRPanModalContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPanModalContentView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7E82E2E29E900172177 /* BREpisodeSelectorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BREpisodeSelectorCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7EA2E2E388800172177 /* BREpisodeMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BREpisodeMenuView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7EC2E2E390500172177 /* BRScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRScrollView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7EE2E2E4BFD00172177 /* BRRateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRRateModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7F02E2E55E300172177 /* BRRateSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRRateSelectorView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B7F22E2E571600172177 /* BRRateSelectorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRRateSelectorCell.swift; sourceTree = "<group>"; };
|
||||||
BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBar.swift; sourceTree = "<group>"; };
|
BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBar.swift; sourceTree = "<group>"; };
|
||||||
BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+BRAdd.swift"; sourceTree = "<group>"; };
|
BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+BRAdd.swift"; sourceTree = "<group>"; };
|
||||||
BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerCache.swift; sourceTree = "<group>"; };
|
BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerCache.swift; sourceTree = "<group>"; };
|
||||||
@ -525,6 +545,13 @@
|
|||||||
BF3338EB2E154BFE00B10F76 /* BRPlayerControlView.swift */,
|
BF3338EB2E154BFE00B10F76 /* BRPlayerControlView.swift */,
|
||||||
BF3338F62E16176900B10F76 /* BRDetailPlayerCell.swift */,
|
BF3338F62E16176900B10F76 /* BRDetailPlayerCell.swift */,
|
||||||
BF3338F82E16178700B10F76 /* BRDetailControlView.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;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -535,6 +562,7 @@
|
|||||||
BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */,
|
BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */,
|
||||||
BFC676802E122733006659E5 /* BRPlayerProtocol.swift */,
|
BFC676802E122733006659E5 /* BRPlayerProtocol.swift */,
|
||||||
BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */,
|
BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */,
|
||||||
|
BF02B7EE2E2E4BFD00172177 /* BRRateModel.swift */,
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -557,6 +585,8 @@
|
|||||||
BFC6766C2E0E3A8D006659E5 /* BRImageView.swift */,
|
BFC6766C2E0E3A8D006659E5 /* BRImageView.swift */,
|
||||||
BFC676722E0E938B006659E5 /* BRTableView.swift */,
|
BFC676722E0E938B006659E5 /* BRTableView.swift */,
|
||||||
BFC676742E0E93B3006659E5 /* BRTableViewCell.swift */,
|
BFC676742E0E93B3006659E5 /* BRTableViewCell.swift */,
|
||||||
|
BF02B7E62E2E1F0500172177 /* BRPanModalContentView.swift */,
|
||||||
|
BF02B7EC2E2E390500172177 /* BRScrollView.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -845,6 +875,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
BF02B7F32E2E571600172177 /* BRRateSelectorCell.swift in Sources */,
|
||||||
BFC676992E1280E3006659E5 /* BRSpotlightRecommandCell.swift in Sources */,
|
BFC676992E1280E3006659E5 /* BRSpotlightRecommandCell.swift in Sources */,
|
||||||
BFC676A42E129D60006659E5 /* BRHomeTop10Cell.swift in Sources */,
|
BFC676A42E129D60006659E5 /* BRHomeTop10Cell.swift in Sources */,
|
||||||
BF692B3C2E0A8D0200A5C2DA /* BRNavigationController.swift in Sources */,
|
BF692B3C2E0A8D0200A5C2DA /* BRNavigationController.swift in Sources */,
|
||||||
@ -862,14 +893,17 @@
|
|||||||
BF692B242E0A825B00A5C2DA /* BRCryptorService.swift in Sources */,
|
BF692B242E0A825B00A5C2DA /* BRCryptorService.swift in Sources */,
|
||||||
BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */,
|
BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */,
|
||||||
BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */,
|
BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */,
|
||||||
|
BF02B7E12E2DE64200172177 /* BRVideoProgressView.swift in Sources */,
|
||||||
BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */,
|
BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */,
|
||||||
BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */,
|
BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */,
|
||||||
BF692B472E0A9B7900A5C2DA /* BRPlayer.swift in Sources */,
|
BF692B472E0A9B7900A5C2DA /* BRPlayer.swift in Sources */,
|
||||||
BF692B6E2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift in Sources */,
|
BF692B6E2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift in Sources */,
|
||||||
BF692AFA2E0A6F0900A5C2DA /* BRNetwork.swift in Sources */,
|
BF692AFA2E0A6F0900A5C2DA /* BRNetwork.swift in Sources */,
|
||||||
|
BF02B7E52E2E1E6100172177 /* BREpisodeSelectorView.swift in Sources */,
|
||||||
BF692B6B2E0BC85300A5C2DA /* BRHomeViewController.swift in Sources */,
|
BF692B6B2E0BC85300A5C2DA /* BRHomeViewController.swift in Sources */,
|
||||||
BF692B7C2E0D3C1300A5C2DA /* BRVideoInfoModel.swift in Sources */,
|
BF692B7C2E0D3C1300A5C2DA /* BRVideoInfoModel.swift in Sources */,
|
||||||
BFC6766D2E0E3A8D006659E5 /* BRImageView.swift in Sources */,
|
BFC6766D2E0E3A8D006659E5 /* BRImageView.swift in Sources */,
|
||||||
|
BF02B7ED2E2E390500172177 /* BRScrollView.swift in Sources */,
|
||||||
BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */,
|
BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */,
|
||||||
BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */,
|
BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */,
|
||||||
BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */,
|
BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */,
|
||||||
@ -901,7 +935,9 @@
|
|||||||
BF3338FD2E1626B000B10F76 /* BRPlayerControlProtocol.swift in Sources */,
|
BF3338FD2E1626B000B10F76 /* BRPlayerControlProtocol.swift in Sources */,
|
||||||
BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */,
|
BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */,
|
||||||
BF692B1F2E0A804600A5C2DA /* BRLocalizedManager.swift in Sources */,
|
BF692B1F2E0A804600A5C2DA /* BRLocalizedManager.swift in Sources */,
|
||||||
|
BF02B7E92E2E29E900172177 /* BREpisodeSelectorCell.swift in Sources */,
|
||||||
BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */,
|
BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */,
|
||||||
|
BF02B7F12E2E55E300172177 /* BRRateSelectorView.swift in Sources */,
|
||||||
BF692B012E0A74A200A5C2DA /* BRDefine.swift in Sources */,
|
BF692B012E0A74A200A5C2DA /* BRDefine.swift in Sources */,
|
||||||
BFC6767B2E0E973B006659E5 /* UIStackView+BRAdd.swift in Sources */,
|
BFC6767B2E0E973B006659E5 /* UIStackView+BRAdd.swift in Sources */,
|
||||||
BF3338F32E16169A00B10F76 /* BRExplorePlayerCell.swift in Sources */,
|
BF3338F32E16169A00B10F76 /* BRExplorePlayerCell.swift in Sources */,
|
||||||
@ -910,6 +946,7 @@
|
|||||||
BFC676892E122FDD006659E5 /* BRVideoAPI.swift in Sources */,
|
BFC676892E122FDD006659E5 /* BRVideoAPI.swift in Sources */,
|
||||||
BFC6769B2E1285C5006659E5 /* BRPagerViewTransformer.swift in Sources */,
|
BFC6769B2E1285C5006659E5 /* BRPagerViewTransformer.swift in Sources */,
|
||||||
BFC676832E122CC5006659E5 /* BRPlayerViewModel.swift in Sources */,
|
BFC676832E122CC5006659E5 /* BRPlayerViewModel.swift in Sources */,
|
||||||
|
BF02B7EF2E2E4BFD00172177 /* BRRateModel.swift in Sources */,
|
||||||
BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */,
|
BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */,
|
||||||
BF3338FB2E161CF900B10F76 /* NSNumber+BRAdd.swift in Sources */,
|
BF3338FB2E161CF900B10F76 /* NSNumber+BRAdd.swift in Sources */,
|
||||||
BF692B222E0A820D00A5C2DA /* String+BRAdd.swift in Sources */,
|
BF692B222E0A820D00A5C2DA /* String+BRAdd.swift in Sources */,
|
||||||
@ -924,7 +961,9 @@
|
|||||||
BFC676972E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift in Sources */,
|
BFC676972E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift in Sources */,
|
||||||
BFC6767F2E121A72006659E5 /* BRSpotlightHotCell.swift in Sources */,
|
BFC6767F2E121A72006659E5 /* BRSpotlightHotCell.swift in Sources */,
|
||||||
BFC6767D2E0E9809006659E5 /* BRSpotlightHotMainCell.swift in Sources */,
|
BFC6767D2E0E9809006659E5 /* BRSpotlightHotMainCell.swift in Sources */,
|
||||||
|
BF02B7EB2E2E388800172177 /* BREpisodeMenuView.swift in Sources */,
|
||||||
BFC676662E0E2C8E006659E5 /* WMZBannerFadeLayout.m in Sources */,
|
BFC676662E0E2C8E006659E5 /* WMZBannerFadeLayout.m in Sources */,
|
||||||
|
BF02B7E72E2E1F0500172177 /* BRPanModalContentView.swift in Sources */,
|
||||||
BFC676872E122E36006659E5 /* BRVideoDetailModel.swift in Sources */,
|
BFC676872E122E36006659E5 /* BRVideoDetailModel.swift in Sources */,
|
||||||
BFC676672E0E2C8E006659E5 /* WMZBannerParam.m in Sources */,
|
BFC676672E0E2C8E006659E5 /* WMZBannerParam.m in Sources */,
|
||||||
BF692B732E0D397700A5C2DA /* BRHomeAPI.swift in Sources */,
|
BF692B732E0D397700A5C2DA /* BRHomeAPI.swift in Sources */,
|
||||||
@ -933,6 +972,7 @@
|
|||||||
BF3338F02E15569600B10F76 /* BRExploreViewController.swift in Sources */,
|
BF3338F02E15569600B10F76 /* BRExploreViewController.swift in Sources */,
|
||||||
BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */,
|
BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */,
|
||||||
BF692B562E0AA92100A5C2DA /* BRCollectionViewCell.swift in Sources */,
|
BF692B562E0AA92100A5C2DA /* BRCollectionViewCell.swift in Sources */,
|
||||||
|
BF02B7E32E2E08BD00172177 /* BRDetailEpButton.swift in Sources */,
|
||||||
BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */,
|
BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */,
|
||||||
BF692B752E0D39D000A5C2DA /* BRListModel.swift in Sources */,
|
BF692B752E0D39D000A5C2DA /* BRListModel.swift in Sources */,
|
||||||
BFC676B92E1385FC006659E5 /* BRPopularPicksSmallCell.swift in Sources */,
|
BFC676B92E1385FC006659E5 /* BRPopularPicksSmallCell.swift in Sources */,
|
||||||
|
@ -66,4 +66,12 @@ extension UIColor {
|
|||||||
static func colorFFFDF9(alpha: CGFloat = 1) -> UIColor {
|
static func colorFFFDF9(alpha: CGFloat = 1) -> UIColor {
|
||||||
return UIColor(rgb: 0xFFFDF9, alpha: alpha)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
self.roundedCorner = BRRoundedCorner(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight)
|
||||||
_updateRoundedCorner()
|
_updateRoundedCorner()
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
let BRBaseURL = "https://api-qjwl168.qjwl168.com"
|
let BRBaseURL = "https://api-breeltv.breeltv.com"
|
||||||
let BRURLPathPrefix = "/velo"
|
let BRURLPathPrefix = "/reel"
|
||||||
|
|
||||||
let BRWebBaseURL = "https://www.qjwl168.com"
|
let BRWebBaseURL = "https://www.breeltv.com"
|
||||||
let BRCampaignWebURL = "https://campaign.qjwl168.com"
|
let BRCampaignWebURL = "https://campaign.breeltv.com"
|
||||||
|
|
||||||
|
|
||||||
///用户协议
|
///用户协议
|
||||||
|
90
BeeReel/Base/View/BRPanModalContentView.swift
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
21
BeeReel/Base/View/BRScrollView.swift
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,7 +32,7 @@ class BRTabBar: UITabBar {
|
|||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
self.backgroundColor = .color1C1C1C()
|
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)
|
addSubview(topImageView)
|
||||||
|
|
||||||
|
@ -47,14 +47,18 @@ class BRExploreViewController: BRPlayerListViewController {
|
|||||||
button.setImage(UIImage(named: "episode_icon_01"), for: .normal)
|
button.setImage(UIImage(named: "episode_icon_01"), for: .normal)
|
||||||
button.setContentHuggingPriority(.required, for: .horizontal)
|
button.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
button.addTarget(self, action: #selector(handleEpisodeButton), for: .touchUpInside)
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var favoriteButton: UIButton = {
|
private lazy var favoriteButton: UIButton = {
|
||||||
let button = UIButton(type: .custom)
|
let button = UIButton(type: .custom)
|
||||||
button.setImage(UIImage(named: "favorite_icon_02"), for: .normal)
|
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.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
button.addTarget(self, action: #selector(handleFavoriteButton), for: .touchUpInside)
|
||||||
return button
|
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 --------------
|
//MARK: -------------- BRPlayerListViewControllerDelegate BRPlayerListViewControllerDataSource --------------
|
||||||
extension BRExploreViewController: 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]
|
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 {
|
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) {
|
func br_playerListViewController(_ viewController: BRPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) {
|
||||||
videoNameLabel.text = self.viewModel.currentPlayer?.shortModel?.name
|
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
|
self.collectionView.isHidden = false
|
||||||
if let listModel = listModel, let list = listModel.list {
|
if let listModel = listModel, let list = listModel.list {
|
||||||
if page == 1 {
|
if page == 1 {
|
||||||
self.dataArr = list
|
|
||||||
self.clearData()
|
self.clearData()
|
||||||
self.reloadData()
|
self.dataArr = list
|
||||||
|
self.reloadData { [weak self] in
|
||||||
|
self?.play()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.addDataArr(dataArr: list)
|
self.addDataArr(dataArr: list)
|
||||||
}
|
}
|
||||||
|
@ -13,5 +13,30 @@ class BRExplorePlayerCell: BRPlayerListCell {
|
|||||||
return BRExploreControlView.self
|
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?()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,12 @@ import SJMediaCacheServer
|
|||||||
@objc protocol BRPlayerListViewControllerDataSource {
|
@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
|
func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
|
||||||
|
|
||||||
|
@objc optional func br_numberOfSections(in viewController: BRPlayerListViewController) -> Int
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BRPlayerListViewController: BRViewController {
|
class BRPlayerListViewController: BRViewController {
|
||||||
@ -75,7 +77,7 @@ class BRPlayerListViewController: BRViewController {
|
|||||||
collectionView.showsHorizontalScrollIndicator = false
|
collectionView.showsHorizontalScrollIndicator = false
|
||||||
collectionView.bounces = false
|
collectionView.bounces = false
|
||||||
collectionView.scrollsToTop = false
|
collectionView.scrollsToTop = false
|
||||||
collectionView.register(CellClass.self, forCellWithReuseIdentifier: "playerCell")
|
collectionView.register(CellClass.self, forCellWithReuseIdentifier: "cell")
|
||||||
return collectionView
|
return collectionView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -88,6 +90,7 @@ class BRPlayerListViewController: BRViewController {
|
|||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
self.statusBarStyle = .lightContent
|
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(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(willResignActiveNotification), name: UIApplication.willResignActiveNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(willResignActiveNotification), name: UIApplication.willResignActiveNotification, object: nil)
|
||||||
@ -201,9 +204,11 @@ extension BRPlayerListViewController {
|
|||||||
extension BRPlayerListViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
extension BRPlayerListViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "playerCell", for: indexPath)
|
var cell: UICollectionViewCell
|
||||||
if let newCell = self.dataSource?.br_playerListViewController(self, collectionView, cellForItemAt: indexPath, oldCell: cell) {
|
if let newCell = self.dataSource?.br_playerListViewController(self, collectionView, cellForItemAt: indexPath) {
|
||||||
cell = newCell
|
cell = newCell
|
||||||
|
} else {
|
||||||
|
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
|
||||||
}
|
}
|
||||||
if let cell = cell as? BRPlayerListCell {
|
if let cell = cell as? BRPlayerListCell {
|
||||||
if cell.viewModel == nil {
|
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) {
|
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
scrollDidEnd(scrollView)
|
scrollDidEnd(scrollView)
|
||||||
@ -293,6 +306,14 @@ extension BRPlayerListViewController: BRPlayerViewModelDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func br_onEpisodeView(viewModel: BRPlayerViewModel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func br_clickRateButton(viewModel: BRPlayerViewModel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BRPlayerListViewController {
|
extension BRPlayerListViewController {
|
||||||
|
@ -21,8 +21,7 @@ class BRVideoDetailViewController: BRPlayerListViewController {
|
|||||||
var shortPlayId: String?
|
var shortPlayId: String?
|
||||||
var activityId: String?
|
var activityId: String?
|
||||||
|
|
||||||
private var detailModel: BRVideoDetailModel?
|
private var detailArr: [BRVideoDetailModel] = []
|
||||||
|
|
||||||
|
|
||||||
//MARK: UI属性
|
//MARK: UI属性
|
||||||
private lazy var backButton: UIButton = {
|
private lazy var backButton: UIButton = {
|
||||||
@ -51,13 +50,15 @@ class BRVideoDetailViewController: BRPlayerListViewController {
|
|||||||
override var previousVideoUrl: String? {
|
override var previousVideoUrl: String? {
|
||||||
let index = self.viewModel.currentIndexPath.row - 1
|
let index = self.viewModel.currentIndexPath.row - 1
|
||||||
guard index > 0 else { return nil }
|
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? {
|
override var nextVideoUrl: String? {
|
||||||
let index = self.viewModel.currentIndexPath.row + 1
|
let index = self.viewModel.currentIndexPath.row + 1
|
||||||
guard index < (detailModel?.episodeList?.count ?? 0) else { return nil }
|
let model = self.detailArr[self.viewModel.currentIndexPath.section]
|
||||||
return detailModel?.episodeList?[index].video_url
|
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 --------------
|
//MARK: -------------- BRPlayerListViewControllerDataSource BRPlayerListViewControllerDelegate --------------
|
||||||
extension BRVideoDetailViewController: 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 {
|
let model = self.detailArr[indexPath.section]
|
||||||
cell.videoInfo = self.detailModel?.episodeList?[indexPath.row]
|
|
||||||
cell.shortModel = self.detailModel?.shortPlayInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
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 {
|
extension BRVideoDetailViewController {
|
||||||
@ -110,7 +142,8 @@ extension BRVideoDetailViewController {
|
|||||||
|
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
guard let model = model else { return }
|
guard let model = model else { return }
|
||||||
self.detailModel = model
|
self.detailArr.removeAll()
|
||||||
|
self.detailArr.append(model)
|
||||||
|
|
||||||
self.reloadData { [weak self] in
|
self.reloadData { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
74
BeeReel/Class/Player/Model/BRRateModel.swift
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -9,12 +9,227 @@ import UIKit
|
|||||||
|
|
||||||
class BRDetailControlView: BRPlayerControlView {
|
class BRDetailControlView: BRPlayerControlView {
|
||||||
|
|
||||||
/*
|
override var viewModel: BRPlayerViewModel? {
|
||||||
// Only override draw() if you perform custom drawing.
|
didSet {
|
||||||
// An empty implementation adversely affects performance during animation.
|
viewModel?.addObserver(self, forKeyPath: "rateModel", context: nil)
|
||||||
override func draw(_ rect: CGRect) {
|
rateButton.setTitle("\(self.viewModel?.rateModel.formatString() ?? "")x", for: .normal)
|
||||||
// Drawing code
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
93
BeeReel/Class/Player/View/BRDetailEpButton.swift
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -9,6 +9,7 @@ import UIKit
|
|||||||
|
|
||||||
class BRDetailPlayerCell: BRPlayerListCell {
|
class BRDetailPlayerCell: BRPlayerListCell {
|
||||||
|
|
||||||
|
|
||||||
override var ControlViewClass: BRPlayerControlView.Type {
|
override var ControlViewClass: BRPlayerControlView.Type {
|
||||||
return BRDetailControlView.self
|
return BRDetailControlView.self
|
||||||
}
|
}
|
||||||
|
138
BeeReel/Class/Player/View/BREpisodeMenuView.swift
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
BeeReel/Class/Player/View/BREpisodeSelectorCell.swift
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
263
BeeReel/Class/Player/View/BREpisodeSelectorView.swift
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,9 +11,6 @@ import UIKit
|
|||||||
class BRPlayerControlView: UIView, BRPlayerControlProtocol {
|
class BRPlayerControlView: UIView, BRPlayerControlProtocol {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
weak var viewModel: BRPlayerViewModel? {
|
weak var viewModel: BRPlayerViewModel? {
|
||||||
didSet {
|
didSet {
|
||||||
self.viewModel?.addObserver(self, forKeyPath: "isPlaying", context: nil)
|
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 = {
|
private lazy var playIconImageView: UIImageView = {
|
||||||
let imageView = UIImageView(image: UIImage(named: "play_icon_05"))
|
let imageView = UIImageView(image: UIImage(named: "play_icon_05"))
|
||||||
return imageView
|
return imageView
|
||||||
|
@ -40,7 +40,11 @@ class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol {
|
|||||||
|
|
||||||
var currentPosition: Int = 0
|
var currentPosition: Int = 0
|
||||||
|
|
||||||
var rate: Float = 1
|
var rate: Float = 1 {
|
||||||
|
didSet {
|
||||||
|
self.player.rate = rate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func prepare() {
|
func prepare() {
|
||||||
|
|
||||||
@ -122,4 +126,18 @@ extension BRPlayerListCell: BRPlayerDelegate {
|
|||||||
self.viewModel?.playFinish(player: self)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
43
BeeReel/Class/Player/View/BRRateSelectorCell.swift
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
109
BeeReel/Class/Player/View/BRRateSelectorView.swift
Normal file
@ -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<UITouch>, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
209
BeeReel/Class/Player/View/BRVideoProgressView.swift
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -10,9 +10,13 @@ import UIKit
|
|||||||
|
|
||||||
@objc protocol BRPlayerViewModelDelegate {
|
@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()
|
oldValue?.pause()
|
||||||
|
|
||||||
self.currentPlayer?.isCurrent = true
|
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) {
|
func playFinish(player: BRPlayerProtocol) {
|
||||||
guard (player as? UICollectionViewCell) == (currentPlayer as? UICollectionViewCell) else { return }
|
guard (player as? UICollectionViewCell) == (currentPlayer as? UICollectionViewCell) else { return }
|
||||||
self.delegate?.br_currentVideoPlayFinish?(viewModel: self)
|
self.delegate?.br_currentVideoPlayFinish(viewModel: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
///切换播放跟暂停
|
///切换播放跟暂停
|
||||||
func switchPlayAndPause() {
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,15 @@ class BRPlayer: NSObject {
|
|||||||
return self.player.currentTime
|
return self.player.currentTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rate: Float {
|
||||||
|
get {
|
||||||
|
return self.player.rate
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self.player.rate = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
brLog(message: "播放器销毁")
|
brLog(message: "播放器销毁")
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "GleeStream 1.png",
|
||||||
"platform" : "ios",
|
|
||||||
"size" : "1024x1024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"idiom" : "universal",
|
|
||||||
"platform" : "ios",
|
|
||||||
"size" : "1024x1024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "tinted"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024"
|
||||||
|
After Width: | Height: | Size: 714 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 268 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/arrow_top_icon_01.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 312 B |
22
BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 350 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/ep_icon_01.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 485 B |
22
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 745 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_02_selected.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@2x.png
vendored
Normal file
After Width: | Height: | Size: 859 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03.imageset/未选中收藏@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@2x.png
vendored
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/favorite_icon_03_selected.imageset/收藏1 2@3x.png
vendored
Normal file
After Width: | Height: | Size: 24 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@2x.png
vendored
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/light_icon_01.imageset/选中光效@3x.png
vendored
Normal file
After Width: | Height: | Size: 12 KiB |
22
BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@2x.png
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
BeeReel/Sources/Assets.xcassets/image/highlight_top_image.imageset/Ellipse 13@3x.png
vendored
Normal file
After Width: | Height: | Size: 136 KiB |
@ -1,6 +1,17 @@
|
|||||||
{
|
{
|
||||||
"sourceLanguage" : "en",
|
"sourceLanguage" : "en",
|
||||||
"strings" : {
|
"strings" : {
|
||||||
|
"All ## Episodes" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "All ## Episodes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Browse Genres" : {
|
"Browse Genres" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -23,6 +34,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"EP.##" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "EP.##"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fresh Stories" : {
|
"Fresh Stories" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -56,6 +78,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Speed" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Speed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Spotlight" : {
|
"Spotlight" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|