播放器功能开发
This commit is contained in:
parent
1914557261
commit
3d8dfd2fe8
@ -9,6 +9,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 */; };
|
||||||
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 */; };
|
||||||
|
BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */; };
|
||||||
|
BF3338EC2E154BFE00B10F76 /* BRPlayerControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338EB2E154BFE00B10F76 /* BRPlayerControlView.swift */; };
|
||||||
|
BF3338F02E15569600B10F76 /* BRExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338EF2E15569600B10F76 /* BRExploreViewController.swift */; };
|
||||||
|
BF3338F32E16169A00B10F76 /* BRExplorePlayerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338F22E16169A00B10F76 /* BRExplorePlayerCell.swift */; };
|
||||||
|
BF3338F52E1616B200B10F76 /* BRExploreControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338F42E1616B200B10F76 /* BRExploreControlView.swift */; };
|
||||||
|
BF3338F72E16176900B10F76 /* BRDetailPlayerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338F62E16176900B10F76 /* BRDetailPlayerCell.swift */; };
|
||||||
|
BF3338F92E16178700B10F76 /* BRDetailControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338F82E16178700B10F76 /* BRDetailControlView.swift */; };
|
||||||
|
BF3338FB2E161CF900B10F76 /* NSNumber+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338FA2E161CF000B10F76 /* NSNumber+BRAdd.swift */; };
|
||||||
|
BF3338FD2E1626B000B10F76 /* BRPlayerControlProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */; };
|
||||||
BF692AEB2E0A475D00A5C2DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */; };
|
BF692AEB2E0A475D00A5C2DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */; };
|
||||||
BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE82E0A475D00A5C2DA /* SceneDelegate.swift */; };
|
BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE82E0A475D00A5C2DA /* SceneDelegate.swift */; };
|
||||||
BF692AEE2E0A475D00A5C2DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF692AE22E0A475D00A5C2DA /* Assets.xcassets */; };
|
BF692AEE2E0A475D00A5C2DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF692AE22E0A475D00A5C2DA /* Assets.xcassets */; };
|
||||||
@ -104,6 +114,16 @@
|
|||||||
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; };
|
||||||
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>"; };
|
||||||
|
BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerCache.swift; sourceTree = "<group>"; };
|
||||||
|
BF3338EB2E154BFE00B10F76 /* BRPlayerControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerControlView.swift; sourceTree = "<group>"; };
|
||||||
|
BF3338EF2E15569600B10F76 /* BRExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRExploreViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF3338F22E16169A00B10F76 /* BRExplorePlayerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRExplorePlayerCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF3338F42E1616B200B10F76 /* BRExploreControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRExploreControlView.swift; sourceTree = "<group>"; };
|
||||||
|
BF3338F62E16176900B10F76 /* BRDetailPlayerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRDetailPlayerCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF3338F82E16178700B10F76 /* BRDetailControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRDetailControlView.swift; sourceTree = "<group>"; };
|
||||||
|
BF3338FA2E161CF000B10F76 /* NSNumber+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+BRAdd.swift"; sourceTree = "<group>"; };
|
||||||
|
BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerControlProtocol.swift; sourceTree = "<group>"; };
|
||||||
BF692AC92E0A475500A5C2DA /* BeeReel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeeReel.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
BF692AC92E0A475500A5C2DA /* BeeReel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeeReel.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
BF692AE22E0A475D00A5C2DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
BF692AE22E0A475D00A5C2DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -233,6 +253,32 @@
|
|||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
BF3338ED2E15566C00B10F76 /* Explore */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF3338EE2E15567200B10F76 /* Controller */,
|
||||||
|
BF3338F12E16167B00B10F76 /* View */,
|
||||||
|
);
|
||||||
|
path = Explore;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF3338EE2E15567200B10F76 /* Controller */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF3338EF2E15569600B10F76 /* BRExploreViewController.swift */,
|
||||||
|
);
|
||||||
|
path = Controller;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF3338F12E16167B00B10F76 /* View */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF3338F22E16169A00B10F76 /* BRExplorePlayerCell.swift */,
|
||||||
|
BF3338F42E1616B200B10F76 /* BRExploreControlView.swift */,
|
||||||
|
);
|
||||||
|
path = View;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
BF692AC02E0A475500A5C2DA = {
|
BF692AC02E0A475500A5C2DA = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -302,6 +348,7 @@
|
|||||||
BF692AF52E0A47D400A5C2DA /* Class */ = {
|
BF692AF52E0A47D400A5C2DA /* Class */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF3338ED2E15566C00B10F76 /* Explore */,
|
||||||
BF692B682E0BC78C00A5C2DA /* Home */,
|
BF692B682E0BC78C00A5C2DA /* Home */,
|
||||||
BF692B4A2E0AA84C00A5C2DA /* Player */,
|
BF692B4A2E0AA84C00A5C2DA /* Player */,
|
||||||
);
|
);
|
||||||
@ -370,6 +417,8 @@
|
|||||||
BF692B0C2E0A7A9A00A5C2DA /* Extension */ = {
|
BF692B0C2E0A7A9A00A5C2DA /* Extension */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF3338FA2E161CF000B10F76 /* NSNumber+BRAdd.swift */,
|
||||||
|
BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */,
|
||||||
BFC676BD2E13A8DD006659E5 /* UIScrollView+BRRefresh.swift */,
|
BFC676BD2E13A8DD006659E5 /* UIScrollView+BRRefresh.swift */,
|
||||||
BFC6768C2E123D67006659E5 /* AttributedString+BRAdd.swift */,
|
BFC6768C2E123D67006659E5 /* AttributedString+BRAdd.swift */,
|
||||||
BFC6767A2E0E9736006659E5 /* UIStackView+BRAdd.swift */,
|
BFC6767A2E0E9736006659E5 /* UIStackView+BRAdd.swift */,
|
||||||
@ -444,6 +493,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF692B462E0A9B7400A5C2DA /* BRPlayer.swift */,
|
BF692B462E0A9B7400A5C2DA /* BRPlayer.swift */,
|
||||||
|
BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */,
|
||||||
);
|
);
|
||||||
path = Player;
|
path = Player;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -472,6 +522,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF692B592E0AAADD00A5C2DA /* BRPlayerListCell.swift */,
|
BF692B592E0AAADD00A5C2DA /* BRPlayerListCell.swift */,
|
||||||
|
BF3338EB2E154BFE00B10F76 /* BRPlayerControlView.swift */,
|
||||||
|
BF3338F62E16176900B10F76 /* BRDetailPlayerCell.swift */,
|
||||||
|
BF3338F82E16178700B10F76 /* BRDetailControlView.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -479,6 +532,7 @@
|
|||||||
BF692B4E2E0AA88600A5C2DA /* Model */ = {
|
BF692B4E2E0AA88600A5C2DA /* Model */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */,
|
||||||
BFC676802E122733006659E5 /* BRPlayerProtocol.swift */,
|
BFC676802E122733006659E5 /* BRPlayerProtocol.swift */,
|
||||||
BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */,
|
BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */,
|
||||||
);
|
);
|
||||||
@ -800,6 +854,7 @@
|
|||||||
BFC676732E0E938B006659E5 /* BRTableView.swift in Sources */,
|
BFC676732E0E938B006659E5 /* BRTableView.swift in Sources */,
|
||||||
BFC676932E126A62006659E5 /* BRSpotlightNewMainCell.swift in Sources */,
|
BFC676932E126A62006659E5 /* BRSpotlightNewMainCell.swift in Sources */,
|
||||||
BFC6768D2E123D6E006659E5 /* AttributedString+BRAdd.swift in Sources */,
|
BFC6768D2E123D6E006659E5 /* AttributedString+BRAdd.swift in Sources */,
|
||||||
|
BF3338F52E1616B200B10F76 /* BRExploreControlView.swift in Sources */,
|
||||||
BF692B132E0A7B9000A5C2DA /* BRUserInfo.swift in Sources */,
|
BF692B132E0A7B9000A5C2DA /* BRUserInfo.swift in Sources */,
|
||||||
BF692B042E0A76D200A5C2DA /* BRLoginManager.swift in Sources */,
|
BF692B042E0A76D200A5C2DA /* BRLoginManager.swift in Sources */,
|
||||||
BFC6769D2E129794006659E5 /* BRHomeTop10ViewController.swift in Sources */,
|
BFC6769D2E129794006659E5 /* BRHomeTop10ViewController.swift in Sources */,
|
||||||
@ -808,6 +863,7 @@
|
|||||||
BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */,
|
BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */,
|
||||||
BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */,
|
BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */,
|
||||||
BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */,
|
BF692B542E0AA8FA00A5C2DA /* BRCollectionView.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 */,
|
||||||
@ -818,6 +874,8 @@
|
|||||||
BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */,
|
BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */,
|
||||||
BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */,
|
BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */,
|
||||||
BF692B162E0A7CD600A5C2DA /* BRHUD.swift in Sources */,
|
BF692B162E0A7CD600A5C2DA /* BRHUD.swift in Sources */,
|
||||||
|
BF3338F72E16176900B10F76 /* BRDetailPlayerCell.swift in Sources */,
|
||||||
|
BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */,
|
||||||
BFC676952E126BBF006659E5 /* BRSpotlightNewCell.swift in Sources */,
|
BFC676952E126BBF006659E5 /* BRSpotlightNewCell.swift in Sources */,
|
||||||
BF692B402E0A8FA100A5C2DA /* UIColor+BRAdd.swift in Sources */,
|
BF692B402E0A8FA100A5C2DA /* UIColor+BRAdd.swift in Sources */,
|
||||||
BF692B102E0A7B4300A5C2DA /* BRUserDefaultsKey.swift in Sources */,
|
BF692B102E0A7B4300A5C2DA /* BRUserDefaultsKey.swift in Sources */,
|
||||||
@ -830,6 +888,7 @@
|
|||||||
BFC676812E122733006659E5 /* BRPlayerProtocol.swift in Sources */,
|
BFC676812E122733006659E5 /* BRPlayerProtocol.swift in Sources */,
|
||||||
BFC676BC2E138ABB006659E5 /* BRNewReleasesViewController.swift in Sources */,
|
BFC676BC2E138ABB006659E5 /* BRNewReleasesViewController.swift in Sources */,
|
||||||
BF692B5F2E0B812800A5C2DA /* BRTabBarItemContainer.swift in Sources */,
|
BF692B5F2E0B812800A5C2DA /* BRTabBarItemContainer.swift in Sources */,
|
||||||
|
BF3338EC2E154BFE00B10F76 /* BRPlayerControlView.swift in Sources */,
|
||||||
BF692B652E0BC53900A5C2DA /* CGMutablePath+BRRoundedCorner.swift in Sources */,
|
BF692B652E0BC53900A5C2DA /* CGMutablePath+BRRoundedCorner.swift in Sources */,
|
||||||
BFC676752E0E93B3006659E5 /* BRTableViewCell.swift in Sources */,
|
BFC676752E0E93B3006659E5 /* BRTableViewCell.swift in Sources */,
|
||||||
BF78108B2E0D4EB3007DEEBC /* BRURLPath.swift in Sources */,
|
BF78108B2E0D4EB3007DEEBC /* BRURLPath.swift in Sources */,
|
||||||
@ -839,16 +898,20 @@
|
|||||||
BFC676BE2E13A8EB006659E5 /* UIScrollView+BRRefresh.swift in Sources */,
|
BFC676BE2E13A8EB006659E5 /* UIScrollView+BRRefresh.swift in Sources */,
|
||||||
BF692B182E0A7D8900A5C2DA /* BRToast.swift in Sources */,
|
BF692B182E0A7D8900A5C2DA /* BRToast.swift in Sources */,
|
||||||
BF692B0E2E0A7AF300A5C2DA /* UserDefaults+BRAdd.swift in Sources */,
|
BF692B0E2E0A7AF300A5C2DA /* UserDefaults+BRAdd.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 */,
|
||||||
BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */,
|
BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.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 */,
|
||||||
|
BF3338F92E16178700B10F76 /* BRDetailControlView.swift in Sources */,
|
||||||
BFC676AB2E1372BD006659E5 /* BRHomeTop3Cell.swift in Sources */,
|
BFC676AB2E1372BD006659E5 /* BRHomeTop3Cell.swift in Sources */,
|
||||||
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 */,
|
||||||
BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */,
|
BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */,
|
||||||
|
BF3338FB2E161CF900B10F76 /* NSNumber+BRAdd.swift in Sources */,
|
||||||
BF692B222E0A820D00A5C2DA /* String+BRAdd.swift in Sources */,
|
BF692B222E0A820D00A5C2DA /* String+BRAdd.swift in Sources */,
|
||||||
BF692B632E0B9D4800A5C2DA /* BRTabBarItem.swift in Sources */,
|
BF692B632E0B9D4800A5C2DA /* BRTabBarItem.swift in Sources */,
|
||||||
BFC6768B2E123690006659E5 /* BRVideoRevolutionManager.swift in Sources */,
|
BFC6768B2E123690006659E5 /* BRVideoRevolutionManager.swift in Sources */,
|
||||||
@ -867,6 +930,7 @@
|
|||||||
BF692B732E0D397700A5C2DA /* BRHomeAPI.swift in Sources */,
|
BF692B732E0D397700A5C2DA /* BRHomeAPI.swift in Sources */,
|
||||||
BF692B7A2E0D3BD300A5C2DA /* BRShortModel.swift in Sources */,
|
BF692B7A2E0D3BD300A5C2DA /* BRShortModel.swift in Sources */,
|
||||||
BFC676712E0E9234006659E5 /* BRSpotlightViewViewController.swift in Sources */,
|
BFC676712E0E9234006659E5 /* BRSpotlightViewViewController.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 */,
|
||||||
BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */,
|
BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */,
|
||||||
|
@ -71,7 +71,7 @@ extension BRTabBarController {
|
|||||||
|
|
||||||
private func br_setup() {
|
private func br_setup() {
|
||||||
let nav1 = createNavigationController(viewController: BRHomeViewController(), title: "首页".localized, image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected"))
|
let nav1 = createNavigationController(viewController: BRHomeViewController(), title: "首页".localized, image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected"))
|
||||||
let nav2 = createNavigationController(viewController: BRViewController(), title: "推荐".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected"))
|
let nav2 = createNavigationController(viewController: BRExploreViewController(), title: "推荐".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected"))
|
||||||
let nav3 = createNavigationController(viewController: BRViewController(), title: "首页".localized, image: UIImage(named: "tabbar_icon_03"), selectedImage: UIImage(named: "tabbar_icon_03_selected"))
|
let nav3 = createNavigationController(viewController: BRViewController(), title: "首页".localized, image: UIImage(named: "tabbar_icon_03"), selectedImage: UIImage(named: "tabbar_icon_03_selected"))
|
||||||
let nav4 = createNavigationController(viewController: BRViewController(), title: "首页".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected"))
|
let nav4 = createNavigationController(viewController: BRViewController(), title: "首页".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected"))
|
||||||
|
|
||||||
|
@ -25,6 +25,14 @@ class BRViewController: UIViewController {
|
|||||||
self.isViewDidLoad = true
|
self.isViewDidLoad = true
|
||||||
self.view.backgroundColor = .backgroundColor()
|
self.view.backgroundColor = .backgroundColor()
|
||||||
|
|
||||||
|
if let navi = navigationController {
|
||||||
|
if navi.visibleViewController == self {
|
||||||
|
if navi.viewControllers.count > 1 {
|
||||||
|
configNavigationBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -56,3 +64,69 @@ class BRViewController: UIViewController {
|
|||||||
completer?()
|
completer?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension UIViewController {
|
||||||
|
func configNavigationBack(_ imageName: String = "nav_back_icon_01") {
|
||||||
|
let image = UIImage(named: imageName)
|
||||||
|
|
||||||
|
let leftBarButtonItem = UIBarButtonItem(image: image, style: .plain ,target: self,action: #selector(handleNavBack))
|
||||||
|
navigationItem.leftBarButtonItem = leftBarButtonItem
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handleNavBack() {
|
||||||
|
self.br_toLastViewController(animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func br_toLastViewController(animated: Bool) {
|
||||||
|
if self.navigationController != nil
|
||||||
|
{
|
||||||
|
if self.navigationController?.viewControllers.count == 1
|
||||||
|
{
|
||||||
|
self.dismiss(animated: animated, completion: nil)
|
||||||
|
} else {
|
||||||
|
self.navigationController?.popViewController(animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if self.presentingViewController != nil {
|
||||||
|
self.dismiss(animated: animated, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIViewController {
|
||||||
|
///设置导航默认样式
|
||||||
|
func br_setNavigationNormalStyle(backgroundColor: UIColor = .clear,
|
||||||
|
isTranslucent: Bool = true,
|
||||||
|
prefersLargeTitles: Bool = false)
|
||||||
|
{
|
||||||
|
self.br_setNavigationBackgroundColor(color: backgroundColor, isTranslucent: isTranslucent)
|
||||||
|
self.br_setNavigationTitleStyle()
|
||||||
|
self.navigationController?.navigationBar.prefersLargeTitles = prefersLargeTitles
|
||||||
|
}
|
||||||
|
|
||||||
|
///设置导航背景色
|
||||||
|
func br_setNavigationBackgroundColor(color: UIColor?, isTranslucent: Bool = false) {
|
||||||
|
guard let nav = navigationController else { return }
|
||||||
|
if nav.visibleViewController == self {
|
||||||
|
nav.navigationBar.br_setBackgroundColor(backgroundColor: color)
|
||||||
|
nav.navigationBar.br_setTranslucent(isTranslucent: isTranslucent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///设置标题样式
|
||||||
|
func br_setNavigationTitleStyle(color: UIColor? = nil, font: UIFont? = nil) {
|
||||||
|
guard let nav = navigationController else { return }
|
||||||
|
if nav.visibleViewController == self {
|
||||||
|
//标题样式
|
||||||
|
var titleTextAttributes = UINavigationBar.br_normalTitleTextAttributes
|
||||||
|
if let color = color {
|
||||||
|
titleTextAttributes[NSAttributedString.Key.foregroundColor] = color
|
||||||
|
}
|
||||||
|
if let font = font {
|
||||||
|
titleTextAttributes[NSAttributedString.Key.font] = font
|
||||||
|
}
|
||||||
|
nav.navigationBar.br_setTitleTextAttributes(titleTextAttributes: titleTextAttributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
39
BeeReel/Base/Extension/NSNumber+BRAdd.swift
Normal file
39
BeeReel/Base/Extension/NSNumber+BRAdd.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// NSNumber+BRAdd.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension NSNumber {
|
||||||
|
|
||||||
|
func br_toString(maximumFractionDigits: Int = 10, minimumFractionDigits: Int? = nil, roundingMode: NumberFormatter.RoundingMode? = nil) -> String {
|
||||||
|
let formatter = NumberFormatter()
|
||||||
|
formatter.minimumIntegerDigits = 1
|
||||||
|
formatter.maximumFractionDigits = maximumFractionDigits
|
||||||
|
if let minimumFractionDigits = minimumFractionDigits {
|
||||||
|
formatter.minimumFractionDigits = minimumFractionDigits
|
||||||
|
}
|
||||||
|
if let roundingMode = roundingMode {
|
||||||
|
formatter.roundingMode = roundingMode
|
||||||
|
}
|
||||||
|
formatter.numberStyle = .none
|
||||||
|
return formatter.string(from: self) ?? "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension Int {
|
||||||
|
func br_formatTimeGroup() -> (String, String, String) {
|
||||||
|
let seconds = self
|
||||||
|
|
||||||
|
var s: String = "00"
|
||||||
|
var m: String = "00"
|
||||||
|
var h: String = "00"
|
||||||
|
s = String(format: "%02d", Int(Int(seconds) % 60))
|
||||||
|
m = String(format: "%02d", Int(seconds / 60) % 60)
|
||||||
|
h = String(format: "%02d", Int(seconds / 3600))
|
||||||
|
|
||||||
|
return (h, m, s)
|
||||||
|
}
|
||||||
|
}
|
@ -58,4 +58,8 @@ extension UIColor {
|
|||||||
static func colorE3FC37(alpha: CGFloat = 1) -> UIColor {
|
static func colorE3FC37(alpha: CGFloat = 1) -> UIColor {
|
||||||
return UIColor(rgb: 0xE3FC37, alpha: alpha)
|
return UIColor(rgb: 0xE3FC37, alpha: alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func color5A5D45(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0x5A5D45, alpha: alpha)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
88
BeeReel/Base/Extension/UINavigationBar+BRAdd.swift
Normal file
88
BeeReel/Base/Extension/UINavigationBar+BRAdd.swift
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// UINavigationBar+BRAdd.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UINavigationBar {
|
||||||
|
|
||||||
|
static let br_normalTitleFont = UIFont.fontBold(ofSize: 16)
|
||||||
|
static var br_normalTitleColor: UIColor {
|
||||||
|
get {
|
||||||
|
return .colorFFFFFF()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
默认标题样式
|
||||||
|
*/
|
||||||
|
static var br_normalTitleTextAttributes: [NSAttributedString.Key : Any] {
|
||||||
|
get {
|
||||||
|
return [
|
||||||
|
NSAttributedString.Key.font : br_normalTitleFont,
|
||||||
|
NSAttributedString.Key.foregroundColor : br_normalTitleColor
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
默认背景色
|
||||||
|
*/
|
||||||
|
static var br_normalBackgroundColor: UIColor {
|
||||||
|
get {
|
||||||
|
return .black
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
static let navBarAppearance: UINavigationBarAppearance = {
|
||||||
|
let navBarAppearance = UINavigationBarAppearance()
|
||||||
|
navBarAppearance.configureWithOpaqueBackground()
|
||||||
|
// 背景色
|
||||||
|
navBarAppearance.backgroundColor = br_normalBackgroundColor
|
||||||
|
// 去掉半透明效果
|
||||||
|
navBarAppearance.backgroundEffect = nil
|
||||||
|
// 去除导航栏阴影(如果不设置clear,导航栏底下会有一条阴影线)
|
||||||
|
navBarAppearance.shadowColor = UIColor.clear
|
||||||
|
// 字体颜色
|
||||||
|
navBarAppearance.titleTextAttributes = br_normalTitleTextAttributes
|
||||||
|
|
||||||
|
return navBarAppearance
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
func br_setTranslucent(isTranslucent: Bool) {
|
||||||
|
self.isTranslucent = isTranslucent
|
||||||
|
}
|
||||||
|
|
||||||
|
func br_setBackgroundColor(backgroundColor: UIColor?) {
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
UINavigationBar.navBarAppearance.backgroundColor = backgroundColor
|
||||||
|
self.standardAppearance = UINavigationBar.navBarAppearance
|
||||||
|
self.scrollEdgeAppearance = UINavigationBar.navBarAppearance
|
||||||
|
}
|
||||||
|
|
||||||
|
if let backgroundColor = backgroundColor {
|
||||||
|
self.setBackgroundImage(UIImage(color: backgroundColor), for: .default)
|
||||||
|
} else {
|
||||||
|
self.setBackgroundImage(UIImage(), for: .default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func br_setTitleTextAttributes(titleTextAttributes: [NSAttributedString.Key : Any]?) {
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
if let titleTextAttributes = titleTextAttributes {
|
||||||
|
UINavigationBar.navBarAppearance.titleTextAttributes = titleTextAttributes
|
||||||
|
}
|
||||||
|
self.scrollEdgeAppearance = UINavigationBar.navBarAppearance
|
||||||
|
self.standardAppearance = UINavigationBar.navBarAppearance
|
||||||
|
} else {
|
||||||
|
|
||||||
|
self.titleTextAttributes = titleTextAttributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -66,6 +66,28 @@ class BRVideoAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///推荐短剧
|
||||||
|
static func requestExploreVideo(page: Int, revolution: BRShortModel.VideoRevolution? = nil, completer: ((_ listModel: BRListModel<BRShortModel>?) -> Void)?) {
|
||||||
|
|
||||||
|
var parameters: [String : Any] = [
|
||||||
|
"page_size" : 20,
|
||||||
|
"current_page" : page
|
||||||
|
]
|
||||||
|
|
||||||
|
if let revolution = revolution?.rawValue {
|
||||||
|
parameters["revolution"] = revolution
|
||||||
|
}
|
||||||
|
|
||||||
|
var param = BRNetworkParameters(path: "/getRecommands")
|
||||||
|
param.method = .get
|
||||||
|
param.parameters = parameters
|
||||||
|
|
||||||
|
|
||||||
|
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<BRListModel<BRShortModel>>) in
|
||||||
|
completer?(response.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
123
BeeReel/Class/Explore/Controller/BRExploreViewController.swift
Normal file
123
BeeReel/Class/Explore/Controller/BRExploreViewController.swift
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
//
|
||||||
|
// BRExploreViewController.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRExploreViewController: BRPlayerListViewController {
|
||||||
|
|
||||||
|
|
||||||
|
private lazy var dataArr: [BRShortModel] = []
|
||||||
|
private lazy var page: Int = 1
|
||||||
|
private var pagination: BRListPaginationModel?
|
||||||
|
|
||||||
|
override var CellClass: BRPlayerListCell.Type {
|
||||||
|
return BRExplorePlayerCell.self
|
||||||
|
}
|
||||||
|
|
||||||
|
override var contentSize: CGSize {
|
||||||
|
let width = UIScreen.width - 30
|
||||||
|
let height = UIScreen.height - UIScreen.customTabBarHeight - 40 - 50 - UIScreen.statusBarHeight
|
||||||
|
return CGSize(width: width, height: height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
self.edgesForExtendedLayout = .all
|
||||||
|
self.delegate = self
|
||||||
|
self.dataSource = self
|
||||||
|
|
||||||
|
self.requestDataArr(page: 1)
|
||||||
|
|
||||||
|
br_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func addDataArr(dataArr: [BRShortModel]) {
|
||||||
|
guard dataArr.count > 0 else { return }
|
||||||
|
|
||||||
|
var indexPaths: [IndexPath] = []
|
||||||
|
var startRow = self.dataArr.count
|
||||||
|
|
||||||
|
dataArr.forEach { _ in
|
||||||
|
indexPaths.append(IndexPath(row: startRow, section: 0))
|
||||||
|
startRow += 1
|
||||||
|
}
|
||||||
|
self.dataArr += dataArr
|
||||||
|
|
||||||
|
CATransaction.setCompletionBlock(nil)
|
||||||
|
CATransaction.begin()
|
||||||
|
self.collectionView.insertItems(at: indexPaths)
|
||||||
|
CATransaction.commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRExploreViewController {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
self.view.backgroundColor = .color1C1C1C()
|
||||||
|
self.collectionView.layer.cornerRadius = 20
|
||||||
|
self.collectionView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
self.collectionView.snp.remakeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview().offset(UIScreen.statusBarHeight + 50)
|
||||||
|
make.width.equalTo(contentSize.width)
|
||||||
|
make.height.equalTo(contentSize.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: -------------- BRPlayerListViewControllerDelegate BRPlayerListViewControllerDataSource --------------
|
||||||
|
extension BRExploreViewController: BRPlayerListViewControllerDelegate, BRPlayerListViewControllerDataSource {
|
||||||
|
|
||||||
|
func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell {
|
||||||
|
let model = dataArr[indexPath.row]
|
||||||
|
if let cell = oldCell as? BRPlayerListCell {
|
||||||
|
cell.shortModel = model
|
||||||
|
cell.videoInfo = model.video_info
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldCell
|
||||||
|
}
|
||||||
|
|
||||||
|
func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return dataArr.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension BRExploreViewController {
|
||||||
|
|
||||||
|
private func requestDataArr(page: Int, completer: (() -> Void)? = nil) {
|
||||||
|
|
||||||
|
BRVideoAPI.requestExploreVideo(page: page, revolution: BRVideoRevolutionManager.manager.revolution) { [weak self] listModel in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.collectionView.isHidden = false
|
||||||
|
if let listModel = listModel, let list = listModel.list {
|
||||||
|
if page == 1 {
|
||||||
|
self.dataArr = list
|
||||||
|
self.clearData()
|
||||||
|
self.reloadData()
|
||||||
|
} else {
|
||||||
|
self.addDataArr(dataArr: list)
|
||||||
|
}
|
||||||
|
self.pagination = listModel.pagination
|
||||||
|
}
|
||||||
|
completer?()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
96
BeeReel/Class/Explore/View/BRExploreControlView.swift
Normal file
96
BeeReel/Class/Explore/View/BRExploreControlView.swift
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// BRExploreControlView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRExploreControlView: BRPlayerControlView {
|
||||||
|
|
||||||
|
|
||||||
|
override var videoInfo: BRVideoInfoModel? {
|
||||||
|
didSet {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var shortModel: BRShortModel? {
|
||||||
|
didSet {
|
||||||
|
hotView.setNeedsUpdateConfiguration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var hotView: UIButton = {
|
||||||
|
var config = UIButton.Configuration.plain()
|
||||||
|
config.image = UIImage(named: "hot_icon_03")
|
||||||
|
config.imagePadding = 3
|
||||||
|
config.imagePlacement = .leading
|
||||||
|
config.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10)
|
||||||
|
|
||||||
|
let button = UIButton(configuration: config)
|
||||||
|
button.isUserInteractionEnabled = false
|
||||||
|
button.backgroundColor = .color5A5D45(alpha: 0.2)
|
||||||
|
button.layer.cornerRadius = 17
|
||||||
|
button.layer.masksToBounds = true
|
||||||
|
button.configurationUpdateHandler = { [weak self] button in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let count = shortModel?.watch_total ?? 0
|
||||||
|
var string = "\(count)"
|
||||||
|
if count >= 1000 {
|
||||||
|
let num = NSNumber(value: Float(count) / 1000)
|
||||||
|
string = num.br_toString(maximumFractionDigits: 1, minimumFractionDigits: 0) + "k"
|
||||||
|
}
|
||||||
|
button.configuration?.attributedTitle = AttributedString.br_createAttributedString(string: string, color: .colorFFFFFF(), font: .fontMedium(ofSize: 12))
|
||||||
|
}
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var fullButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setImage(UIImage(named: "full_icon_01"), for: .normal)
|
||||||
|
button.setBackgroundImage(UIImage(color: .color5A5D45(alpha: 0.2)), for: .normal)
|
||||||
|
button.layer.cornerRadius = 17
|
||||||
|
button.layer.masksToBounds = true
|
||||||
|
button.addTarget(self, action: #selector(handleFullButton), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
br_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleFullButton() {
|
||||||
|
let vc = BRVideoDetailViewController()
|
||||||
|
vc.shortPlayId = self.shortModel?.short_play_id
|
||||||
|
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRExploreControlView {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
addSubview(hotView)
|
||||||
|
addSubview(fullButton)
|
||||||
|
|
||||||
|
hotView.snp.makeConstraints { make in
|
||||||
|
make.right.equalToSuperview().offset(-10)
|
||||||
|
make.top.equalToSuperview().offset(10)
|
||||||
|
make.height.equalTo(34)
|
||||||
|
}
|
||||||
|
|
||||||
|
fullButton.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(10)
|
||||||
|
make.centerY.equalTo(hotView)
|
||||||
|
make.width.equalTo(40)
|
||||||
|
make.height.equalTo(34)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
BeeReel/Class/Explore/View/BRExplorePlayerCell.swift
Normal file
17
BeeReel/Class/Explore/View/BRExplorePlayerCell.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// BRExplorePlayerCell.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRExplorePlayerCell: BRPlayerListCell {
|
||||||
|
|
||||||
|
override var ControlViewClass: BRPlayerControlView.Type {
|
||||||
|
return BRExploreControlView.self
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SJMediaCacheServer
|
||||||
|
|
||||||
|
|
||||||
@objc protocol BRPlayerListViewControllerDelegate {
|
@objc protocol BRPlayerListViewControllerDelegate {
|
||||||
@ -44,11 +45,18 @@ class BRPlayerListViewController: BRViewController {
|
|||||||
return BRPlayerListCell.self
|
return BRPlayerListCell.self
|
||||||
}
|
}
|
||||||
|
|
||||||
private(set) lazy var viewModel = BRPlayerViewModel()
|
private(set) lazy var viewModel: BRPlayerViewModel = {
|
||||||
|
let vm = BRPlayerViewModel()
|
||||||
|
vm.delegate = self
|
||||||
|
return vm
|
||||||
|
}()
|
||||||
|
|
||||||
weak var delegate: BRPlayerListViewControllerDelegate?
|
weak var delegate: BRPlayerListViewControllerDelegate?
|
||||||
weak var dataSource: BRPlayerListViewControllerDataSource?
|
weak var dataSource: BRPlayerListViewControllerDataSource?
|
||||||
|
|
||||||
|
///预加载
|
||||||
|
private var prePrefetchTask: MCSPrefetchTask?
|
||||||
|
private var nextPrefetchTask: MCSPrefetchTask?
|
||||||
|
|
||||||
private lazy var collectionViewLayout: UICollectionViewLayout = {
|
private lazy var collectionViewLayout: UICollectionViewLayout = {
|
||||||
let layout = UICollectionViewFlowLayout()
|
let layout = UICollectionViewFlowLayout()
|
||||||
@ -67,13 +75,19 @@ 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: "cell")
|
collectionView.register(CellClass.self, forCellWithReuseIdentifier: "playerCell")
|
||||||
return collectionView
|
return collectionView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.prePrefetchTask?.cancel()
|
||||||
|
self.nextPrefetchTask?.cancel()
|
||||||
|
self.collectionView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
self.statusBarStyle = .lightContent
|
||||||
br_setupUI()
|
br_setupUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +95,30 @@ class BRPlayerListViewController: BRViewController {
|
|||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
if self.viewModel.isPlaying {
|
||||||
|
self.viewModel.currentPlayer?.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidDisappear(_ animated: Bool) {
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
self.viewModel.currentPlayer?.pause()
|
||||||
|
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.scrollDidEnd(self.collectionView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///返回上个视频路径 需要子类重写
|
||||||
|
var previousVideoUrl: String? { return nil }
|
||||||
|
///返回下个视频路径
|
||||||
|
var nextVideoUrl: String? { return nil }
|
||||||
|
|
||||||
func play() {
|
func play() {
|
||||||
if self.isDidAppear {
|
if self.isDidAppear {
|
||||||
self.viewModel.currentPlayer?.start()
|
self.viewModel.currentPlayer?.start()
|
||||||
@ -93,6 +131,47 @@ class BRPlayerListViewController: BRViewController {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reloadData(completion: (() -> Void)? = nil) {
|
||||||
|
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.collectionView.performBatchUpdates(nil) { [weak self] finish in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let cell = self.collectionView.cellForItem(at: viewModel.currentIndexPath) as? BRPlayerProtocol
|
||||||
|
self.viewModel.currentPlayer = cell
|
||||||
|
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollToItem(indexPath: IndexPath, animated: Bool = true, completer: (() -> Void)? = nil) {
|
||||||
|
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
self.collectionView.scrollToItem(at: indexPath, at: .top, animated: animated);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.collectionView.performBatchUpdates(nil) { [weak self] _ in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if !animated {
|
||||||
|
if viewModel.currentIndexPath != indexPath {
|
||||||
|
self.skip(indexPath: indexPath)
|
||||||
|
} else {
|
||||||
|
self.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completer?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearData() {
|
||||||
|
self.viewModel.currentPlayer = nil
|
||||||
|
self.viewModel.currentIndexPath = .init(row: 0, section: 0)
|
||||||
|
self.collectionView.contentOffset = .init(x: 0, y: 0)
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BRPlayerListViewController {
|
extension BRPlayerListViewController {
|
||||||
@ -113,7 +192,7 @@ 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: "cell", for: indexPath)
|
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "playerCell", for: indexPath)
|
||||||
if let newCell = self.dataSource?.br_playerListViewController(self, collectionView, cellForItemAt: indexPath, oldCell: cell) {
|
if let newCell = self.dataSource?.br_playerListViewController(self, collectionView, cellForItemAt: indexPath, oldCell: cell) {
|
||||||
cell = newCell
|
cell = newCell
|
||||||
}
|
}
|
||||||
@ -123,10 +202,25 @@ extension BRPlayerListViewController: UICollectionViewDelegate, UICollectionView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.viewModel.currentPlayer == nil, indexPath == viewModel.currentIndexPath, let playerProtocol = cell as? BRPlayerProtocol {
|
||||||
|
viewModel.currentIndexPath = indexPath
|
||||||
|
self.viewModel.currentPlayer = playerProtocol
|
||||||
|
didChangeIndexPathForVisible()
|
||||||
|
}
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
||||||
|
|
||||||
|
self.prePrefetchTask?.cancel()
|
||||||
|
self.nextPrefetchTask?.cancel()
|
||||||
|
|
||||||
|
self.prePrefetchTask = self.prefetchTask(url: self.previousVideoUrl)
|
||||||
|
self.nextPrefetchTask = self.prefetchTask(url: self.nextVideoUrl)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
if let count = self.dataSource?.br_playerListViewController(self, collectionView, numberOfItemsInSection: section) {
|
if let count = self.dataSource?.br_playerListViewController(self, collectionView, numberOfItemsInSection: section) {
|
||||||
return count
|
return count
|
||||||
@ -134,6 +228,82 @@ extension BRPlayerListViewController: UICollectionViewDelegate, UICollectionView
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//滑动停止
|
||||||
|
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
|
scrollDidEnd(scrollView)
|
||||||
|
brLog(message: "scrollViewDidEndDecelerating+++++++++++++++")
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
|
||||||
|
scrollDidEnd(scrollView)
|
||||||
|
brLog(message: "scrollViewDidEndScrollingAnimation+++++++++++++++")
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||||
|
// if !decelerate {
|
||||||
|
// scrollDidEnd(scrollView)
|
||||||
|
// }
|
||||||
|
brLog(message: "scrollViewDidEndDragging+++++++++++++++\(decelerate)")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func scrollDidEnd(_ scrollView: UIScrollView) {
|
||||||
|
let offsetY = scrollView.contentOffset.y
|
||||||
|
let indexPaths = self.collectionView.indexPathsForVisibleItems
|
||||||
|
for indexPath in indexPaths {
|
||||||
|
guard let cell = self.collectionView.cellForItem(at: indexPath) else { continue }
|
||||||
|
if floor(offsetY) == floor(cell.frame.origin.y) {
|
||||||
|
if viewModel.currentIndexPath != indexPath {
|
||||||
|
self.skip(indexPath: indexPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func skip(indexPath: IndexPath) {
|
||||||
|
guard let currentPlayer = self.collectionView.cellForItem(at: indexPath) as? BRPlayerProtocol else { return }
|
||||||
|
viewModel.currentIndexPath = indexPath
|
||||||
|
self.viewModel.currentPlayer = currentPlayer
|
||||||
|
didChangeIndexPathForVisible()
|
||||||
|
self.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- BRPlayerViewModelDelegate --------------
|
||||||
|
extension BRPlayerListViewController: BRPlayerViewModelDelegate {
|
||||||
|
|
||||||
|
func br_currentVideoPlayFinish(viewModel: BRPlayerViewModel) {
|
||||||
|
scrollToNextEpisode()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRPlayerListViewController {
|
||||||
|
|
||||||
|
///滑动至下一级
|
||||||
|
private func scrollToNextEpisode() {
|
||||||
|
|
||||||
|
var contentOffset = self.collectionView.contentOffset
|
||||||
|
|
||||||
|
if hasNextEpisode() {
|
||||||
|
contentOffset.y = floor(contentOffset.y + self.contentSize.height)
|
||||||
|
self.collectionView.setContentOffset(contentOffset, animated: true)
|
||||||
|
} else {
|
||||||
|
self.viewModel.currentPlayer?.replay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///是否还有下一级
|
||||||
|
private func hasNextEpisode() -> Bool {
|
||||||
|
let contentOffset = self.collectionView.contentOffset
|
||||||
|
let contentSize = self.collectionView.contentSize
|
||||||
|
if contentOffset.y >= contentSize.height - self.contentSize.height {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -154,3 +324,11 @@ extension BRPlayerListViewController {
|
|||||||
self.delegate?.br_playerListViewController?(self, didChangeIndexPathForVisible: viewModel.currentIndexPath)
|
self.delegate?.br_playerListViewController?(self, didChangeIndexPathForVisible: viewModel.currentIndexPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- 预加载 --------------
|
||||||
|
extension BRPlayerListViewController {
|
||||||
|
|
||||||
|
private func prefetchTask(url: String?) -> MCSPrefetchTask? {
|
||||||
|
return BRPlayerCache.prefetch(urlString: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,23 +10,91 @@ import UIKit
|
|||||||
class BRVideoDetailViewController: BRPlayerListViewController {
|
class BRVideoDetailViewController: BRPlayerListViewController {
|
||||||
|
|
||||||
|
|
||||||
|
override var contentSize: CGSize {
|
||||||
|
return .init(width: UIScreen.width, height: UIScreen.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override var CellClass: BRPlayerListCell.Type {
|
||||||
|
return BRDetailPlayerCell.self
|
||||||
|
}
|
||||||
|
|
||||||
var shortPlayId: String?
|
var shortPlayId: String?
|
||||||
var activityId: String?
|
var activityId: String?
|
||||||
|
|
||||||
private var detailModel: BRVideoDetailModel?
|
private var detailModel: BRVideoDetailModel?
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: UI属性
|
||||||
|
private lazy var backButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setImage(UIImage(named: "nav_back_icon_01"), for: .normal)
|
||||||
|
button.addTarget(self, action: #selector(handleNavBack), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
self.delegate = self
|
||||||
|
self.dataSource = self
|
||||||
|
|
||||||
self.requestDetailData()
|
self.requestDetailData()
|
||||||
|
|
||||||
|
br_setupUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override var previousVideoUrl: String? {
|
||||||
|
let index = self.viewModel.currentIndexPath.row - 1
|
||||||
|
guard index > 0 else { return nil }
|
||||||
|
return detailModel?.episodeList?[index].video_url
|
||||||
|
}
|
||||||
|
|
||||||
|
override var nextVideoUrl: String? {
|
||||||
|
let index = self.viewModel.currentIndexPath.row + 1
|
||||||
|
guard index < (detailModel?.episodeList?.count ?? 0) else { return nil }
|
||||||
|
return detailModel?.episodeList?[index].video_url
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension BRVideoDetailViewController {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
view.addSubview(backButton)
|
||||||
|
|
||||||
|
backButton.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview().offset(UIScreen.statusBarHeight + 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: -------------- BRPlayerListViewControllerDataSource BRPlayerListViewControllerDelegate --------------
|
||||||
|
extension BRVideoDetailViewController: BRPlayerListViewControllerDataSource, BRPlayerListViewControllerDelegate {
|
||||||
|
|
||||||
|
func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell {
|
||||||
|
|
||||||
|
if let cell = oldCell as? BRPlayerListCell {
|
||||||
|
cell.videoInfo = self.detailModel?.episodeList?[indexPath.row]
|
||||||
|
cell.shortModel = self.detailModel?.shortPlayInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldCell
|
||||||
|
}
|
||||||
|
|
||||||
|
func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return self.detailModel?.episodeList?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extension BRVideoDetailViewController {
|
extension BRVideoDetailViewController {
|
||||||
|
|
||||||
@ -43,32 +111,30 @@ 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.detailModel = model
|
||||||
// self.videoNameLabel.text = model.shortPlayInfo?.name
|
|
||||||
|
|
||||||
/*
|
|
||||||
self.reloadData { [weak self] in
|
self.reloadData { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
self.play()
|
||||||
|
// if let indexPath = indexPath {
|
||||||
|
// self.scrollToItem(indexPath: indexPath, animated: false)
|
||||||
|
//
|
||||||
|
// } else if let videoInfo = self.detailModel?.video_info {
|
||||||
|
// var row: Int?
|
||||||
|
// self.detailModel?.episodeList?.enumerated().forEach({
|
||||||
|
// if $1.id == videoInfo.id {
|
||||||
|
// row = $0
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// if let row = row {
|
||||||
|
// self.scrollToItem(indexPath: .init(row: row, section: 0), animated: false)
|
||||||
|
// } else {
|
||||||
|
// self.scrollToItem(indexPath: .init(row: 0, section: 0), animated: false)
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// self.scrollToItem(indexPath: .init(row: 0, section: 0), animated: false)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
if let indexPath = indexPath {
|
|
||||||
self.scrollToItem(indexPath: indexPath, animated: false)
|
|
||||||
|
|
||||||
} else if let videoInfo = self.detailModel?.video_info {
|
|
||||||
var row: Int?
|
|
||||||
self.detailModel?.episodeList?.enumerated().forEach({
|
|
||||||
if $1.id == videoInfo.id {
|
|
||||||
row = $0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let row = row {
|
|
||||||
self.scrollToItem(indexPath: .init(row: row, section: 0), animated: false)
|
|
||||||
} else {
|
|
||||||
self.scrollToItem(indexPath: .init(row: 0, section: 0), animated: false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.scrollToItem(indexPath: .init(row: 0, section: 0), animated: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
BeeReel/Class/Player/Model/BRPlayerControlProtocol.swift
Normal file
16
BeeReel/Class/Player/Model/BRPlayerControlProtocol.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// BRPlayerControlProtocol.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
@objc protocol BRPlayerControlProtocol {
|
||||||
|
|
||||||
|
///点击事件
|
||||||
|
func singleTapEvent()
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,9 @@ import UIKit
|
|||||||
///暂停播放
|
///暂停播放
|
||||||
func pause()
|
func pause()
|
||||||
|
|
||||||
|
///停止播放
|
||||||
|
func stop()
|
||||||
|
|
||||||
///从头播放
|
///从头播放
|
||||||
func replay()
|
func replay()
|
||||||
|
|
||||||
|
20
BeeReel/Class/Player/View/BRDetailControlView.swift
Normal file
20
BeeReel/Class/Player/View/BRDetailControlView.swift
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// BRDetailControlView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRDetailControlView: BRPlayerControlView {
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Only override draw() if you perform custom drawing.
|
||||||
|
// An empty implementation adversely affects performance during animation.
|
||||||
|
override func draw(_ rect: CGRect) {
|
||||||
|
// Drawing code
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
16
BeeReel/Class/Player/View/BRDetailPlayerCell.swift
Normal file
16
BeeReel/Class/Player/View/BRDetailPlayerCell.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// BRDetailPlayerCell.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRDetailPlayerCell: BRPlayerListCell {
|
||||||
|
|
||||||
|
override var ControlViewClass: BRPlayerControlView.Type {
|
||||||
|
return BRDetailControlView.self
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
BeeReel/Class/Player/View/BRPlayerControlView.swift
Normal file
49
BeeReel/Class/Player/View/BRPlayerControlView.swift
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// BRPlayerControlView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
class BRPlayerControlView: UIView, BRPlayerControlProtocol {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
weak var viewModel: BRPlayerViewModel? {
|
||||||
|
didSet {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortModel: BRShortModel? {
|
||||||
|
didSet {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var videoInfo: BRVideoInfoModel? {
|
||||||
|
didSet {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: BRPlayerControlProtocol
|
||||||
|
func singleTapEvent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -10,14 +10,22 @@ import UIKit
|
|||||||
class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol {
|
class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol {
|
||||||
|
|
||||||
|
|
||||||
var viewModel: BRPlayerViewModel? {
|
weak var viewModel: BRPlayerViewModel? {
|
||||||
didSet {
|
didSet {
|
||||||
|
self.controlView.viewModel = viewModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortModel: BRShortModel? {
|
||||||
|
didSet {
|
||||||
|
self.controlView.shortModel = shortModel
|
||||||
|
self.player.coverImageView?.br_setImage(url: shortModel?.image_url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var videoInfo: BRVideoInfoModel? {
|
var videoInfo: BRVideoInfoModel? {
|
||||||
didSet {
|
didSet {
|
||||||
|
self.controlView.videoInfo = videoInfo
|
||||||
player.setPlayUrl(url: videoInfo?.video_url ?? "")
|
player.setPlayUrl(url: videoInfo?.video_url ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,11 +43,15 @@ class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
player.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func pause() {
|
func pause() {
|
||||||
|
player.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
player.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func replay() {
|
func replay() {
|
||||||
@ -51,9 +63,15 @@ class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ControlViewClass: BRPlayerControlView.Type {
|
||||||
|
return BRPlayerControlView.self
|
||||||
|
}
|
||||||
|
|
||||||
private lazy var player: BRPlayer = {
|
private lazy var player: BRPlayer = {
|
||||||
let player = BRPlayer()
|
let player = BRPlayer(controlView: nil)
|
||||||
player.playerView = self.playerView
|
player.playerView = self.playerView
|
||||||
|
player.delegate = self
|
||||||
|
|
||||||
return player
|
return player
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -62,6 +80,11 @@ class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol {
|
|||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var controlView: BRPlayerControlView = {
|
||||||
|
let view = ControlViewClass.init()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
@ -79,9 +102,20 @@ extension BRPlayerListCell {
|
|||||||
private func br_setupUI() {
|
private func br_setupUI() {
|
||||||
contentView.addSubview(playerView)
|
contentView.addSubview(playerView)
|
||||||
|
|
||||||
|
|
||||||
playerView.snp.makeConstraints { make in
|
playerView.snp.makeConstraints { make in
|
||||||
make.edges.equalToSuperview()
|
make.edges.equalToSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- BRPlayerDelegate --------------
|
||||||
|
extension BRPlayerListCell: BRPlayerDelegate {
|
||||||
|
|
||||||
|
func br_playerDidPlayFinish(_ player: BRPlayer) {
|
||||||
|
self.viewModel?.playFinish(player: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,16 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
@objc protocol BRPlayerViewModelDelegate {
|
||||||
|
|
||||||
|
@objc optional func br_currentVideoPlayFinish(viewModel: BRPlayerViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class BRPlayerViewModel: NSObject {
|
class BRPlayerViewModel: NSObject {
|
||||||
|
|
||||||
|
weak var delegate: BRPlayerViewModelDelegate?
|
||||||
|
|
||||||
@objc dynamic var isPlaying: Bool = true
|
@objc dynamic var isPlaying: Bool = true
|
||||||
|
|
||||||
@ -24,3 +32,14 @@ class BRPlayerViewModel: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extension BRPlayerViewModel {
|
||||||
|
|
||||||
|
func playFinish(player: BRPlayerProtocol) {
|
||||||
|
guard (player as? UICollectionViewCell) == (currentPlayer as? UICollectionViewCell) else { return }
|
||||||
|
self.delegate?.br_currentVideoPlayFinish?(viewModel: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -9,15 +9,51 @@ import UIKit
|
|||||||
import SJBaseVideoPlayer
|
import SJBaseVideoPlayer
|
||||||
|
|
||||||
|
|
||||||
class BRPlayer {
|
@objc protocol BRPlayerDelegate: NSObjectProtocol {
|
||||||
|
///更新当前总进度
|
||||||
|
@objc optional func br_playerDurationDidChange(_ player: BRPlayer, duration: TimeInterval)
|
||||||
|
///更新当前进度
|
||||||
|
@objc optional func br_playerCurrentTimeDidChange(_ player: BRPlayer, time: TimeInterval)
|
||||||
|
|
||||||
|
///播放状态变化
|
||||||
|
// @objc optional func vp_player(_ player: VPPlayer, playStateDidChanged state: VPPlayer.PlayState)
|
||||||
|
|
||||||
|
///加载状态发生变化
|
||||||
|
// @objc optional func vp_player(_ player: VPPlayer, loadStateDidChange state: VPPlayer.LoadState)
|
||||||
|
|
||||||
|
///播放时间发生变化
|
||||||
|
// @objc optional func vp_playTimeChanged(_ player: BRPlayer, currentTime: Int, duration: Int)
|
||||||
|
|
||||||
|
///显示首帧
|
||||||
|
// @objc optional func br_firstRenderedStart(_ player: BRPlayer)
|
||||||
|
|
||||||
|
///准备完成
|
||||||
|
// @objc optional func br_playerReadyToPlay(_ player: BRPlayer)
|
||||||
|
|
||||||
|
///播放完成
|
||||||
|
@objc optional func br_playerDidPlayFinish(_ player: BRPlayer)
|
||||||
|
|
||||||
|
///缓冲完成
|
||||||
|
// @objc optional func br_playLoadingEnd(_ player: BRPlayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
class BRPlayer: NSObject {
|
||||||
|
|
||||||
private lazy var player: SJBaseVideoPlayer = {
|
private lazy var player: SJBaseVideoPlayer = {
|
||||||
let player = SJBaseVideoPlayer()
|
let player = SJBaseVideoPlayer()
|
||||||
player.autoplayWhenSetNewAsset = false
|
player.autoplayWhenSetNewAsset = false
|
||||||
|
player.resumePlaybackWhenAppDidEnterForeground = false
|
||||||
|
player.accurateSeeking = true
|
||||||
|
player.videoGravity = .resizeAspectFill
|
||||||
|
// player.disableVolumeSetting = true
|
||||||
|
player.rotationManager?.isDisabledAutorotation = true
|
||||||
|
player.controlLayerDataSource = self
|
||||||
return player
|
return player
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var playerView: UIView? {
|
weak var delegate: BRPlayerDelegate?
|
||||||
|
|
||||||
|
weak var playerView: UIView? {
|
||||||
didSet {
|
didSet {
|
||||||
playerView?.addSubview(player.view)
|
playerView?.addSubview(player.view)
|
||||||
player.view.snp.makeConstraints { make in
|
player.view.snp.makeConstraints { make in
|
||||||
@ -26,6 +62,13 @@ class BRPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private(set) weak var br_controlView: BRPlayerControlProtocol?;
|
||||||
|
|
||||||
|
var coverImageView: UIImageView? {
|
||||||
|
return self.player.presentView.placeholderImageView
|
||||||
|
}
|
||||||
|
|
||||||
|
///精确到秒
|
||||||
var duration: TimeInterval {
|
var duration: TimeInterval {
|
||||||
return self.player.duration
|
return self.player.duration
|
||||||
}
|
}
|
||||||
@ -34,14 +77,23 @@ class BRPlayer {
|
|||||||
return self.player.currentTime
|
return self.player.currentTime
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
deinit {
|
||||||
|
brLog(message: "播放器销毁")
|
||||||
|
}
|
||||||
|
|
||||||
|
init(controlView: BRPlayerControlProtocol?) {
|
||||||
|
super.init()
|
||||||
|
self.br_controlView = controlView
|
||||||
|
|
||||||
setupPlayer()
|
setupPlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPlayUrl(url: String) {
|
func setPlayUrl(url: String) {
|
||||||
self.stop()
|
self.stop()
|
||||||
guard let url = URL(string: url) else { return }
|
guard let url = URL(string: url) else { return }
|
||||||
let asset = SJVideoPlayerURLAsset(url: url)
|
guard let proxyUrl = BRPlayerCache.proxyURL(url: url) else { return }
|
||||||
|
|
||||||
|
let asset = SJVideoPlayerURLAsset(url: proxyUrl)
|
||||||
self.player.urlAsset = asset
|
self.player.urlAsset = asset
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,22 +105,34 @@ class BRPlayer {
|
|||||||
self.player.pause()
|
self.player.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///用户暂停
|
||||||
|
func pauseForUser() {
|
||||||
|
self.player.pauseForUser()
|
||||||
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
self.player.stop()
|
self.player.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func seek(toTime: Int) {
|
func seek(toTime: Int) {
|
||||||
|
self.player.seek(toTime: TimeInterval(toTime))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BRPlayer {
|
extension BRPlayer {
|
||||||
|
|
||||||
private func setupPlayer() {
|
private func setupPlayer() {
|
||||||
|
//设置支持的手势
|
||||||
|
self.player.gestureController.supportedGestureTypes = .singleTap
|
||||||
|
self.player.gestureController.singleTapHandler = { [weak self] _, _ in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.br_controlView?.singleTapEvent()
|
||||||
|
}
|
||||||
|
|
||||||
//播放完成回调
|
//播放完成回调
|
||||||
self.player.playbackObserver.playbackDidFinishExeBlock = { [weak self] player in
|
self.player.playbackObserver.playbackDidFinishExeBlock = { [weak self] player in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
self.delegate?.br_playerDidPlayFinish?(self)
|
||||||
}
|
}
|
||||||
//播放状态改变
|
//播放状态改变
|
||||||
self.player.playbackObserver.playbackStatusDidChangeExeBlock = { [weak self] player in
|
self.player.playbackObserver.playbackStatusDidChangeExeBlock = { [weak self] player in
|
||||||
@ -78,13 +142,23 @@ extension BRPlayer {
|
|||||||
//播放时长改变
|
//播放时长改变
|
||||||
self.player.playbackObserver.durationDidChangeExeBlock = { [weak self] player in
|
self.player.playbackObserver.durationDidChangeExeBlock = { [weak self] player in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
self.delegate?.br_playerDurationDidChange?(self, duration: player.duration)
|
||||||
}
|
}
|
||||||
//播放进度改变
|
//播放进度改变
|
||||||
self.player.playbackObserver.currentTimeDidChangeExeBlock = { [weak self] player in
|
self.player.playbackObserver.currentTimeDidChangeExeBlock = { [weak self] player in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
self.delegate?.br_playerCurrentTimeDidChange?(self, time: player.currentTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- SJVideoPlayerControlLayerDataSource --------------
|
||||||
|
extension BRPlayer: SJVideoPlayerControlLayerDataSource {
|
||||||
|
func controlView() -> UIView! {
|
||||||
|
return self.br_controlView as? UIView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
28
BeeReel/Lib/Player/BRPlayerCache.swift
Normal file
28
BeeReel/Lib/Player/BRPlayerCache.swift
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// BRPlayerCache.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SJMediaCacheServer
|
||||||
|
|
||||||
|
class BRPlayerCache {
|
||||||
|
|
||||||
|
static func proxyURL(url: URL) -> URL? {
|
||||||
|
return SJMediaCacheServer.shared().proxyURL(from: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
///预加载数据
|
||||||
|
static func prefetch(url: URL?) -> MCSPrefetchTask? {
|
||||||
|
guard let url = url else { return nil }
|
||||||
|
return SJMediaCacheServer.shared().prefetch(with: url, prefetchSize: 1 * 1024 * 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func prefetch(urlString: String?) -> MCSPrefetchTask? {
|
||||||
|
guard let str = urlString else { return nil }
|
||||||
|
return prefetch(url: URL(string: str))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
BeeReel/Sources/Assets.xcassets/icon/full_icon_01.imageset/Contents.json
vendored
Normal file
22
BeeReel/Sources/Assets.xcassets/icon/full_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/full_icon_01.imageset/Frame@2x.png
vendored
Normal file
BIN
BeeReel/Sources/Assets.xcassets/icon/full_icon_01.imageset/Frame@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 385 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/full_icon_01.imageset/Frame@3x.png
vendored
Normal file
BIN
BeeReel/Sources/Assets.xcassets/icon/full_icon_01.imageset/Frame@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 485 B |
22
BeeReel/Sources/Assets.xcassets/icon/hot_icon_03.imageset/Contents.json
vendored
Normal file
22
BeeReel/Sources/Assets.xcassets/icon/hot_icon_03.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/hot_icon_03.imageset/Frame@2x.png
vendored
Normal file
BIN
BeeReel/Sources/Assets.xcassets/icon/hot_icon_03.imageset/Frame@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 467 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/hot_icon_03.imageset/Frame@3x.png
vendored
Normal file
BIN
BeeReel/Sources/Assets.xcassets/icon/hot_icon_03.imageset/Frame@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 646 B |
22
BeeReel/Sources/Assets.xcassets/icon/nav_back_icon_01.imageset/Contents.json
vendored
Normal file
22
BeeReel/Sources/Assets.xcassets/icon/nav_back_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame 1498@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame 1498@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/nav_back_icon_01.imageset/Frame 1498@2x.png
vendored
Normal file
BIN
BeeReel/Sources/Assets.xcassets/icon/nav_back_icon_01.imageset/Frame 1498@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/nav_back_icon_01.imageset/Frame 1498@3x.png
vendored
Normal file
BIN
BeeReel/Sources/Assets.xcassets/icon/nav_back_icon_01.imageset/Frame 1498@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
1
Podfile
1
Podfile
@ -23,6 +23,7 @@ target 'BeeReel' do
|
|||||||
pod 'Toast' #吐司提示
|
pod 'Toast' #吐司提示
|
||||||
# pod 'YYKit' #工具类
|
# pod 'YYKit' #工具类
|
||||||
pod 'SJVideoPlayer' #播放器
|
pod 'SJVideoPlayer' #播放器
|
||||||
|
pod 'SJMediaCacheServer' #播放器缓存
|
||||||
pod 'WMZPageController' #分页控制器
|
pod 'WMZPageController' #分页控制器
|
||||||
pod 'YYCategories'
|
pod 'YYCategories'
|
||||||
pod 'YYText'
|
pod 'YYText'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user