diff --git a/.gitignore b/.gitignore index 8a981e9..7f16740 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ xcuserdata/ ## Playgrounds timeline.xctimeline playground.xcworkspace +*.xcworkspace +Podfile.lock +Pods/ # Swift Package Manager # @@ -38,7 +41,6 @@ playground.xcworkspace # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # -# Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace diff --git a/BeeReel.xcodeproj/project.pbxproj b/BeeReel.xcodeproj/project.pbxproj new file mode 100644 index 0000000..30e0a62 --- /dev/null +++ b/BeeReel.xcodeproj/project.pbxproj @@ -0,0 +1,1051 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 440A41A6E6A22A02807AE759 /* Pods_BeeReel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 899B3015B03D5E1A5A6507EB /* Pods_BeeReel.framework */; }; + BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */; }; + BF692AEB2E0A475D00A5C2DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */; }; + BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE82E0A475D00A5C2DA /* SceneDelegate.swift */; }; + BF692AEE2E0A475D00A5C2DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF692AE22E0A475D00A5C2DA /* Assets.xcassets */; }; + BF692AF02E0A475D00A5C2DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF692AE52E0A475D00A5C2DA /* LaunchScreen.storyboard */; }; + BF692AFA2E0A6F0900A5C2DA /* BRNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AF92E0A6EFD00A5C2DA /* BRNetwork.swift */; }; + BF692AFC2E0A6F8000A5C2DA /* BRNetworkTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AFB2E0A6F1B00A5C2DA /* BRNetworkTarget.swift */; }; + BF692B012E0A74A200A5C2DA /* BRDefine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B002E0A749C00A5C2DA /* BRDefine.swift */; }; + BF692B042E0A76D200A5C2DA /* BRLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B032E0A76D200A5C2DA /* BRLoginManager.swift */; }; + BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B062E0A771C00A5C2DA /* BRModel.swift */; }; + BF692B092E0A775500A5C2DA /* BRLoginToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B082E0A775500A5C2DA /* BRLoginToken.swift */; }; + BF692B0E2E0A7AF300A5C2DA /* UserDefaults+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B0D2E0A7AED00A5C2DA /* UserDefaults+BRAdd.swift */; }; + BF692B102E0A7B4300A5C2DA /* BRUserDefaultsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B0F2E0A7B3400A5C2DA /* BRUserDefaultsKey.swift */; }; + BF692B132E0A7B9000A5C2DA /* BRUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B122E0A7B9000A5C2DA /* BRUserInfo.swift */; }; + BF692B162E0A7CD600A5C2DA /* BRHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B152E0A7CD200A5C2DA /* BRHUD.swift */; }; + BF692B182E0A7D8900A5C2DA /* BRToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B172E0A7D8200A5C2DA /* BRToast.swift */; }; + BF692B1C2E0A7DE800A5C2DA /* BRAppTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B1B2E0A7DE200A5C2DA /* BRAppTool.swift */; }; + BF692B1F2E0A804600A5C2DA /* BRLocalizedManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B1E2E0A804100A5C2DA /* BRLocalizedManager.swift */; }; + BF692B222E0A820D00A5C2DA /* String+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B212E0A820C00A5C2DA /* String+BRAdd.swift */; }; + BF692B242E0A825B00A5C2DA /* BRCryptorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B232E0A825600A5C2DA /* BRCryptorService.swift */; }; + BF692B2A2E0A84F700A5C2DA /* JXUUID.m in Sources */ = {isa = PBXBuildFile; fileRef = BF692B262E0A84F700A5C2DA /* JXUUID.m */; }; + BF692B2B2E0A84F700A5C2DA /* PDKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = BF692B282E0A84F700A5C2DA /* PDKeyChain.m */; }; + BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B332E0A87BD00A5C2DA /* UIDevice+BRAdd.swift */; }; + BF692B3A2E0A8C6000A5C2DA /* BRViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B392E0A8C6000A5C2DA /* BRViewController.swift */; }; + BF692B3C2E0A8D0200A5C2DA /* BRNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B3B2E0A8D0200A5C2DA /* BRNavigationController.swift */; }; + BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B3D2E0A8D2300A5C2DA /* BRTabBarController.swift */; }; + BF692B402E0A8FA100A5C2DA /* UIColor+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B3F2E0A8F9D00A5C2DA /* UIColor+BRAdd.swift */; }; + BF692B422E0A8FB500A5C2DA /* UIFont+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B412E0A8FAE00A5C2DA /* UIFont+BRAdd.swift */; }; + BF692B442E0A910E00A5C2DA /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = BF692B432E0A910E00A5C2DA /* Localizable.xcstrings */; }; + BF692B472E0A9B7900A5C2DA /* BRPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B462E0A9B7400A5C2DA /* BRPlayer.swift */; }; + BF692B492E0A9D0E00A5C2DA /* UIView+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B482E0A9D0800A5C2DA /* UIView+BRAdd.swift */; }; + BF692B512E0AA8C600A5C2DA /* BRPlayerListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B502E0AA8C600A5C2DA /* BRPlayerListViewController.swift */; }; + BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B532E0AA8FA00A5C2DA /* BRCollectionView.swift */; }; + BF692B562E0AA92100A5C2DA /* BRCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B552E0AA92100A5C2DA /* BRCollectionViewCell.swift */; }; + BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B572E0AAA5F00A5C2DA /* UIScreen+BRAdd.swift */; }; + BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B592E0AAADD00A5C2DA /* BRPlayerListCell.swift */; }; + BF692B5F2E0B812800A5C2DA /* BRTabBarItemContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B5E2E0B812800A5C2DA /* BRTabBarItemContainer.swift */; }; + BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B602E0B814F00A5C2DA /* BRTabBarItemContentView.swift */; }; + BF692B632E0B9D4800A5C2DA /* BRTabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B622E0B9D4800A5C2DA /* BRTabBarItem.swift */; }; + BF692B652E0BC53900A5C2DA /* CGMutablePath+BRRoundedCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B642E0BC53900A5C2DA /* CGMutablePath+BRRoundedCorner.swift */; }; + BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B662E0BC6BC00A5C2DA /* AppDelegate+BRConfig.swift */; }; + BF692B6B2E0BC85300A5C2DA /* BRHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B6A2E0BC85300A5C2DA /* BRHomeViewController.swift */; }; + BF692B6E2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B6D2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift */; }; + BF692B732E0D397700A5C2DA /* BRHomeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B722E0D396800A5C2DA /* BRHomeAPI.swift */; }; + BF692B752E0D39D000A5C2DA /* BRListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B742E0D39D000A5C2DA /* BRListModel.swift */; }; + BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B772E0D3A1200A5C2DA /* BRHomeModuleItem.swift */; }; + BF692B7A2E0D3BD300A5C2DA /* BRShortModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B792E0D3BD300A5C2DA /* BRShortModel.swift */; }; + BF692B7C2E0D3C1300A5C2DA /* BRVideoInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692B7B2E0D3C1300A5C2DA /* BRVideoInfoModel.swift */; }; + BF78108B2E0D4EB3007DEEBC /* BRURLPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF78108A2E0D4EB3007DEEBC /* BRURLPath.swift */; }; + BFC676522E0D4EFD006659E5 /* BRHomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676512E0D4EFD006659E5 /* BRHomeViewModel.swift */; }; + BFC676622E0E2C8E006659E5 /* WMZBannerView.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC676602E0E2C8E006659E5 /* WMZBannerView.m */; }; + BFC676632E0E2C8E006659E5 /* WMZBannerOverLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC6765C2E0E2C8E006659E5 /* WMZBannerOverLayout.m */; }; + BFC676642E0E2C8E006659E5 /* WMZBannerFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC6765A2E0E2C8E006659E5 /* WMZBannerFlowLayout.m */; }; + BFC676652E0E2C8E006659E5 /* WMZBannerControl.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC676562E0E2C8E006659E5 /* WMZBannerControl.m */; }; + BFC676662E0E2C8E006659E5 /* WMZBannerFadeLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC676582E0E2C8E006659E5 /* WMZBannerFadeLayout.m */; }; + BFC676672E0E2C8E006659E5 /* WMZBannerParam.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC6765E2E0E2C8E006659E5 /* WMZBannerParam.m */; }; + BFC676692E0E34DA006659E5 /* BRUserAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676682E0E34D9006659E5 /* BRUserAPI.swift */; }; + BFC6766B2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6766A2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift */; }; + BFC6766D2E0E3A8D006659E5 /* BRImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6766C2E0E3A8D006659E5 /* BRImageView.swift */; }; + BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6766E2E0E3B51006659E5 /* UIImageView+BRAdd.swift */; }; + BFC676712E0E9234006659E5 /* BRSpotlightViewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676702E0E9234006659E5 /* BRSpotlightViewViewController.swift */; }; + BFC676732E0E938B006659E5 /* BRTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676722E0E938B006659E5 /* BRTableView.swift */; }; + BFC676752E0E93B3006659E5 /* BRTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676742E0E93B3006659E5 /* BRTableViewCell.swift */; }; + BFC676782E0E9553006659E5 /* BRSpotlightMainBaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676772E0E9553006659E5 /* BRSpotlightMainBaseCell.swift */; }; + BFC6767B2E0E973B006659E5 /* UIStackView+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6767A2E0E9736006659E5 /* UIStackView+BRAdd.swift */; }; + BFC6767D2E0E9809006659E5 /* BRSpotlightHotMainCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6767C2E0E9809006659E5 /* BRSpotlightHotMainCell.swift */; }; + BFC6767F2E121A72006659E5 /* BRSpotlightHotCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6767E2E121A72006659E5 /* BRSpotlightHotCell.swift */; }; + BFC676812E122733006659E5 /* BRPlayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676802E122733006659E5 /* BRPlayerProtocol.swift */; }; + BFC676832E122CC5006659E5 /* BRPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676822E122CC5006659E5 /* BRPlayerViewModel.swift */; }; + BFC676852E122D9E006659E5 /* BRVideoDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676842E122D9E006659E5 /* BRVideoDetailViewController.swift */; }; + BFC676872E122E36006659E5 /* BRVideoDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */; }; + BFC676892E122FDD006659E5 /* BRVideoAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676882E122FDB006659E5 /* BRVideoAPI.swift */; }; + BFC6768B2E123690006659E5 /* BRVideoRevolutionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6768A2E12368C006659E5 /* BRVideoRevolutionManager.swift */; }; + BFC6768D2E123D6E006659E5 /* AttributedString+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6768C2E123D67006659E5 /* AttributedString+BRAdd.swift */; }; + BFC6768F2E125D5B006659E5 /* BRSpotlightTopMainCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6768E2E125D5B006659E5 /* BRSpotlightTopMainCell.swift */; }; + BFC676912E126248006659E5 /* BRSpotlightTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676902E126248006659E5 /* BRSpotlightTopCell.swift */; }; + BFC676932E126A62006659E5 /* BRSpotlightNewMainCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676922E126A62006659E5 /* BRSpotlightNewMainCell.swift */; }; + BFC676952E126BBF006659E5 /* BRSpotlightNewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676942E126BBF006659E5 /* BRSpotlightNewCell.swift */; }; + BFC676972E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676962E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift */; }; + BFC676992E1280E3006659E5 /* BRSpotlightRecommandCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC676982E1280E3006659E5 /* BRSpotlightRecommandCell.swift */; }; + BFC6769B2E1285C5006659E5 /* BRPagerViewTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6769A2E1285C5006659E5 /* BRPagerViewTransformer.swift */; }; + BFC6769D2E129794006659E5 /* BRHomeTop10ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6769C2E129794006659E5 /* BRHomeTop10ViewController.swift */; }; +/* End PBXBuildFile 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + BF692AE22E0A475D00A5C2DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BF692AE32E0A475D00A5C2DA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BF692AE42E0A475D00A5C2DA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + BF692AE82E0A475D00A5C2DA /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + BF692AF92E0A6EFD00A5C2DA /* BRNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRNetwork.swift; sourceTree = ""; }; + BF692AFB2E0A6F1B00A5C2DA /* BRNetworkTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRNetworkTarget.swift; sourceTree = ""; }; + BF692B002E0A749C00A5C2DA /* BRDefine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRDefine.swift; sourceTree = ""; }; + BF692B032E0A76D200A5C2DA /* BRLoginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRLoginManager.swift; sourceTree = ""; }; + BF692B062E0A771C00A5C2DA /* BRModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRModel.swift; sourceTree = ""; }; + BF692B082E0A775500A5C2DA /* BRLoginToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRLoginToken.swift; sourceTree = ""; }; + BF692B0D2E0A7AED00A5C2DA /* UserDefaults+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+BRAdd.swift"; sourceTree = ""; }; + BF692B0F2E0A7B3400A5C2DA /* BRUserDefaultsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRUserDefaultsKey.swift; sourceTree = ""; }; + BF692B122E0A7B9000A5C2DA /* BRUserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRUserInfo.swift; sourceTree = ""; }; + BF692B152E0A7CD200A5C2DA /* BRHUD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHUD.swift; sourceTree = ""; }; + BF692B172E0A7D8200A5C2DA /* BRToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRToast.swift; sourceTree = ""; }; + BF692B1B2E0A7DE200A5C2DA /* BRAppTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRAppTool.swift; sourceTree = ""; }; + BF692B1E2E0A804100A5C2DA /* BRLocalizedManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRLocalizedManager.swift; sourceTree = ""; }; + BF692B212E0A820C00A5C2DA /* String+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+BRAdd.swift"; sourceTree = ""; }; + BF692B232E0A825600A5C2DA /* BRCryptorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCryptorService.swift; sourceTree = ""; }; + BF692B252E0A84F700A5C2DA /* JXUUID.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JXUUID.h; sourceTree = ""; }; + BF692B262E0A84F700A5C2DA /* JXUUID.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JXUUID.m; sourceTree = ""; }; + BF692B272E0A84F700A5C2DA /* PDKeyChain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PDKeyChain.h; sourceTree = ""; }; + BF692B282E0A84F700A5C2DA /* PDKeyChain.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PDKeyChain.m; sourceTree = ""; }; + BF692B322E0A872600A5C2DA /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + BF692B332E0A87BD00A5C2DA /* UIDevice+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+BRAdd.swift"; sourceTree = ""; }; + BF692B392E0A8C6000A5C2DA /* BRViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRViewController.swift; sourceTree = ""; }; + BF692B3B2E0A8D0200A5C2DA /* BRNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRNavigationController.swift; sourceTree = ""; }; + BF692B3D2E0A8D2300A5C2DA /* BRTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBarController.swift; sourceTree = ""; }; + BF692B3F2E0A8F9D00A5C2DA /* UIColor+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+BRAdd.swift"; sourceTree = ""; }; + BF692B412E0A8FAE00A5C2DA /* UIFont+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+BRAdd.swift"; sourceTree = ""; }; + BF692B432E0A910E00A5C2DA /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + BF692B462E0A9B7400A5C2DA /* BRPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayer.swift; sourceTree = ""; }; + BF692B482E0A9D0800A5C2DA /* UIView+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+BRAdd.swift"; sourceTree = ""; }; + BF692B502E0AA8C600A5C2DA /* BRPlayerListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerListViewController.swift; sourceTree = ""; }; + BF692B532E0AA8FA00A5C2DA /* BRCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCollectionView.swift; sourceTree = ""; }; + BF692B552E0AA92100A5C2DA /* BRCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRCollectionViewCell.swift; sourceTree = ""; }; + BF692B572E0AAA5F00A5C2DA /* UIScreen+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+BRAdd.swift"; sourceTree = ""; }; + BF692B592E0AAADD00A5C2DA /* BRPlayerListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerListCell.swift; sourceTree = ""; }; + BF692B5E2E0B812800A5C2DA /* BRTabBarItemContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBarItemContainer.swift; sourceTree = ""; }; + BF692B602E0B814F00A5C2DA /* BRTabBarItemContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBarItemContentView.swift; sourceTree = ""; }; + BF692B622E0B9D4800A5C2DA /* BRTabBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBarItem.swift; sourceTree = ""; }; + BF692B642E0BC53900A5C2DA /* CGMutablePath+BRRoundedCorner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGMutablePath+BRRoundedCorner.swift"; sourceTree = ""; }; + BF692B662E0BC6BC00A5C2DA /* AppDelegate+BRConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+BRConfig.swift"; sourceTree = ""; }; + BF692B6A2E0BC85300A5C2DA /* BRHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHomeViewController.swift; sourceTree = ""; }; + BF692B6D2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHomeHeaderView.swift; sourceTree = ""; }; + BF692B722E0D396800A5C2DA /* BRHomeAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHomeAPI.swift; sourceTree = ""; }; + BF692B742E0D39D000A5C2DA /* BRListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRListModel.swift; sourceTree = ""; }; + BF692B772E0D3A1200A5C2DA /* BRHomeModuleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHomeModuleItem.swift; sourceTree = ""; }; + BF692B792E0D3BD300A5C2DA /* BRShortModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRShortModel.swift; sourceTree = ""; }; + BF692B7B2E0D3C1300A5C2DA /* BRVideoInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoInfoModel.swift; sourceTree = ""; }; + BF78108A2E0D4EB3007DEEBC /* BRURLPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRURLPath.swift; sourceTree = ""; }; + BFC676512E0D4EFD006659E5 /* BRHomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHomeViewModel.swift; sourceTree = ""; }; + BFC676542E0E2C8E006659E5 /* WMZBannerConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZBannerConfig.h; sourceTree = ""; }; + BFC676552E0E2C8E006659E5 /* WMZBannerControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZBannerControl.h; sourceTree = ""; }; + BFC676562E0E2C8E006659E5 /* WMZBannerControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZBannerControl.m; sourceTree = ""; }; + BFC676572E0E2C8E006659E5 /* WMZBannerFadeLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZBannerFadeLayout.h; sourceTree = ""; }; + BFC676582E0E2C8E006659E5 /* WMZBannerFadeLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZBannerFadeLayout.m; sourceTree = ""; }; + BFC676592E0E2C8E006659E5 /* WMZBannerFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZBannerFlowLayout.h; sourceTree = ""; }; + BFC6765A2E0E2C8E006659E5 /* WMZBannerFlowLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZBannerFlowLayout.m; sourceTree = ""; }; + BFC6765B2E0E2C8E006659E5 /* WMZBannerOverLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZBannerOverLayout.h; sourceTree = ""; }; + BFC6765C2E0E2C8E006659E5 /* WMZBannerOverLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZBannerOverLayout.m; sourceTree = ""; }; + BFC6765D2E0E2C8E006659E5 /* WMZBannerParam.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZBannerParam.h; sourceTree = ""; }; + BFC6765E2E0E2C8E006659E5 /* WMZBannerParam.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZBannerParam.m; sourceTree = ""; }; + BFC6765F2E0E2C8E006659E5 /* WMZBannerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMZBannerView.h; sourceTree = ""; }; + BFC676602E0E2C8E006659E5 /* WMZBannerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMZBannerView.m; sourceTree = ""; }; + BFC676682E0E34D9006659E5 /* BRUserAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRUserAPI.swift; sourceTree = ""; }; + BFC6766A2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHomeHeaderBannerCell.swift; sourceTree = ""; }; + BFC6766C2E0E3A8D006659E5 /* BRImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRImageView.swift; sourceTree = ""; }; + BFC6766E2E0E3B51006659E5 /* UIImageView+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+BRAdd.swift"; sourceTree = ""; }; + BFC676702E0E9234006659E5 /* BRSpotlightViewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightViewViewController.swift; sourceTree = ""; }; + BFC676722E0E938B006659E5 /* BRTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTableView.swift; sourceTree = ""; }; + BFC676742E0E93B3006659E5 /* BRTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTableViewCell.swift; sourceTree = ""; }; + BFC676772E0E9553006659E5 /* BRSpotlightMainBaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightMainBaseCell.swift; sourceTree = ""; }; + BFC6767A2E0E9736006659E5 /* UIStackView+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+BRAdd.swift"; sourceTree = ""; }; + BFC6767C2E0E9809006659E5 /* BRSpotlightHotMainCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightHotMainCell.swift; sourceTree = ""; }; + BFC6767E2E121A72006659E5 /* BRSpotlightHotCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightHotCell.swift; sourceTree = ""; }; + BFC676802E122733006659E5 /* BRPlayerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerProtocol.swift; sourceTree = ""; }; + BFC676822E122CC5006659E5 /* BRPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerViewModel.swift; sourceTree = ""; }; + BFC676842E122D9E006659E5 /* BRVideoDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoDetailViewController.swift; sourceTree = ""; }; + BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoDetailModel.swift; sourceTree = ""; }; + BFC676882E122FDB006659E5 /* BRVideoAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoAPI.swift; sourceTree = ""; }; + BFC6768A2E12368C006659E5 /* BRVideoRevolutionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoRevolutionManager.swift; sourceTree = ""; }; + BFC6768C2E123D67006659E5 /* AttributedString+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+BRAdd.swift"; sourceTree = ""; }; + BFC6768E2E125D5B006659E5 /* BRSpotlightTopMainCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightTopMainCell.swift; sourceTree = ""; }; + BFC676902E126248006659E5 /* BRSpotlightTopCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightTopCell.swift; sourceTree = ""; }; + BFC676922E126A62006659E5 /* BRSpotlightNewMainCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightNewMainCell.swift; sourceTree = ""; }; + BFC676942E126BBF006659E5 /* BRSpotlightNewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightNewCell.swift; sourceTree = ""; }; + BFC676962E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightRecommandMainCell.swift; sourceTree = ""; }; + BFC676982E1280E3006659E5 /* BRSpotlightRecommandCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSpotlightRecommandCell.swift; sourceTree = ""; }; + BFC6769A2E1285C5006659E5 /* BRPagerViewTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPagerViewTransformer.swift; sourceTree = ""; }; + BFC6769C2E129794006659E5 /* BRHomeTop10ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHomeTop10ViewController.swift; sourceTree = ""; }; + C8F11086BA392585E9563BA7 /* Pods-ShortBox.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShortBox.release.xcconfig"; path = "Target Support Files/Pods-ShortBox/Pods-ShortBox.release.xcconfig"; sourceTree = ""; }; + F06627B1DEE86552C2A87AEC /* Pods-BeeReel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeeReel.debug.xcconfig"; path = "Target Support Files/Pods-BeeReel/Pods-BeeReel.debug.xcconfig"; sourceTree = ""; }; + F70FA1F4169364C4C53534CE /* Pods-BeeReel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeeReel.release.xcconfig"; path = "Target Support Files/Pods-BeeReel/Pods-BeeReel.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BF692AC62E0A475500A5C2DA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 440A41A6E6A22A02807AE759 /* Pods_BeeReel.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8972E1AE57ADB2F6844701DA /* Pods */ = { + isa = PBXGroup; + children = ( + 86290EBFA8B93C91B3BAD835 /* Pods-ShortBox.debug.xcconfig */, + C8F11086BA392585E9563BA7 /* Pods-ShortBox.release.xcconfig */, + F06627B1DEE86552C2A87AEC /* Pods-BeeReel.debug.xcconfig */, + F70FA1F4169364C4C53534CE /* Pods-BeeReel.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + BF692AC02E0A475500A5C2DA = { + isa = PBXGroup; + children = ( + BF692AEA2E0A475D00A5C2DA /* BeeReel */, + BF692ACA2E0A475500A5C2DA /* Products */, + 8972E1AE57ADB2F6844701DA /* Pods */, + DF948F1E234E75684255568B /* Frameworks */, + ); + sourceTree = ""; + }; + BF692ACA2E0A475500A5C2DA /* Products */ = { + isa = PBXGroup; + children = ( + BF692AC92E0A475500A5C2DA /* BeeReel.app */, + ); + name = Products; + sourceTree = ""; + }; + BF692AEA2E0A475D00A5C2DA /* BeeReel */ = { + isa = PBXGroup; + children = ( + BF692AF32E0A47B500A5C2DA /* Delegate */, + BF692AF42E0A47CA00A5C2DA /* Base */, + BF692AF52E0A47D400A5C2DA /* Class */, + BF692AF22E0A478D00A5C2DA /* Sources */, + BF692AF62E0A480000A5C2DA /* Lib */, + BF692AF72E0A480E00A5C2DA /* Thirdparty */, + ); + path = BeeReel; + sourceTree = ""; + }; + BF692AF22E0A478D00A5C2DA /* Sources */ = { + isa = PBXGroup; + children = ( + BF692AE22E0A475D00A5C2DA /* Assets.xcassets */, + BF692AE32E0A475D00A5C2DA /* Info.plist */, + BF692AE52E0A475D00A5C2DA /* LaunchScreen.storyboard */, + BF692B322E0A872600A5C2DA /* Bridging-Header.h */, + BF692B432E0A910E00A5C2DA /* Localizable.xcstrings */, + ); + path = Sources; + sourceTree = ""; + }; + BF692AF32E0A47B500A5C2DA /* Delegate */ = { + isa = PBXGroup; + children = ( + BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */, + BF692AE82E0A475D00A5C2DA /* SceneDelegate.swift */, + BF692B662E0BC6BC00A5C2DA /* AppDelegate+BRConfig.swift */, + ); + path = Delegate; + sourceTree = ""; + }; + BF692AF42E0A47CA00A5C2DA /* Base */ = { + isa = PBXGroup; + children = ( + BF692B352E0A8AF200A5C2DA /* Controller */, + BF692B522E0AA8D600A5C2DA /* View */, + BF692B0C2E0A7A9A00A5C2DA /* Extension */, + BF692B052E0A770F00A5C2DA /* Model */, + BF692AFF2E0A748D00A5C2DA /* Define */, + BF692AF82E0A51CF00A5C2DA /* Network */, + ); + path = Base; + sourceTree = ""; + }; + BF692AF52E0A47D400A5C2DA /* Class */ = { + isa = PBXGroup; + children = ( + BF692B682E0BC78C00A5C2DA /* Home */, + BF692B4A2E0AA84C00A5C2DA /* Player */, + ); + path = Class; + sourceTree = ""; + }; + BF692AF62E0A480000A5C2DA /* Lib */ = { + isa = PBXGroup; + children = ( + BF692B452E0A9B5800A5C2DA /* Player */, + BF692B1D2E0A803000A5C2DA /* LocalizedManager */, + BF692B142E0A7CB500A5C2DA /* HUD */, + BF692B112E0A7B7500A5C2DA /* User */, + BF692B022E0A769C00A5C2DA /* Login */, + BF692B192E0A7DBB00A5C2DA /* AppTool */, + ); + path = Lib; + sourceTree = ""; + }; + BF692AF72E0A480E00A5C2DA /* Thirdparty */ = { + isa = PBXGroup; + children = ( + BFC676612E0E2C8E006659E5 /* WMZBanner */, + BF692B292E0A84F700A5C2DA /* JXUUID */, + ); + path = Thirdparty; + sourceTree = ""; + }; + BF692AF82E0A51CF00A5C2DA /* Network */ = { + isa = PBXGroup; + children = ( + BF692B712E0D395300A5C2DA /* API */, + BF692B702E0D394800A5C2DA /* Base */, + ); + path = Network; + sourceTree = ""; + }; + BF692AFF2E0A748D00A5C2DA /* Define */ = { + isa = PBXGroup; + children = ( + BF692B0F2E0A7B3400A5C2DA /* BRUserDefaultsKey.swift */, + BF692B002E0A749C00A5C2DA /* BRDefine.swift */, + ); + path = Define; + sourceTree = ""; + }; + BF692B022E0A769C00A5C2DA /* Login */ = { + isa = PBXGroup; + children = ( + BF692B032E0A76D200A5C2DA /* BRLoginManager.swift */, + BF692B082E0A775500A5C2DA /* BRLoginToken.swift */, + ); + path = Login; + sourceTree = ""; + }; + BF692B052E0A770F00A5C2DA /* Model */ = { + isa = PBXGroup; + children = ( + BF692B062E0A771C00A5C2DA /* BRModel.swift */, + BF692B742E0D39D000A5C2DA /* BRListModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + BF692B0C2E0A7A9A00A5C2DA /* Extension */ = { + isa = PBXGroup; + children = ( + BFC6768C2E123D67006659E5 /* AttributedString+BRAdd.swift */, + BFC6767A2E0E9736006659E5 /* UIStackView+BRAdd.swift */, + BFC6766E2E0E3B51006659E5 /* UIImageView+BRAdd.swift */, + BF692B572E0AAA5F00A5C2DA /* UIScreen+BRAdd.swift */, + BF692B482E0A9D0800A5C2DA /* UIView+BRAdd.swift */, + BF692B412E0A8FAE00A5C2DA /* UIFont+BRAdd.swift */, + BF692B3F2E0A8F9D00A5C2DA /* UIColor+BRAdd.swift */, + BF692B332E0A87BD00A5C2DA /* UIDevice+BRAdd.swift */, + BF692B212E0A820C00A5C2DA /* String+BRAdd.swift */, + BF692B0D2E0A7AED00A5C2DA /* UserDefaults+BRAdd.swift */, + BF692B642E0BC53900A5C2DA /* CGMutablePath+BRRoundedCorner.swift */, + ); + path = Extension; + sourceTree = ""; + }; + BF692B112E0A7B7500A5C2DA /* User */ = { + isa = PBXGroup; + children = ( + BF692B122E0A7B9000A5C2DA /* BRUserInfo.swift */, + ); + path = User; + sourceTree = ""; + }; + BF692B142E0A7CB500A5C2DA /* HUD */ = { + isa = PBXGroup; + children = ( + BF692B152E0A7CD200A5C2DA /* BRHUD.swift */, + BF692B172E0A7D8200A5C2DA /* BRToast.swift */, + ); + path = HUD; + sourceTree = ""; + }; + BF692B192E0A7DBB00A5C2DA /* AppTool */ = { + isa = PBXGroup; + children = ( + BF692B1B2E0A7DE200A5C2DA /* BRAppTool.swift */, + ); + path = AppTool; + sourceTree = ""; + }; + BF692B1D2E0A803000A5C2DA /* LocalizedManager */ = { + isa = PBXGroup; + children = ( + BF692B1E2E0A804100A5C2DA /* BRLocalizedManager.swift */, + ); + path = LocalizedManager; + sourceTree = ""; + }; + BF692B292E0A84F700A5C2DA /* JXUUID */ = { + isa = PBXGroup; + children = ( + BF692B252E0A84F700A5C2DA /* JXUUID.h */, + BF692B262E0A84F700A5C2DA /* JXUUID.m */, + BF692B272E0A84F700A5C2DA /* PDKeyChain.h */, + BF692B282E0A84F700A5C2DA /* PDKeyChain.m */, + ); + path = JXUUID; + sourceTree = ""; + }; + BF692B352E0A8AF200A5C2DA /* Controller */ = { + isa = PBXGroup; + children = ( + BF692B3D2E0A8D2300A5C2DA /* BRTabBarController.swift */, + BF692B3B2E0A8D0200A5C2DA /* BRNavigationController.swift */, + BF692B392E0A8C6000A5C2DA /* BRViewController.swift */, + ); + path = Controller; + sourceTree = ""; + }; + BF692B452E0A9B5800A5C2DA /* Player */ = { + isa = PBXGroup; + children = ( + BF692B462E0A9B7400A5C2DA /* BRPlayer.swift */, + ); + path = Player; + sourceTree = ""; + }; + BF692B4A2E0AA84C00A5C2DA /* Player */ = { + isa = PBXGroup; + children = ( + BF692B4F2E0AA88B00A5C2DA /* Controller */, + BF692B4D2E0AA88000A5C2DA /* View */, + BF692B4E2E0AA88600A5C2DA /* Model */, + BF692B4B2E0AA86200A5C2DA /* ViewModel */, + ); + path = Player; + sourceTree = ""; + }; + BF692B4B2E0AA86200A5C2DA /* ViewModel */ = { + isa = PBXGroup; + children = ( + BFC676822E122CC5006659E5 /* BRPlayerViewModel.swift */, + BFC6768A2E12368C006659E5 /* BRVideoRevolutionManager.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + BF692B4D2E0AA88000A5C2DA /* View */ = { + isa = PBXGroup; + children = ( + BF692B592E0AAADD00A5C2DA /* BRPlayerListCell.swift */, + ); + path = View; + sourceTree = ""; + }; + BF692B4E2E0AA88600A5C2DA /* Model */ = { + isa = PBXGroup; + children = ( + BFC676802E122733006659E5 /* BRPlayerProtocol.swift */, + BFC676862E122E36006659E5 /* BRVideoDetailModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + BF692B4F2E0AA88B00A5C2DA /* Controller */ = { + isa = PBXGroup; + children = ( + BF692B502E0AA8C600A5C2DA /* BRPlayerListViewController.swift */, + BFC676842E122D9E006659E5 /* BRVideoDetailViewController.swift */, + ); + path = Controller; + sourceTree = ""; + }; + BF692B522E0AA8D600A5C2DA /* View */ = { + isa = PBXGroup; + children = ( + BF692B5B2E0AB31700A5C2DA /* TabBar */, + BF692B532E0AA8FA00A5C2DA /* BRCollectionView.swift */, + BF692B552E0AA92100A5C2DA /* BRCollectionViewCell.swift */, + BFC6766C2E0E3A8D006659E5 /* BRImageView.swift */, + BFC676722E0E938B006659E5 /* BRTableView.swift */, + BFC676742E0E93B3006659E5 /* BRTableViewCell.swift */, + ); + path = View; + sourceTree = ""; + }; + BF692B5B2E0AB31700A5C2DA /* TabBar */ = { + isa = PBXGroup; + children = ( + BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */, + BF692B622E0B9D4800A5C2DA /* BRTabBarItem.swift */, + BF692B602E0B814F00A5C2DA /* BRTabBarItemContentView.swift */, + BF692B5E2E0B812800A5C2DA /* BRTabBarItemContainer.swift */, + ); + path = TabBar; + sourceTree = ""; + }; + BF692B682E0BC78C00A5C2DA /* Home */ = { + isa = PBXGroup; + children = ( + BF692B692E0BC82C00A5C2DA /* Controller */, + BF692B6C2E0BD4B200A5C2DA /* View */, + BF692B762E0D3A0300A5C2DA /* Model */, + BF692B7D2E0D3D4C00A5C2DA /* ViewModel */, + ); + path = Home; + sourceTree = ""; + }; + BF692B692E0BC82C00A5C2DA /* Controller */ = { + isa = PBXGroup; + children = ( + BF692B6A2E0BC85300A5C2DA /* BRHomeViewController.swift */, + BFC676702E0E9234006659E5 /* BRSpotlightViewViewController.swift */, + BFC6769C2E129794006659E5 /* BRHomeTop10ViewController.swift */, + ); + path = Controller; + sourceTree = ""; + }; + BF692B6C2E0BD4B200A5C2DA /* View */ = { + isa = PBXGroup; + children = ( + BFC676762E0E950A006659E5 /* Spotlight */, + BF692B6D2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift */, + BFC6766A2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift */, + ); + path = View; + sourceTree = ""; + }; + BF692B702E0D394800A5C2DA /* Base */ = { + isa = PBXGroup; + children = ( + BF692B232E0A825600A5C2DA /* BRCryptorService.swift */, + BF692AFB2E0A6F1B00A5C2DA /* BRNetworkTarget.swift */, + BF692AF92E0A6EFD00A5C2DA /* BRNetwork.swift */, + BF78108A2E0D4EB3007DEEBC /* BRURLPath.swift */, + ); + path = Base; + sourceTree = ""; + }; + BF692B712E0D395300A5C2DA /* API */ = { + isa = PBXGroup; + children = ( + BFC676882E122FDB006659E5 /* BRVideoAPI.swift */, + BFC676682E0E34D9006659E5 /* BRUserAPI.swift */, + BF692B722E0D396800A5C2DA /* BRHomeAPI.swift */, + ); + path = API; + sourceTree = ""; + }; + BF692B762E0D3A0300A5C2DA /* Model */ = { + isa = PBXGroup; + children = ( + BF692B772E0D3A1200A5C2DA /* BRHomeModuleItem.swift */, + BF692B792E0D3BD300A5C2DA /* BRShortModel.swift */, + BF692B7B2E0D3C1300A5C2DA /* BRVideoInfoModel.swift */, + BFC6769A2E1285C5006659E5 /* BRPagerViewTransformer.swift */, + ); + path = Model; + sourceTree = ""; + }; + BF692B7D2E0D3D4C00A5C2DA /* ViewModel */ = { + isa = PBXGroup; + children = ( + BFC676512E0D4EFD006659E5 /* BRHomeViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + BFC676612E0E2C8E006659E5 /* WMZBanner */ = { + isa = PBXGroup; + children = ( + BFC676542E0E2C8E006659E5 /* WMZBannerConfig.h */, + BFC676552E0E2C8E006659E5 /* WMZBannerControl.h */, + BFC676562E0E2C8E006659E5 /* WMZBannerControl.m */, + BFC676572E0E2C8E006659E5 /* WMZBannerFadeLayout.h */, + BFC676582E0E2C8E006659E5 /* WMZBannerFadeLayout.m */, + BFC676592E0E2C8E006659E5 /* WMZBannerFlowLayout.h */, + BFC6765A2E0E2C8E006659E5 /* WMZBannerFlowLayout.m */, + BFC6765B2E0E2C8E006659E5 /* WMZBannerOverLayout.h */, + BFC6765C2E0E2C8E006659E5 /* WMZBannerOverLayout.m */, + BFC6765D2E0E2C8E006659E5 /* WMZBannerParam.h */, + BFC6765E2E0E2C8E006659E5 /* WMZBannerParam.m */, + BFC6765F2E0E2C8E006659E5 /* WMZBannerView.h */, + BFC676602E0E2C8E006659E5 /* WMZBannerView.m */, + ); + path = WMZBanner; + sourceTree = ""; + }; + BFC676762E0E950A006659E5 /* Spotlight */ = { + isa = PBXGroup; + children = ( + BFC676772E0E9553006659E5 /* BRSpotlightMainBaseCell.swift */, + BFC6767C2E0E9809006659E5 /* BRSpotlightHotMainCell.swift */, + BFC6767E2E121A72006659E5 /* BRSpotlightHotCell.swift */, + BFC6768E2E125D5B006659E5 /* BRSpotlightTopMainCell.swift */, + BFC676902E126248006659E5 /* BRSpotlightTopCell.swift */, + BFC676922E126A62006659E5 /* BRSpotlightNewMainCell.swift */, + BFC676942E126BBF006659E5 /* BRSpotlightNewCell.swift */, + BFC676962E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift */, + BFC676982E1280E3006659E5 /* BRSpotlightRecommandCell.swift */, + ); + path = Spotlight; + sourceTree = ""; + }; + DF948F1E234E75684255568B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 899B3015B03D5E1A5A6507EB /* Pods_BeeReel.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BF692AC82E0A475500A5C2DA /* BeeReel */ = { + isa = PBXNativeTarget; + buildConfigurationList = BF692ADC2E0A475700A5C2DA /* Build configuration list for PBXNativeTarget "BeeReel" */; + buildPhases = ( + 1E47DF7D50FE03B0B10E2DED /* [CP] Check Pods Manifest.lock */, + BF692AC52E0A475500A5C2DA /* Sources */, + BF692AC62E0A475500A5C2DA /* Frameworks */, + BF692AC72E0A475500A5C2DA /* Resources */, + BDB57E17AABB03B647FB0C32 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BeeReel; + productName = ShortBox; + productReference = BF692AC92E0A475500A5C2DA /* BeeReel.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BF692AC12E0A475500A5C2DA /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1640; + LastUpgradeCheck = 1640; + TargetAttributes = { + BF692AC82E0A475500A5C2DA = { + CreatedOnToolsVersion = 16.4; + }; + }; + }; + buildConfigurationList = BF692AC42E0A475500A5C2DA /* Build configuration list for PBXProject "BeeReel" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BF692AC02E0A475500A5C2DA; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = BF692ACA2E0A475500A5C2DA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BF692AC82E0A475500A5C2DA /* BeeReel */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BF692AC72E0A475500A5C2DA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BF692AEE2E0A475D00A5C2DA /* Assets.xcassets in Resources */, + BF692AF02E0A475D00A5C2DA /* LaunchScreen.storyboard in Resources */, + BF692B442E0A910E00A5C2DA /* Localizable.xcstrings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1E47DF7D50FE03B0B10E2DED /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-BeeReel-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + BDB57E17AABB03B647FB0C32 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-BeeReel/Pods-BeeReel-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-BeeReel/Pods-BeeReel-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BeeReel/Pods-BeeReel-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BF692AC52E0A475500A5C2DA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BFC676992E1280E3006659E5 /* BRSpotlightRecommandCell.swift in Sources */, + BF692B3C2E0A8D0200A5C2DA /* BRNavigationController.swift in Sources */, + BFC676692E0E34DA006659E5 /* BRUserAPI.swift in Sources */, + BFC676782E0E9553006659E5 /* BRSpotlightMainBaseCell.swift in Sources */, + BFC676732E0E938B006659E5 /* BRTableView.swift in Sources */, + BFC676932E126A62006659E5 /* BRSpotlightNewMainCell.swift in Sources */, + BFC6768D2E123D6E006659E5 /* AttributedString+BRAdd.swift in Sources */, + BF692B132E0A7B9000A5C2DA /* BRUserInfo.swift in Sources */, + BF692B042E0A76D200A5C2DA /* BRLoginManager.swift in Sources */, + BFC6769D2E129794006659E5 /* BRHomeTop10ViewController.swift in Sources */, + BF692AEB2E0A475D00A5C2DA /* AppDelegate.swift in Sources */, + BF692B242E0A825B00A5C2DA /* BRCryptorService.swift in Sources */, + BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */, + BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */, + BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */, + BF692B472E0A9B7900A5C2DA /* BRPlayer.swift in Sources */, + BF692B6E2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift in Sources */, + BF692AFA2E0A6F0900A5C2DA /* BRNetwork.swift in Sources */, + BF692B6B2E0BC85300A5C2DA /* BRHomeViewController.swift in Sources */, + BF692B7C2E0D3C1300A5C2DA /* BRVideoInfoModel.swift in Sources */, + BFC6766D2E0E3A8D006659E5 /* BRImageView.swift in Sources */, + BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */, + BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */, + BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */, + BF692B162E0A7CD600A5C2DA /* BRHUD.swift in Sources */, + BFC676952E126BBF006659E5 /* BRSpotlightNewCell.swift in Sources */, + BF692B402E0A8FA100A5C2DA /* UIColor+BRAdd.swift in Sources */, + BF692B102E0A7B4300A5C2DA /* BRUserDefaultsKey.swift in Sources */, + BFC676852E122D9E006659E5 /* BRVideoDetailViewController.swift in Sources */, + BFC676912E126248006659E5 /* BRSpotlightTopCell.swift in Sources */, + BF692B422E0A8FB500A5C2DA /* UIFont+BRAdd.swift in Sources */, + BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */, + BF692B492E0A9D0E00A5C2DA /* UIView+BRAdd.swift in Sources */, + BFC676812E122733006659E5 /* BRPlayerProtocol.swift in Sources */, + BF692B5F2E0B812800A5C2DA /* BRTabBarItemContainer.swift in Sources */, + BF692B652E0BC53900A5C2DA /* CGMutablePath+BRRoundedCorner.swift in Sources */, + BFC676752E0E93B3006659E5 /* BRTableViewCell.swift in Sources */, + BF78108B2E0D4EB3007DEEBC /* BRURLPath.swift in Sources */, + BF692B092E0A775500A5C2DA /* BRLoginToken.swift in Sources */, + BF692AFC2E0A6F8000A5C2DA /* BRNetworkTarget.swift in Sources */, + BF692B3A2E0A8C6000A5C2DA /* BRViewController.swift in Sources */, + BF692B182E0A7D8900A5C2DA /* BRToast.swift in Sources */, + BF692B0E2E0A7AF300A5C2DA /* UserDefaults+BRAdd.swift in Sources */, + BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */, + BF692B1F2E0A804600A5C2DA /* BRLocalizedManager.swift in Sources */, + BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */, + BF692B012E0A74A200A5C2DA /* BRDefine.swift in Sources */, + BFC6767B2E0E973B006659E5 /* UIStackView+BRAdd.swift in Sources */, + BFC676892E122FDD006659E5 /* BRVideoAPI.swift in Sources */, + BFC6769B2E1285C5006659E5 /* BRPagerViewTransformer.swift in Sources */, + BFC676832E122CC5006659E5 /* BRPlayerViewModel.swift in Sources */, + BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */, + BF692B222E0A820D00A5C2DA /* String+BRAdd.swift in Sources */, + BF692B632E0B9D4800A5C2DA /* BRTabBarItem.swift in Sources */, + BFC6768B2E123690006659E5 /* BRVideoRevolutionManager.swift in Sources */, + BFC6768F2E125D5B006659E5 /* BRSpotlightTopMainCell.swift in Sources */, + BF692B1C2E0A7DE800A5C2DA /* BRAppTool.swift in Sources */, + BFC676622E0E2C8E006659E5 /* WMZBannerView.m in Sources */, + BFC676632E0E2C8E006659E5 /* WMZBannerOverLayout.m in Sources */, + BFC676642E0E2C8E006659E5 /* WMZBannerFlowLayout.m in Sources */, + BFC676652E0E2C8E006659E5 /* WMZBannerControl.m in Sources */, + BFC676972E127D3C006659E5 /* BRSpotlightRecommandMainCell.swift in Sources */, + BFC6767F2E121A72006659E5 /* BRSpotlightHotCell.swift in Sources */, + BFC6767D2E0E9809006659E5 /* BRSpotlightHotMainCell.swift in Sources */, + BFC676662E0E2C8E006659E5 /* WMZBannerFadeLayout.m in Sources */, + BFC676872E122E36006659E5 /* BRVideoDetailModel.swift in Sources */, + BFC676672E0E2C8E006659E5 /* WMZBannerParam.m in Sources */, + BF692B732E0D397700A5C2DA /* BRHomeAPI.swift in Sources */, + BF692B7A2E0D3BD300A5C2DA /* BRShortModel.swift in Sources */, + BFC676712E0E9234006659E5 /* BRSpotlightViewViewController.swift in Sources */, + BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */, + BF692B562E0AA92100A5C2DA /* BRCollectionViewCell.swift in Sources */, + BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */, + BF692B752E0D39D000A5C2DA /* BRListModel.swift in Sources */, + BF692B512E0AA8C600A5C2DA /* BRPlayerListViewController.swift in Sources */, + BFC676522E0D4EFD006659E5 /* BRHomeViewModel.swift in Sources */, + BF692B2A2E0A84F700A5C2DA /* JXUUID.m in Sources */, + BF692B2B2E0A84F700A5C2DA /* PDKeyChain.m in Sources */, + BFC6766B2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + BF692AE52E0A475D00A5C2DA /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BF692AE42E0A475D00A5C2DA /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + BF692ADD2E0A475700A5C2DA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F06627B1DEE86552C2A87AEC /* Pods-BeeReel.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 394VH538M8; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = BeeReel/Sources/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = BeeReel; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.BeeReel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "BeeReel/Sources/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + BF692ADE2E0A475700A5C2DA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F70FA1F4169364C4C53534CE /* Pods-BeeReel.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 394VH538M8; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = BeeReel/Sources/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = BeeReel; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.BeeReel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "BeeReel/Sources/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + BF692ADF2E0A475700A5C2DA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 394VH538M8; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + BF692AE02E0A475700A5C2DA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 394VH538M8; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BF692AC42E0A475500A5C2DA /* Build configuration list for PBXProject "BeeReel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BF692ADF2E0A475700A5C2DA /* Debug */, + BF692AE02E0A475700A5C2DA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BF692ADC2E0A475700A5C2DA /* Build configuration list for PBXNativeTarget "BeeReel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BF692ADD2E0A475700A5C2DA /* Debug */, + BF692ADE2E0A475700A5C2DA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BF692AC12E0A475500A5C2DA /* Project object */; +} diff --git a/BeeReel/Base/Controller/BRNavigationController.swift b/BeeReel/Base/Controller/BRNavigationController.swift new file mode 100644 index 0000000..09c44ad --- /dev/null +++ b/BeeReel/Base/Controller/BRNavigationController.swift @@ -0,0 +1,44 @@ +// +// BRNavigationController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRNavigationController: UINavigationController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func pushViewController(_ viewController: UIViewController, animated: Bool) { + if children.count > 0 { + viewController.hidesBottomBarWhenPushed = true + } + super.pushViewController(viewController, animated: animated) + } + + override func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) { + for (index, value) in viewControllers.enumerated() { + if index != 0 { + value.hidesBottomBarWhenPushed = true + } + } + super.setViewControllers(viewControllers, animated: animated) + } + + + //MARK:-------------- 状态栏样式 -------------- + override var childForStatusBarStyle: UIViewController? { + return self.topViewController + } + + override var childForStatusBarHidden: UIViewController? { + return self.topViewController + } + +} diff --git a/BeeReel/Base/Controller/BRTabBarController.swift b/BeeReel/Base/Controller/BRTabBarController.swift new file mode 100644 index 0000000..0273498 --- /dev/null +++ b/BeeReel/Base/Controller/BRTabBarController.swift @@ -0,0 +1,118 @@ +// +// BRTabBarController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRTabBarController: UITabBarController { + + private var ignoreNextSelection = false + + override var selectedViewController: UIViewController? { + willSet { + guard let newValue = newValue else { + // if newValue == nil ... + return + } + + guard !ignoreNextSelection else { + ignoreNextSelection = false + return + } + + guard let tabBar = self.tabBar as? BRTabBar, let index = viewControllers?.firstIndex(of: newValue) else { + return + } + tabBar.select(itemAtIndex: index, animated: false) + } + } + + override var selectedIndex: Int { + willSet { + guard let tabBar = self.tabBar as? BRTabBar else { + return + } + guard !ignoreNextSelection else { + ignoreNextSelection = false + return + } + + tabBar.select(itemAtIndex: newValue, animated: false) + } + } + + override func viewDidLoad() { + super.viewDidLoad() + let tabBar = BRTabBar() + tabBar.delegate = self + self.setValue(tabBar, forKey: "tabBar") + self.tabBar.isTranslucent = true + + + br_setup() + } + + + //MARK:-------------- 状态栏样式 -------------- + override var childForStatusBarStyle: UIViewController? { + return self.selectedViewController + } + + override var childForStatusBarHidden: UIViewController? { + return self.selectedViewController + } + +} + +extension BRTabBarController { + + 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 nav2 = createNavigationController(viewController: BRViewController(), 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 nav4 = createNavigationController(viewController: BRViewController(), title: "首页".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected")) + + viewControllers = [nav1, nav2, nav3, nav4] + + } + + private func createNavigationController(viewController: UIViewController, title: String?, image: UIImage?, selectedImage: UIImage?) -> UINavigationController { + let tabBarItem = BRTabBarItem() + tabBarItem.title = title + tabBarItem.image = image + tabBarItem.selectedImage = selectedImage + + let nav = BRNavigationController(rootViewController: viewController) + nav.tabBarItem = tabBarItem + return nav + } +} + +extension BRTabBarController { + + override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { + guard let idx = tabBar.items?.firstIndex(of: item) else { + return; + } + if let vc = viewControllers?[idx] { + ignoreNextSelection = true + selectedIndex = idx + delegate?.tabBarController?(self, didSelect: vc) + } + } + + override func tabBar(_ tabBar: UITabBar, willBeginCustomizing items: [UITabBarItem]) { + if let tabBar = tabBar as? BRTabBar { + tabBar.updateLayout() + } + } + + override func tabBar(_ tabBar: UITabBar, didEndCustomizing items: [UITabBarItem], changed: Bool) { + if let tabBar = tabBar as? BRTabBar { + tabBar.updateLayout() + } + } +} diff --git a/BeeReel/Base/Controller/BRViewController.swift b/BeeReel/Base/Controller/BRViewController.swift new file mode 100644 index 0000000..81ff9f3 --- /dev/null +++ b/BeeReel/Base/Controller/BRViewController.swift @@ -0,0 +1,54 @@ +// +// BRViewController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRViewController: UIViewController { + + private(set) var isViewDidLoad = false + private(set) var isDidAppear = false + private(set) var hasViewDidAppear = false + + var statusBarStyle: UIStatusBarStyle = .darkContent { + didSet { + self.setNeedsStatusBarAppearanceUpdate() + } + } + + override func viewDidLoad() { + super.viewDidLoad() + self.edgesForExtendedLayout = [] + self.isViewDidLoad = true + self.view.backgroundColor = .backgroundColor() + + } + + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + isDidAppear = true + hasViewDidAppear = true + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + isDidAppear = false + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + isDidAppear = false + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return statusBarStyle + } + + func handleHeaderRefresh(_ completer: (() -> Void)?) {} + + func handleFooterRefresh(_ completer: (() -> Void)?) {} +} diff --git a/BeeReel/Base/Define/BRDefine.swift b/BeeReel/Base/Define/BRDefine.swift new file mode 100644 index 0000000..f1503ad --- /dev/null +++ b/BeeReel/Base/Define/BRDefine.swift @@ -0,0 +1,47 @@ +// +// BRDefine.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +//MARK:-------------- 系统版本号 -------------- +///当前系统版本号 +let kBROsVersion: String = UIDevice.current.systemVersion +let kBRAPPBundleIdentifier: String = (Bundle.main.infoDictionary!["CFBundleIdentifier"] as? String) ?? "0" + +///app版本号 +public let kBRAPPVersion: String = (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String) ?? "0" +public let kSBAPPBundleVersion: String = (Bundle.main.infoDictionary!["CFBundleVersion"] as? String) ?? "0" + +public let kBRAPPBundleName: String = (Bundle.main.infoDictionary!["CFBundleName"] as? String) ?? "" +public let kBRAPPName: String = (Bundle.main.infoDictionary!["CFBundleDisplayName"] as? String) ?? "" + + +//MARK: ------- 打印信息 ---------- +#if DEBUG +public func brLog(message: Any? , file: String = #file, function: String = #function, line: Int = #line) { + print("\n\(Date(timeIntervalSinceNow: 8 * 60 * 60)) \(file.components(separatedBy: "/").last ?? "") \(function) \(line): \(message ?? "")") +} +#else +public func brLog(message: Any?) { } +#endif + +public func br_swizzled_instanceMethod(_ prefix: String, oldClass: Swift.AnyClass!, oldSelector: String, newClass: Swift.AnyClass) { + let newSelector = prefix + "_" + oldSelector; + let originalSelector = NSSelectorFromString(oldSelector) + let swizzledSelector = NSSelectorFromString(newSelector) + + let originalMethod = class_getInstanceMethod(oldClass, originalSelector) + let swizzledMethod = class_getInstanceMethod(newClass, swizzledSelector) + + let isAdd = class_addMethod(oldClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) + + if isAdd { + class_replaceMethod(newClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!)) + }else { + method_exchangeImplementations(originalMethod!, swizzledMethod!) + } +} diff --git a/BeeReel/Base/Define/BRUserDefaultsKey.swift b/BeeReel/Base/Define/BRUserDefaultsKey.swift new file mode 100644 index 0000000..0ce5633 --- /dev/null +++ b/BeeReel/Base/Define/BRUserDefaultsKey.swift @@ -0,0 +1,17 @@ +// +// BRUserDefaultsKey.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + + +///登录token +let kBRLoginTokenDefaultsKey = "kBRLoginTokenDefaultsKey" + +///用户信息 +let kBRLoginUserInfoDefaultsKey = "kBRLoginUserInfoDefaultsKey" + +///分辨率 +let kBRVideoRevolutionDefaultsKey = "kBRVideoRevolutionDefaultsKey" + diff --git a/BeeReel/Base/Extension/AttributedString+BRAdd.swift b/BeeReel/Base/Extension/AttributedString+BRAdd.swift new file mode 100644 index 0000000..78c5aba --- /dev/null +++ b/BeeReel/Base/Extension/AttributedString+BRAdd.swift @@ -0,0 +1,19 @@ +// +// AttributedString+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +extension AttributedString { + + static func br_createAttributedString(string: String, color: UIColor, font: UIFont) -> AttributedString { + return AttributedString(string, attributes: AttributeContainer([ + .foregroundColor: color, + .font : font + ])) + } + +} diff --git a/BeeReel/Base/Extension/CGMutablePath+BRRoundedCorner.swift b/BeeReel/Base/Extension/CGMutablePath+BRRoundedCorner.swift new file mode 100644 index 0000000..83bdbf6 --- /dev/null +++ b/BeeReel/Base/Extension/CGMutablePath+BRRoundedCorner.swift @@ -0,0 +1,65 @@ +// +// CGMutablePath+BRRoundedCorner.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/25. +// + +import UIKit + +struct BRRoundedCorner { + var topLeft:CGFloat = 0 + var topRight:CGFloat = 0 + var bottomLeft:CGFloat = 0 + var bottomRight:CGFloat = 0 + + public static let zero = BRRoundedCorner(topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0) + public init(topLeft: CGFloat, topRight:CGFloat, bottomLeft:CGFloat, bottomRight:CGFloat) { + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight + } + static func ==(v1:BRRoundedCorner, v2:BRRoundedCorner) -> Bool { + return v1.bottomLeft == v2.bottomLeft + && v1.bottomRight == v2.bottomRight + && v1.topLeft == v2.topLeft + && v1.topRight == v2.topRight + } + static func !=(v1:BRRoundedCorner, v2:BRRoundedCorner) -> Bool { + return !(v1 == v2) + } +} + +extension CGMutablePath { + func addRadiusRectangle(_ circulars: BRRoundedCorner, rect: CGRect) { + let minX = rect.minX + let minY = rect.minY + let maxX = rect.maxX + let maxY = rect.maxY + + //获取四个圆心 + let topLeftCenterX = minX + circulars.topLeft + let topLeftCenterY = minY + circulars.topLeft + + let topRightCenterX = maxX - circulars.topRight + let topRightCenterY = minY + circulars.topRight + + let bottomLeftCenterX = minX + circulars.bottomLeft + let bottomLeftCenterY = maxY - circulars.bottomLeft + + let bottomRightCenterX = maxX - circulars.bottomRight + let bottomRightCenterY = maxY - circulars.bottomRight + + //顶 左 + addArc(center: CGPoint(x: topLeftCenterX, y: topLeftCenterY), radius: circulars.topLeft, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 3 / 2, clockwise: false) + //顶右 + addArc(center: CGPoint(x: topRightCenterX, y: topRightCenterY), radius: circulars.topRight, startAngle: CGFloat.pi * 3 / 2, endAngle: 0, clockwise: false) + //底右 + addArc(center: CGPoint(x: bottomRightCenterX, y: bottomRightCenterY), radius: circulars.bottomRight, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: false) + //底左 + addArc(center: CGPoint(x: bottomLeftCenterX, y: bottomLeftCenterY), radius: circulars.bottomLeft, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: false) + closeSubpath(); + + } +} diff --git a/BeeReel/Base/Extension/String+BRAdd.swift b/BeeReel/Base/Extension/String+BRAdd.swift new file mode 100644 index 0000000..f645b3d --- /dev/null +++ b/BeeReel/Base/Extension/String+BRAdd.swift @@ -0,0 +1,12 @@ +// +// String+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import SmartCodable + +extension String: SmartCodable { + +} diff --git a/BeeReel/Base/Extension/UIColor+BRAdd.swift b/BeeReel/Base/Extension/UIColor+BRAdd.swift new file mode 100644 index 0000000..930d0ba --- /dev/null +++ b/BeeReel/Base/Extension/UIColor+BRAdd.swift @@ -0,0 +1,53 @@ +// +// UIColor.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +extension UIColor { + + static func backgroundColor() -> UIColor { + return colorEFEFF5() + } + + static func themeColor() -> UIColor { + return color1C1C1C() + } +} + +extension UIColor { + static func colorFFFFFF(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xFFFFFF, alpha: alpha) + } + + static func color000000(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0x000000, alpha: alpha) + } + + static func color1C1C1C(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0x1C1C1C, alpha: alpha) + } + + static func colorEFEFF5(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xEFEFF5, alpha: alpha) + } + + static func colorD3D3D3(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xD3D3D3, alpha: alpha) + } + + static func colorF1FF94(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xF1FF94, alpha: alpha) + } + + static func color899D00(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0x899D00, alpha: alpha) + } + + static func colorFF7489(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xFF7489, alpha: alpha) + } +} diff --git a/BeeReel/Base/Extension/UIDevice+BRAdd.swift b/BeeReel/Base/Extension/UIDevice+BRAdd.swift new file mode 100644 index 0000000..c052a6f --- /dev/null +++ b/BeeReel/Base/Extension/UIDevice+BRAdd.swift @@ -0,0 +1,71 @@ +// +// UIDevice+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +extension UIDevice { + //http://theiphonewiki.com/wiki/Models + static func br_machineModelName() -> String { + guard let machineModel = UIDevice.current.machineModel else { return "" } + let map = [ + "iPhone1,1" : "iPhone", + "iPhone1,2" : "iPhone 3G", + "iPhone2,1" : "iPhone 3GS", + "iPhone3,1" : "iPhone 4", + "iPhone3,2" : "iPhone 4", + "iPhone3,3" : "iPhone 4", + "iPhone4,1" : "iPhone4,1", + "iPhone5,1" : "iPhone 5", + "iPhone5,2" : "iPhone 5", + "iPhone5,3" : "iPhone 5c", + "iPhone5,4" : "iPhone 5c", + "iPhone6,1" : "iPhone 5s", + "iPhone6,2" : "iPhone 5s", + "iPhone7,2" : "iPhone 6", + "iPhone7,1" : "iPhone 6 Plus", + "iPhone8,1" : "iPhone 6s", + "iPhone8,2" : "iPhone 6s Plus", + "iPhone8,4" : "iPhone SE (1st generation)", + "iPhone9,1" : "iPhone 7", + "iPhone9,3" : "iPhone 7", + "iPhone9,2" : "iPhone 7 Plus", + "iPhone9,4" : "iPhone 7 Plus", + "iPhone10,1" : "iPhone 8", + "iPhone10,4" : "iPhone 8", + "iPhone10,2" : "iPhone 8 Plus", + "iPhone10,5" : "iPhone 8 Plus", + "iPhone10,3" : "iPhone X", + "iPhone10,6" : "iPhone X", + "iPhone11,8" : "iPhone XR", + "iPhone11,2" : "iPhone11,2", + "iPhone11,6" : "iPhone XS Max", + "iPhone11,4" : "iPhone XS Max", + "iPhone12,1" : "iPhone 11", + "iPhone12,3" : "iPhone 11 Pro", + "iPhone12,5" : "iPhone 11 Pro Max", + "iPhone12,8" : "iPhone SE (2nd generation)", + "iPhone13,1" : "iPhone 12 mini", + "iPhone13,2" : "iPhone13,2", + "iPhone13,3" : "iPhone 12 Pro", + "iPhone13,4" : "iPhone 12 Pro Max", + "iPhone14,4" : "iPhone 13 mini", + "iPhone14,5" : "iPhone 13", + "iPhone14,2" : "iPhone 13 Pro", + "iPhone14,3" : "iPhone 13 Pro Max", + "iPhone14,6" : "iPhone SE (3rd generation)", + "iPhone14,7" : "iPhone 14", + "iPhone14,8" : "iPhone 14 Plus", + "iPhone15,2" : "iPhone 14 Pro", + "iPhone15,3" : "iPhone 14 Pro Max", + ] + if let name = map[machineModel] { + return name + } + + return machineModel + } +} diff --git a/BeeReel/Base/Extension/UIFont+BRAdd.swift b/BeeReel/Base/Extension/UIFont+BRAdd.swift new file mode 100644 index 0000000..ca26bfb --- /dev/null +++ b/BeeReel/Base/Extension/UIFont+BRAdd.swift @@ -0,0 +1,24 @@ +// +// UIFont+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + + +extension UIFont { + + static func fontRegular(ofSize: CGFloat) -> UIFont { + return .systemFont(ofSize: ofSize, weight: .regular) + } + + static func fontMedium(ofSize: CGFloat) -> UIFont { + return .systemFont(ofSize: ofSize, weight: .medium) + } + + static func fontBold(ofSize: CGFloat) -> UIFont { + return .systemFont(ofSize: ofSize, weight: .bold) + } +} diff --git a/BeeReel/Base/Extension/UIImageView+BRAdd.swift b/BeeReel/Base/Extension/UIImageView+BRAdd.swift new file mode 100644 index 0000000..fd6f3f5 --- /dev/null +++ b/BeeReel/Base/Extension/UIImageView+BRAdd.swift @@ -0,0 +1,24 @@ +// +// UIImageView+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit +import Kingfisher + +extension UIImageView { + func br_setImage(url: String?, placeholder: UIImage? = nil, completer: ((_ image: UIImage?, _ url: URL?) -> Void)? = nil) { + + self.kf.setImage(with: URL(string: url ?? ""), placeholder: placeholder, options: nil) { result in + switch result { + case .success(let value): + completer?(value.image, value.source.url) + default : + completer?(nil, nil) + break + } + } + } +} diff --git a/BeeReel/Base/Extension/UIScreen+BRAdd.swift b/BeeReel/Base/Extension/UIScreen+BRAdd.swift new file mode 100644 index 0000000..1b0c91c --- /dev/null +++ b/BeeReel/Base/Extension/UIScreen+BRAdd.swift @@ -0,0 +1,49 @@ +// +// UIScreen+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +extension UIScreen { + + static var screen: UIScreen { + if let screen = BRAppTool.windowScene?.screen { + return screen + } else { + return UIScreen.main + } + } + + static var width: CGFloat { + return screen.bounds.width + } + + static var height: CGFloat { + return screen.bounds.height + } + + static var statusBarHeight: CGFloat { + let top = BRAppTool.windowScene?.windows.first?.safeAreaInsets.top ?? 20 + return top + } + + static var tabbarSafeBottomMargin: CGFloat { + let bottom = BRAppTool.windowScene?.windows.first?.safeAreaInsets.bottom ?? 0 + return bottom + } + + static var navBarHeight: CGFloat { + return statusBarHeight + 44 + } + + static var tabBarHeight: CGFloat { + return tabbarSafeBottomMargin + 49 + } + + static var customTabBarHeight: CGFloat { + return tabbarSafeBottomMargin + 50 + } +} diff --git a/BeeReel/Base/Extension/UIStackView+BRAdd.swift b/BeeReel/Base/Extension/UIStackView+BRAdd.swift new file mode 100644 index 0000000..e978947 --- /dev/null +++ b/BeeReel/Base/Extension/UIStackView+BRAdd.swift @@ -0,0 +1,18 @@ +// +// UIStackView+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +extension UIStackView { + + func br_removeAllArrangedSubview() { + let arrangedSubviews = self.arrangedSubviews + + arrangedSubviews.forEach { + self.removeArrangedSubview($0) + $0.removeFromSuperview() + } + } +} diff --git a/BeeReel/Base/Extension/UIView+BRAdd.swift b/BeeReel/Base/Extension/UIView+BRAdd.swift new file mode 100644 index 0000000..5b31f6a --- /dev/null +++ b/BeeReel/Base/Extension/UIView+BRAdd.swift @@ -0,0 +1,94 @@ +// +// UIView+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit +import SnapKit + +extension UIView { + fileprivate struct AssociatedKeys { + static var br_roundedCorner: Int? + static var br_effect: Int? + } + + @objc public static func vp_Awake() { + br_swizzled_instanceMethod("br", oldClass: self, oldSelector: "layoutSubviews", newClass: self) + } + + @objc func br_layoutSubviews() { + br_layoutSubviews() + + _updateRoundedCorner() + + if let effectView = effectView, effectView.frame != self.bounds { + effectView.frame = self.bounds + } + } +} + +//MARK: -------------- 圆角 -------------- +extension UIView { + + + private var roundedCorner: BRRoundedCorner? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.br_roundedCorner) as? BRRoundedCorner + } + set { + objc_setAssociatedObject(self, &AssociatedKeys.br_roundedCorner, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + ///设置圆角 + func setRoundedCorner(topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat) { + //清空其它设置方法 + self.roundedCorner = BRRoundedCorner(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight) + _updateRoundedCorner() + } + + private func _updateRoundedCorner() { + guard let roundedCorner = self.roundedCorner else { return } + let rect = self.bounds + + let path = CGMutablePath() + path.addRadiusRectangle(roundedCorner, rect: rect) + + let maskLayer = CAShapeLayer() + maskLayer.frame = self.bounds + maskLayer.path = path + self.layer.mask = maskLayer + } + +} + +//MARK: -------------- 模糊效果 -------------- +extension UIView { + private var effectView: UIVisualEffectView? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.br_effect) as? UIVisualEffectView + } + set { + objc_setAssociatedObject(self, &AssociatedKeys.br_effect, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + ///添加模糊效果 + func br_addEffectView(style: UIBlurEffect.Style = .light) { + if self.effectView == nil { + let blur = UIBlurEffect(style: style) + let effectView = UIVisualEffectView(effect: blur) + effectView.isUserInteractionEnabled = false + self.addSubview(effectView) + self.sendSubviewToBack(effectView) + + self.effectView = effectView + } + } + ///删除模糊效果 + func br_removeEffectView() { + self.effectView?.removeFromSuperview() + self.effectView = nil + } +} diff --git a/BeeReel/Base/Extension/UserDefaults+BRAdd.swift b/BeeReel/Base/Extension/UserDefaults+BRAdd.swift new file mode 100644 index 0000000..aa67397 --- /dev/null +++ b/BeeReel/Base/Extension/UserDefaults+BRAdd.swift @@ -0,0 +1,44 @@ +// +// UserDefaults+BRAdd.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import Foundation + + +extension UserDefaults { + + static func br_setObject(_ obj: NSSecureCoding?, forKey key: String) { + let defaults = UserDefaults.standard + + guard let obj = obj else { + defaults.removeObject(forKey: key) + return + } + + do { + let data = try NSKeyedArchiver.archivedData(withRootObject: obj, requiringSecureCoding: true) + defaults.set(data, forKey: key) + } catch { + print("Error archiving object: \(error)") + } + } + + static func br_object(forKey key: String, as type: T.Type) -> T? { + let defaults = UserDefaults.standard + + guard let data = defaults.data(forKey: key) else { + return nil + } + + do { + let object = try NSKeyedUnarchiver.unarchivedObject(ofClass: type, from: data) + return object + } catch { + print("Error unarchiving object: \(error)") + return nil + } + } +} diff --git a/BeeReel/Base/Model/BRListModel.swift b/BeeReel/Base/Model/BRListModel.swift new file mode 100644 index 0000000..8f14ead --- /dev/null +++ b/BeeReel/Base/Model/BRListModel.swift @@ -0,0 +1,22 @@ +// +// BRListModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/26. +// + +import UIKit +import SmartCodable + +class BRListModel: BRModel, SmartCodable { + var list: [T]? + var pagination: BRListPaginationModel? +} + + +class BRListPaginationModel: BRModel, SmartCodable { + var current_page: Int? + var page_size: Int? + var page_total: Int? + var total_size: Int? +} diff --git a/BeeReel/Base/Model/BRModel.swift b/BeeReel/Base/Model/BRModel.swift new file mode 100644 index 0000000..1def2ff --- /dev/null +++ b/BeeReel/Base/Model/BRModel.swift @@ -0,0 +1,15 @@ +// +// BRModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRModel: NSObject { + + required override init() { + super.init() + } +} diff --git a/BeeReel/Base/Network/API/BRHomeAPI.swift b/BeeReel/Base/Network/API/BRHomeAPI.swift new file mode 100644 index 0000000..0ad9ca4 --- /dev/null +++ b/BeeReel/Base/Network/API/BRHomeAPI.swift @@ -0,0 +1,32 @@ +// +// BRHomeAPI.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/26. +// + + +class BRHomeAPI { + ///首页数据 + static func requestHomeData(completer: ((_ list: [BRHomeModuleItem]?) -> Void)?) { + var param = BRNetworkParameters(path: "/home/all-modules") + param.method = .get + param.isToast = true + + BRNetwork.request(parameters: param) { (response: BRNetworkResponse>) in + completer?(response.data?.list) + } + } + + ///排行 + static func requestTop10List(completer: ((_ list: [BRShortModel]?) -> Void)?) { + var param = BRNetworkParameters(path: "/getVisitTop") + param.method = .get + + BRNetwork.request(parameters: param) { (response: BRNetworkResponse<[BRShortModel]>) in + completer?(response.data) + } + } + + +} diff --git a/BeeReel/Base/Network/API/BRUserAPI.swift b/BeeReel/Base/Network/API/BRUserAPI.swift new file mode 100644 index 0000000..300d0d1 --- /dev/null +++ b/BeeReel/Base/Network/API/BRUserAPI.swift @@ -0,0 +1,20 @@ +// +// BRUserAPI.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + + +class BRUserAPI { + ///获取用户信息 + static func requestUserInfo(completer: ((_ userInfo: BRUserInfo?) -> Void)?) { + + var param = BRNetworkParameters(path: "/customer/info") + param.method = .get + + BRNetwork.request(parameters: param) { (response: BRNetworkResponse) in + completer?(response.data) + } + } +} diff --git a/BeeReel/Base/Network/API/BRVideoAPI.swift b/BeeReel/Base/Network/API/BRVideoAPI.swift new file mode 100644 index 0000000..782ee7e --- /dev/null +++ b/BeeReel/Base/Network/API/BRVideoAPI.swift @@ -0,0 +1,75 @@ +// +// BRVideoAPI.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRVideoAPI { + ///获取视频详情 + static func requestVideoDetail(shortPlayId: String, activityId: String? = nil, revolution: BRShortModel.VideoRevolution? = nil, completer: ((_ model: BRVideoDetailModel?) -> Void)?) { + var parameters: [String : Any] = [ + "short_play_id" : shortPlayId, + "video_id" : "0" + ] + + if let activityId = activityId { + parameters["activity_id"] = activityId + } + + if let revolution = revolution?.rawValue { + parameters["revolution"] = revolution + } + + var param = BRNetworkParameters(path: "/getVideoDetails") + param.method = .get + param.parameters = parameters + + BRNetwork.request(parameters: param) { (response: BRNetworkResponse) in + completer?(response.data) + } + } + + + ///收藏 + static func requestFavorite(isFavorite: Bool, shortPlayId: String, videoId: String?, isLoding: Bool = true, success: (() -> Void)?, failure: (() -> Void)? = nil) { + let path: String + if isFavorite { + path = "/collect" + } else { + path = "/cancelCollect" + } + + var parameters: [String : Any] = [ + "short_play_id" : shortPlayId, + ] + + if let videoId = videoId { + parameters["video_id"] = videoId + } + + var param = BRNetworkParameters(path: path) + param.isLoding = isLoding + param.parameters = parameters + + BRNetwork.request(parameters: param) { (response: BRNetworkResponse) in + if response.code == BRNetworkCodeSucceed { + success?() + NotificationCenter.default.post(name: BRVideoAPI.updateShortFavoriteStateNotification, object: nil, userInfo: [ + "state" : isFavorite, + "id" : shortPlayId, + ]) + } else { + failure?() + } + } + } +} + + +extension BRVideoAPI { + ///更新短剧关注状态 [ "state" : isFavorite, "id" : shortPlayId,] + @objc static let updateShortFavoriteStateNotification = NSNotification.Name(rawValue: "BRVideoAPI.updateShortFavoriteStateNotification") +} diff --git a/BeeReel/Base/Network/Base/BRCryptorService.swift b/BeeReel/Base/Network/Base/BRCryptorService.swift new file mode 100644 index 0000000..9044dce --- /dev/null +++ b/BeeReel/Base/Network/Base/BRCryptorService.swift @@ -0,0 +1,102 @@ +// +// BRCryptorService.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +struct BRCryptorService { + + // 定义常量 + static let EN_STR_TAG: String = "$" // 替换为实际的加密标记 + + // 解密字符串 + static func decrypt(data: String) -> String { + guard data.hasPrefix(EN_STR_TAG) else { +// fatalError("Invalid encoded string") + return data + } + + let decryptedData = deStrBytes(data: data) + return String(data: decryptedData, encoding: .utf8) ?? "" + } + + // 从十六进制字符串解密字节 + static func deStrBytes(data: String) -> Data { + + let hexData = String(data.dropFirst()) + var bytes = Data() + + var index = hexData.startIndex + while index < hexData.endIndex { + let nextIndex = hexData.index(index, offsetBy: 2, limitedBy: hexData.endIndex) ?? hexData.endIndex + let byteString = String(hexData[index.. Data { + guard !data.isEmpty else { + return data + } + + let saltLen = Int(data[data.startIndex]) + guard data.count >= 1 + saltLen else { + return data + } + + let salt = data.subdata(in: 1..<1+saltLen) + let encryptedData = data.subdata(in: 1+saltLen.. Data { + let decryptedData = cxEd(data: data) + return removeSalt(data: decryptedData, salt: salt) + } + + // 加密/解密数据(按位取反) + static func cxEd(data: Data) -> Data { + return Data(data.map { $0 ^ 0xFF }) + } + + // 从数据中移除盐值 + static func removeSalt(data: Data, salt: Data) -> Data { + guard !salt.isEmpty else { + return data + } + + var result = Data() + let saltBytes = [UInt8](salt) + let saltCount = saltBytes.count + + for (index, byte) in data.enumerated() { + let saltByte = saltBytes[index % saltCount] + let decryptedByte = calRemoveSalt(v: byte, s: saltByte) + result.append(decryptedByte) + } + + return result + } + + // 计算移除盐值后的字节 + static func calRemoveSalt(v: UInt8, s: UInt8) -> UInt8 { + if v >= s { + return v - s + } else { + return UInt8(0xFF) - (s - v) + 1 + } + } +} diff --git a/BeeReel/Base/Network/Base/BRNetwork.swift b/BeeReel/Base/Network/Base/BRNetwork.swift new file mode 100644 index 0000000..e5ef104 --- /dev/null +++ b/BeeReel/Base/Network/Base/BRNetwork.swift @@ -0,0 +1,229 @@ +// +// BRNetwork.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import Foundation +import Moya +import SmartCodable + +///获取数据成功 +let BRNetworkCodeSucceed = 200 + + +struct BRNetwork { + ///用来获取token的线程 + private static let operationQueue = OperationQueue() + private static var tokenOperation: BlockOperation? + + static let provider = MoyaProvider(requestClosure: CustomApiTimeoutClosure) + + static func request(parameters: BRNetworkParameters, completion: ((_ response: BRNetworkResponse) -> Void)?) { + + if BRLoginManager.manager.token == nil { + self.requestToken(completer: nil) + } + + + if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" { + + let requestOperation = BlockOperation { + let semaphore = DispatchSemaphore(value: 0) + _request(parameters: parameters) { (response: BRNetworkResponse) in + semaphore.signal() + completion?(response) + } + semaphore.wait() + } + ///设置依赖关系 + requestOperation.addDependency(tokenOperation) + + operationQueue.addOperation(requestOperation) + } else { + _request(parameters: parameters, completion: completion) + } + } + + @discardableResult + static func _request(parameters: BRNetworkParameters, completion: ((_ response: BRNetworkResponse) -> Void)?) -> Cancellable { + + if parameters.isLoding { + BRHUD.show() + } + return provider.requestCustomJson(.request(parameters: parameters)) { (result) in + + if parameters.isLoding { + BRHUD.dismiss() + } + guard let completion = completion else {return} + + + _resultDispose(parameters: parameters, result: result, completion: completion) + } + } + + + private static func _resultDispose(parameters: BRNetworkParameters, result: Result, completion: ((_ response: BRNetworkResponse) -> Void)?) { + + switch result { + case .success(let response): + let code = response.statusCode + if code == 401 || code == 402 || code == 403 { + + if parameters.path == "/customer/register" { + var res = BRNetworkResponse() + res.code = -1 + if parameters.isToast { + BRToast.show(text: "Error".localized) + } + completion?(res) + } else { + if code == 402, parameters.isToast { + BRToast.show(text: "veloria_network_error_1".localized) + } + ///重新获取token + self.requestToken { token in + if token != nil { + BRLoginManager.manager.updateUserInfo(completer: nil) + } + } + + ///将请求失败数据重新请求 + if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" { + + let requestOperation = BlockOperation { + let semaphore = DispatchSemaphore(value: 0) + _request(parameters: parameters) { (response: BRNetworkResponse) in + semaphore.signal() + completion?(response) + } + semaphore.wait() + } + ///设置依赖关系 + requestOperation.addDependency(tokenOperation) + + operationQueue.addOperation(requestOperation) + } + } + + + return + } + + do { + let tempData = try response.mapString() + brLog(message: parameters.parameters) + brLog(message: parameters.path) + + + DispatchQueue.global().async { + let response: BRNetworkResponse = _deserialize(data: tempData) + + DispatchQueue.main.async { + if response.code != BRNetworkCodeSucceed { + if parameters.isToast { + BRToast.show(text: response.msg) + } + } + completion?(response) + } + } + + } catch { + var res = BRNetworkResponse() + res.code = -1 + if parameters.isToast { + BRToast.show(text: "Error".localized) + } + completion?(res) + } + case .failure(let error): + brLog(message: error) + var res = BRNetworkResponse() + res.code = -1 + if parameters.isToast { + BRToast.show(text: "veloria_network".localized) + } + completion?(res) + break + } + + } + + ///解析数据 + static private func _deserialize(data: String) -> BRNetworkResponse { + var response: BRNetworkResponse? + + let decrypted = BRCryptorService.decrypt(data: data) + brLog(message: decrypted) +// let option: SmartDecodingOption = .key(.fromSnakeCase) + response = BRNetworkResponse.deserialize(from: decrypted) + response?.rawData = decrypted + + if let response = response { + return response + } else { + var response = BRNetworkResponse() + response.code = -1 + response.msg = "Error".localized + return response + } + } +} + +extension BRNetwork { + ///获取token + static func requestToken(completer: ((_ token: BRLoginToken?) -> Void)?) { + guard self.tokenOperation == nil else { + completer?(nil) + return + } + + self.tokenOperation = BlockOperation(block: { + let semaphore = DispatchSemaphore(value: 0) + let param = BRNetworkParameters(path: "/customer/register") + BRNetwork.request(parameters: param) { (response: BRNetworkResponse) in + if let token = response.data { + BRLoginManager.manager.setLoginToken(token: token) + } + do { semaphore.signal() } + self.tokenOperation = nil + completer?(response.data) + } + semaphore.wait() + }) + operationQueue.addOperation(self.tokenOperation!) + } + + +} + + +extension MoyaProvider { + + @discardableResult + func requestCustomJson(_ target: Target, callbackQueue: DispatchQueue? = nil, completion: Completion?) -> Cancellable { + return request(target, callbackQueue: callbackQueue) { (result) in + guard let completion = completion else {return} + completion(result) + } + } +} + +let CustomApiTimeoutClosure = {(endpoint: Endpoint, closure: MoyaProvider.RequestResultClosure) -> Void in + + if var urlRequest = try? endpoint.urlRequest() { + ///总是获取新数据 + urlRequest.cachePolicy = .reloadIgnoringCacheData + urlRequest.timeoutInterval = 30 + closure(.success(urlRequest)) + } else { + closure(.failure(MoyaError.requestMapping(endpoint.url))) + } + + #if DEBUG /// + //print(try? endpoint.urlRequest() ) + #endif +} diff --git a/BeeReel/Base/Network/Base/BRNetworkTarget.swift b/BeeReel/Base/Network/Base/BRNetworkTarget.swift new file mode 100644 index 0000000..fe48473 --- /dev/null +++ b/BeeReel/Base/Network/Base/BRNetworkTarget.swift @@ -0,0 +1,105 @@ +// +// BRNetworkTarget.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + + +import Moya +import SmartCodable + +struct BRNetworkData { + var parameters: BRNetworkParameters? + var completion: ((_ response: BRNetworkResponse) -> Void)? +} + +struct BRNetworkParameters { + var baseURL: URL? + var parameters: [String : Any]? + var method: Moya.Method = .post + var path: String + var isLoding: Bool = false + var isToast: Bool = true +} + +struct BRNetworkResponse: SmartCodable { + var code: Int? + var data: T? + var msg: String? + + ///原始数据 + @IgnoredKey + var rawData: Any? +} + +enum BRNetworkTarget { + case request(parameters: BRNetworkParameters) +} + +extension BRNetworkTarget: TargetType { + var baseURL: URL { + return .init(string: BRBaseURL)! + } + + var path: String { + switch self { + case .request(let parameters): + return BRURLPathPrefix + parameters.path + } + } + + var method: Moya.Method { + switch self { + case .request(let parameters): + return parameters.method + } + } + + var task: Moya.Task { + switch self { + case .request(let parameters): + let parameters = parameters.parameters ?? [:] + return .requestParameters(parameters: parameters, encoding: getEncoding()) + } + } + + var headers: [String : String]? { + let userToken = BRLoginManager.manager.token?.token ?? "" + let dic: [String : String] = [ + "system-version" : kBROsVersion, + "lang-key" : BRLocalizedManager.manager.currentLocalizedKey,//当前语言 + "time-zone" : self.timeZone(), //时区 + "app-version" : kBRAPPVersion, + "device-id" : JXUUID.uuid(), //设备id + "brand" : "apple", //品牌 + "app-name" : kBRAPPBundleIdentifier, + "system-type" : "ios", + "idfa" : JXUUID.idfa(), + "model" : UIDevice.br_machineModelName(), + "authorization" : userToken + ] + return dic + } + +} + +extension BRNetworkTarget { + var sampleData: Data { return "".data(using: String.Encoding.utf8)! } + + func getEncoding() -> ParameterEncoding { + switch self.method { + case .get, .delete: + return URLEncoding.default + default: + return JSONEncoding.default + } + } + + func timeZone() -> String { + let timeZone = NSTimeZone.local as NSTimeZone + let timeZoneSecondsFromGMT = timeZone.secondsFromGMT / 3600 + return String(format: "GMT+0%d:00", timeZoneSecondsFromGMT) + } + +} diff --git a/BeeReel/Base/Network/Base/BRURLPath.swift b/BeeReel/Base/Network/Base/BRURLPath.swift new file mode 100644 index 0000000..5e3b49a --- /dev/null +++ b/BeeReel/Base/Network/Base/BRURLPath.swift @@ -0,0 +1,39 @@ +// +// BRURLPath.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + + + +let BRBaseURL = "https://api-qjwl168.qjwl168.com" +let BRURLPathPrefix = "/velo" + +let BRWebBaseURL = "https://www.qjwl168.com" +let BRCampaignWebURL = "https://campaign.qjwl168.com" + + +///用户协议 +let kSBUserAgreementWebUrl = BRWebBaseURL + "/user_policy" +///隐私协议 +let kSBPrivacyPolicyWebUrl = BRWebBaseURL + "/private" +///儿童个人信息保护规则 +let kSBInformationProtectionWebUrl = BRWebBaseURL + "/information_protection" +///第三方共享清单 +let kSBInformationSharingWebUrl = BRWebBaseURL + "/information_sharing" +///收集个人信息明示清单 +let kSBPersoInforDisclosureWebUrl = BRWebBaseURL + "/persoInfor_disclosure" +///全国青少年互联网文明公约 +let kSBCivizatioConventionWebUrl = BRWebBaseURL + "/civizatio_convention" +///会员服务协议 +let kSBMemberShipAgreement = BRWebBaseURL + "/member_ship_agreement" + +///反馈首页 +let kSBFeedBackHomeWebUrl = BRCampaignWebURL + "/pages/leave/index" +///反馈列表 +let kSBFeedBackListWebUrl = BRCampaignWebURL + "/pages/leave/list" +///反馈详情 +let kSBFeedBackDetailWebUrl = BRCampaignWebURL + "/pages/leave/detail" +///活动页面 +let kSBRewardsWebUrl = BRCampaignWebURL + "/pages/reward/theme4" diff --git a/BeeReel/Base/View/BRCollectionView.swift b/BeeReel/Base/View/BRCollectionView.swift new file mode 100644 index 0000000..0e5ed56 --- /dev/null +++ b/BeeReel/Base/View/BRCollectionView.swift @@ -0,0 +1,22 @@ +// +// BRCollectionView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRCollectionView: UICollectionView { + + override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { + super.init(frame: frame, collectionViewLayout: layout) + self.backgroundColor = .clear + self.contentInsetAdjustmentBehavior = .never + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/BeeReel/Base/View/BRCollectionViewCell.swift b/BeeReel/Base/View/BRCollectionViewCell.swift new file mode 100644 index 0000000..902056f --- /dev/null +++ b/BeeReel/Base/View/BRCollectionViewCell.swift @@ -0,0 +1,21 @@ +// +// BRCollectionViewCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRCollectionViewCell: UICollectionViewCell { + + override init(frame: CGRect) { + super.init(frame: frame) + self.layer.rasterizationScale = UIScreen.main.scale + self.layer.shouldRasterize = true + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } +} diff --git a/BeeReel/Base/View/BRImageView.swift b/BeeReel/Base/View/BRImageView.swift new file mode 100644 index 0000000..b6fb3d4 --- /dev/null +++ b/BeeReel/Base/View/BRImageView.swift @@ -0,0 +1,77 @@ +// +// BRImageView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit + +class BRImageView: UIImageView { + + var placeholderColor = UIColor.black + + private lazy var placeholderImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "placeholder_image_01")) + imageView.isHidden = true + imageView.contentMode = .scaleAspectFit + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + override init(image: UIImage?) { + super.init(image: image) + _init() + } + + override init(image: UIImage?, highlightedImage: UIImage?) { + super.init(image: image, highlightedImage: highlightedImage) + _init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + func _init() { + self.contentMode = .scaleAspectFill + self.layer.masksToBounds = true + if image == nil { + self.backgroundColor = self.placeholderColor + placeholderImageView.isHidden = false + } + + addSubview(placeholderImageView) + } + + override var image: UIImage? { + didSet { + if self.backgroundColor == nil && image == nil { + self.backgroundColor = self.placeholderColor + } else if image != nil { + if self.backgroundColor == self.placeholderColor { + self.backgroundColor = nil + } + } + + if image == nil { + placeholderImageView.isHidden = false + } else { + placeholderImageView.isHidden = true + } + } + } + + + override func layoutSubviews() { + super.layoutSubviews() + + placeholderImageView.frame = .init(x: 0, y: 0, width: self.bounds.width * (2 / 3), height: self.bounds.height * (2 / 3)) + placeholderImageView.center = .init(x: self.bounds.width / 2, y: self.bounds.height / 2) + } +} diff --git a/BeeReel/Base/View/BRTableView.swift b/BeeReel/Base/View/BRTableView.swift new file mode 100644 index 0000000..0172b95 --- /dev/null +++ b/BeeReel/Base/View/BRTableView.swift @@ -0,0 +1,49 @@ +// +// BRTableView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit + +class BRTableView: UITableView { + + var insetGroupedMargins: CGFloat = 15 + + override init(frame: CGRect, style: UITableView.Style) { + super.init(frame: frame, style: style) + separatorColor = .colorFFFFFF(alpha: 0.1) + separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) + self.backgroundColor = .clear + self.contentInsetAdjustmentBehavior = .never + + if style == .insetGrouped { + sectionFooterHeight = 12 + sectionHeaderHeight = 0.1 + } else if style == .plain { + if #available(iOS 15.0, *) { + sectionHeaderTopPadding = 0 + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + ///修改 insetGrouped 的边距 参考https://github.com/QMUI/QMUIDemo_iOS/blob/master/QMUI/QMUIKit/UIKitExtensions/UITableView%2BQMUI.m + override var layoutMargins: UIEdgeInsets { + set { + super.layoutMargins = newValue + } + get { + var margins = super.layoutMargins + if self.style == .insetGrouped { + margins.left = self.safeAreaInsets.left + insetGroupedMargins + margins.right = self.safeAreaInsets.right + insetGroupedMargins + } + return margins + } + } +} diff --git a/BeeReel/Base/View/BRTableViewCell.swift b/BeeReel/Base/View/BRTableViewCell.swift new file mode 100644 index 0000000..f4dc34f --- /dev/null +++ b/BeeReel/Base/View/BRTableViewCell.swift @@ -0,0 +1,49 @@ +// +// BRTableViewCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit + +class BRTableViewCell: UITableViewCell { + + private(set) lazy var br_indicatorImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "arrow_right_icon_02")) + return imageView + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + self.layer.rasterizationScale = UIScreen.main.scale + self.layer.shouldRasterize = true + self.selectionStyle = .none + self.backgroundColor = .clear + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + +} + + +extension UITableViewCell { + + var br_tableView: UITableView? { + return self.value(forKey: "_tableView") as? UITableView + } +} diff --git a/BeeReel/Base/View/TabBar/BRTabBar.swift b/BeeReel/Base/View/TabBar/BRTabBar.swift new file mode 100644 index 0000000..9e4b461 --- /dev/null +++ b/BeeReel/Base/View/TabBar/BRTabBar.swift @@ -0,0 +1,224 @@ +// +// BRTabBar.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRTabBar: UITabBar { + + var containers = [BRTabBarItemContainer]() + private let tagOffset = 1000 + + override func sizeThatFits(_ size: CGSize) -> CGSize { + var size = super.sizeThatFits(size) + size.height = UIScreen.customTabBarHeight + return size + } + + private lazy var topImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "tabbar_top_icon")) + return imageView + }() + + override var items: [UITabBarItem]? { + didSet { + reload() + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.backgroundColor = .color1C1C1C() + self.setRoundedCorner(topLeft: 30, topRight: 30, bottomLeft: 0, bottomRight: 0) + + addSubview(topImageView) + + topImageView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.centerX.equalToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() +// updateLayout() + + } + + override func setItems(_ items: [UITabBarItem]?, animated: Bool) { + super.setItems(items, animated: animated) + self.reload() + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + var b = super.point(inside: point, with: event) + if !b { + for container in containers { + let p = CGPoint.init(x: point.x - container.frame.origin.x, y: point.y - container.frame.origin.y) + if container.point(inside: p, with: event) { + b = true + } + } + } + return b + } +} + +extension BRTabBar { + + func updateLayout() { + subviews.forEach { + if let cls = NSClassFromString("UITabBarButton") { + if $0.isKind(of: cls) == true { + $0.isHidden = true + } + } + } + + var index = 0 + let width = self.width / CGFloat(containers.count) + let height = self.height + var x = 0.0 + containers.forEach { + if $0.contentView == (selectedItem as? BRTabBarItem)?.contentView { + index = $0.tag - tagOffset + } + + $0.frame = .init(x: x, y: 0, width: width, height: height) + x += width + + } + updateSelectedStatus(index: index, animated: false) + + } + + + ///更新选中状态 + private func updateSelectedStatus(index: Int, animated: Bool) { + self.bringSubviewToFront(self.topImageView) + + var selectedView: UIView? + containers.forEach { + if $0.tag == index + tagOffset { + selectedView = $0 + } + } + + if let view = selectedView { + self.topImageView.snp.remakeConstraints { make in + make.top.equalToSuperview() + make.centerX.equalTo(view) + } + } else { + self.topImageView.snp.removeConstraints() + } + + if animated { + UIView.animate(withDuration: 0.3) { + self.layoutIfNeeded() + } + } + + } + + +} + +extension BRTabBar { + + func removeAll() { + for container in containers { + container.removeFromSuperview() + } + containers.removeAll() + } + + func reload() { + removeAll() + + guard let items = self.items else { return } + + + items.enumerated().forEach { (index, item) in + let container = BRTabBarItemContainer(self, tag: index + tagOffset) + addSubview(container) + + if let item = item as? BRTabBarItem { + container.contentView = item.contentView + } + containers.append(container) + } + updateLayout() + + self.setNeedsLayout() + + } + + @objc func highlightAction(_ sender: AnyObject?) { + guard let container = sender as? BRTabBarItemContainer else { + return + } + let newIndex = max(0, container.tag - tagOffset) + guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else { + return + } + + if let item = item as? BRTabBarItem { + item.contentView.highlight(animated: true, completion: nil) + } + } + + @objc func dehighlightAction(_ sender: AnyObject?) { + guard let container = sender as? BRTabBarItemContainer else { + return + } + let newIndex = max(0, container.tag - tagOffset) + guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else { + return + } + + if let item = item as? BRTabBarItem { + item.contentView.dehighlight(animated: true, completion: nil) + } + } + + @objc func selectAction(_ sender: AnyObject?) { + guard let container = sender as? BRTabBarItemContainer else { + return + } + select(itemAtIndex: container.tag - tagOffset, animated: true) + } + + @objc func select(itemAtIndex idx: Int, animated: Bool) { + let newIndex = max(0, idx) + let currentIndex = (selectedItem != nil) ? (items?.firstIndex(of: selectedItem!) ?? -1) : -1 + guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else { + return + } + + if currentIndex != newIndex { + if currentIndex != -1 && currentIndex < items?.count ?? 0{ + if let currentItem = items?[currentIndex] as? BRTabBarItem { + currentItem.contentView.deselect(animated: animated, completion: nil) + } + } + if let item = item as? BRTabBarItem { + item.contentView.select(animated: animated, completion: nil) + } + } else if currentIndex == newIndex { + if let item = item as? BRTabBarItem { + item.contentView.reselect(animated: animated, completion: nil) + } + } + updateSelectedStatus(index: newIndex, animated: animated) + + delegate?.tabBar?(self, didSelect: item) + } +} diff --git a/BeeReel/Base/View/TabBar/BRTabBarItem.swift b/BeeReel/Base/View/TabBar/BRTabBarItem.swift new file mode 100644 index 0000000..e3ef996 --- /dev/null +++ b/BeeReel/Base/View/TabBar/BRTabBarItem.swift @@ -0,0 +1,45 @@ +// +// BRTabBarItem.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/25. +// + +import UIKit + +class BRTabBarItem: UITabBarItem { + + private(set) var contentView: BRTabBarItemContentView + + + override var title: String? { + didSet { + contentView.title = title + } + } + override var image: UIImage? { + didSet { + contentView.image = image + } + } + + override var selectedImage: UIImage? { + didSet { + contentView.selectedImage = selectedImage + } + } + + public init(_ contentView: BRTabBarItemContentView = BRTabBarItemContentView(), title: String? = nil, image: UIImage? = nil, selectedImage: UIImage? = nil, tag: Int = 0) { + self.contentView = contentView + super.init() + + self.title = title + self.image = image + self.selectedImage = selectedImage + self.tag = tag + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/BeeReel/Base/View/TabBar/BRTabBarItemContainer.swift b/BeeReel/Base/View/TabBar/BRTabBarItemContainer.swift new file mode 100644 index 0000000..2b12493 --- /dev/null +++ b/BeeReel/Base/View/TabBar/BRTabBarItemContainer.swift @@ -0,0 +1,56 @@ +// +// BRTabBarItemContainer.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/25. +// + +import UIKit + +class BRTabBarItemContainer: UIControl { + + var contentView: BRTabBarItemContentView? { + didSet { + oldValue?.removeFromSuperview() + + if let contentView = contentView { + addSubview(contentView) + } + } + } + + internal init(_ target: AnyObject?, tag: Int) { + super.init(frame: CGRect.zero) + self.tag = tag + self.addTarget(target, action: #selector(BRTabBar.selectAction(_:)), for: .touchUpInside) + self.addTarget(target, action: #selector(BRTabBar.highlightAction(_:)), for: .touchDown) + self.addTarget(target, action: #selector(BRTabBar.highlightAction(_:)), for: .touchDragEnter) + self.addTarget(target, action: #selector(BRTabBar.dehighlightAction(_:)), for: .touchDragExit) + self.backgroundColor = .clear +// self.isAccessibilityElement = true + } + + internal required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + internal override func layoutSubviews() { + super.layoutSubviews() + if let contentView = contentView { + contentView.frame = self.bounds +// contentView.updateLayout() + } + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + var b = super.point(inside: point, with: event) + if !b { + for subview in self.subviews { + if subview.point(inside: CGPoint.init(x: point.x - subview.frame.origin.x, y: point.y - subview.frame.origin.y), with: event) { + b = true + } + } + } + return b + } +} diff --git a/BeeReel/Base/View/TabBar/BRTabBarItemContentView.swift b/BeeReel/Base/View/TabBar/BRTabBarItemContentView.swift new file mode 100644 index 0000000..5f97343 --- /dev/null +++ b/BeeReel/Base/View/TabBar/BRTabBarItemContentView.swift @@ -0,0 +1,161 @@ +// +// BRTabBarItemContentView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/25. +// + +import UIKit + +class BRTabBarItemContentView: UIView { + + /// 是否被选中 + open var selected = false + + /// 是否处于高亮状态 + open var highlighted = false + + /// 是否支持高亮 + open var highlightEnabled = true + + var title: String? { + didSet { +// selectedView.title = title + } + } + var image: UIImage? { + didSet { +// normalView.image = image + updateLayout() + } + } + + var selectedImage: UIImage? { + didSet { +// selectedView.image = selectedImage + updateLayout() + } + } + + private lazy var imageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.isUserInteractionEnabled = false + self.layer.masksToBounds = true + + addSubview(imageView) + + + imageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 2)) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + open func updateDisplay() { + + } + + func updateLayout() { + imageView.image = selected ? selectedImage : image + + } +} + +extension BRTabBarItemContentView { + // MARK: - INTERNAL METHODS + internal final func select(animated: Bool, completion: (() -> ())?) { + selected = true + if highlightEnabled && highlighted { + highlighted = false + dehighlightAnimation(animated: animated, completion: { [weak self] in + self?.updateDisplay() + self?.selectAnimation(animated: animated, completion: completion) + }) + } else { + updateDisplay() + selectAnimation(animated: animated, completion: completion) + } + } + + internal final func deselect(animated: Bool, completion: (() -> ())?) { + selected = false + updateDisplay() + self.deselectAnimation(animated: animated, completion: completion) + } + + internal final func reselect(animated: Bool, completion: (() -> ())?) { + if selected == false { + select(animated: animated, completion: completion) + } else { + if highlightEnabled && highlighted { + highlighted = false + dehighlightAnimation(animated: animated, completion: { [weak self] in + self?.reselectAnimation(animated: animated, completion: completion) + }) + } else { + reselectAnimation(animated: animated, completion: completion) + } + } + } + + internal final func highlight(animated: Bool, completion: (() -> ())?) { + if !highlightEnabled { + return + } + if highlighted == true { + return + } + highlighted = true + self.highlightAnimation(animated: animated, completion: completion) + } + + internal final func dehighlight(animated: Bool, completion: (() -> ())?) { + if !highlightEnabled { + return + } + if !highlighted { + return + } + highlighted = false + self.dehighlightAnimation(animated: animated, completion: completion) + } + + internal func badgeChanged(animated: Bool, completion: (() -> ())?) { + self.badgeChangedAnimation(animated: animated, completion: completion) + } + + // MARK: - ANIMATION METHODS + func selectAnimation(animated: Bool, completion: (() -> ())?) { + self.imageView.image = self.selectedImage + + } + + func deselectAnimation(animated: Bool, completion: (() -> ())?) { + self.imageView.image = self.image + } + + func reselectAnimation(animated: Bool, completion: (() -> ())?) { + completion?() + } + + func highlightAnimation(animated: Bool, completion: (() -> ())?) { + completion?() + } + + func dehighlightAnimation(animated: Bool, completion: (() -> ())?) { + completion?() + } + + func badgeChangedAnimation(animated: Bool, completion: (() -> ())?) { + completion?() + } +} diff --git a/BeeReel/Class/Home/Controller/BRHomeTop10ViewController.swift b/BeeReel/Class/Home/Controller/BRHomeTop10ViewController.swift new file mode 100644 index 0000000..1548dca --- /dev/null +++ b/BeeReel/Class/Home/Controller/BRHomeTop10ViewController.swift @@ -0,0 +1,45 @@ +// +// BRHomeTop10ViewController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRHomeTop10ViewController: BRViewController { + + + private lazy var dataArr: [BRShortModel] = [] + + +// private lazy var collectionView: BRCollectionView = { +// let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: <#T##UICollectionViewLayout#>) +// }() + + override func viewDidLoad() { + super.viewDidLoad() + + requestDataArr() + } + + + + + +} + +extension BRHomeTop10ViewController { + + private func requestDataArr() { + + BRHomeAPI.requestTop10List { [weak self] list in + guard let self = self else { return } + guard let list = list else { return } + self.dataArr = list + } + + } + + +} diff --git a/BeeReel/Class/Home/Controller/BRHomeViewController.swift b/BeeReel/Class/Home/Controller/BRHomeViewController.swift new file mode 100644 index 0000000..0aa13c5 --- /dev/null +++ b/BeeReel/Class/Home/Controller/BRHomeViewController.swift @@ -0,0 +1,239 @@ +// +// BRHomeViewController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/25. +// + +import UIKit + +class BRHomeViewController: BRViewController { + + + private lazy var viewModel = BRHomeViewModel() + + + private lazy var spotlightVC: BRSpotlightViewViewController = { + let vc = BRSpotlightViewViewController() + vc.viewModel = self.viewModel + return vc + }() + + private lazy var vcArr: [BRViewController] = { + return [ + spotlightVC, + BRHomeTop10ViewController(), + BRViewController(), + BRViewController(), + BRViewController() + ] + }() + + private lazy var pageParam: WMZPageParam = { + let param = WMZPageParam() + param.wTitleArr = [ + "Spotlight".localized, + "Top 10".localized, + "Popular Picks".localized, + "New Releases".localized, + "Categories".localized + ] + param.wViewController = { [weak self] index in + return self?.vcArr[index] + } + param.wMenuHeadView = { [weak self] in + if let bannerArr = self?.viewModel.bannerArr, bannerArr.count > 0 { + return self?.headerView + } else { + return nil + } + } + //顶部可下拉 + param.wBounces = true + param.wMenuTitleColor = .colorFFFFFF() + param.wMenuTitleSelectColor = .colorFFFFFF() + param.wMenuTitleUIFont = .fontMedium(ofSize: 12) + param.wMenuTitleSelectUIFont = .fontMedium(ofSize: 12) + param.wMenuTitleRadios = 17 + param.wMenuHeight = 34 + param.wMenuTitleOffset = 10 + param.wMenuInsets = UIEdgeInsets(top: 40, left: 59, bottom: 0, right: 15) + param.wMenuBgColor = .clear + param.wBgColor = .clear + + + param.wTopSuspension = true + param.wCustomDataViewTopOffset = UIScreen.statusBarHeight + + param.wCustomMenuTitle = { [weak self] buttons in + buttons?.forEach({ button in + self?.setMenuTitle(button: button) + }) + } + + param.wCustomNaviBarY = { _ in + return 0 + } + param.wCustomTabbarY = { _ in + return UIScreen.customTabBarHeight + } + + + param.wEventChildVCDidSroll = { [weak self] (pageVC, oldPoint, newPonit, currentScrollView) in + guard let self = self else { return } + + let top = max(0, newPonit.y) + self.bgImageView.snp.updateConstraints { make in + make.top.equalToSuperview().offset(-top) + } + self.updateStatusBarStyle() + } + + return param + }() + + private lazy var pageView: WMZPageView = { + let view = WMZPageView(frame: self.view.bounds, autoFix: true, param: pageParam, parentReponder: self) + view.backgroundColor = .clear + view.downSc?.backgroundColor = .clear + return view + }() + + private lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "home_header_bg_image")) + imageView.isHidden = true + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 17) + label.textColor = .color1C1C1C() + label.text = "Browse Genres".localized + return label + }() + + private lazy var headerView: BRHomeHeaderView = { + let view = BRHomeHeaderView() + view.frame = .init(x: 0, y: 0, width: UIScreen.width, height: view.contentHeight) + return view + }() + + private lazy var menuLeftView: UIView = { + let view = UIView() + view.backgroundColor = self.view.backgroundColor + return view + }() + + private lazy var searchButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "search_button_01"), for: .normal) + return button + }() + + override func viewDidLoad() { + super.viewDidLoad() + self.edgesForExtendedLayout = [.top, .bottom] + self.statusBarStyle = .lightContent + self.navigationController?.isNavigationBarHidden = true + + br_setupUI() + + requestHomeData() + + setupPageView() + + updateStatusBarStyle() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: true) + } + + + private func updateStatusBarStyle() { + let point = self.pageView.downSc?.contentOffset ?? .zero + + if point.y < 300, let bannerArr = self.viewModel.bannerArr, bannerArr.count > 0 { + self.statusBarStyle = .lightContent + } else { + self.statusBarStyle = .darkContent + } + } + + +} + +extension BRHomeViewController { + + private func br_setupUI() { + view.addSubview(bgImageView) + view.addSubview(pageView) + menuLeftView.addSubview(searchButton) + + bgImageView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(0) + make.left.right.equalToSuperview() + make.height.equalTo(UIScreen.statusBarHeight + 300) + } + + searchButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.centerY.equalToSuperview() + } + } + + ///自定义菜单样式 + private func setMenuTitle(button: UIButton) { + button.setBackgroundImage(UIImage(named: "menu_bg_image_01"), for: .selected) + button.setBackgroundImage(UIImage(color: .colorD3D3D3()), for: .normal) + button.layer.masksToBounds = true + } + + private func setupPageView() { + + self.pageView.upSc.addSubview(titleLabel) + self.pageView.upSc.addSubview(menuLeftView) + +// self.pageView.upSc.sendSubviewToBack(menuBgView) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview().offset(10) + } + + menuLeftView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalToSuperview().offset(pageParam.wMenuInsets.top) + make.height.equalTo(34) + make.width.equalTo(59) + } + } + +} + +extension BRHomeViewController { + + private func requestHomeData() { + + BRHomeAPI.requestHomeData { [weak self] list in + guard let self = self else { return } + self.viewModel.homeOldDataArr = list ?? [] + + self.headerView.bannerArr = self.viewModel.bannerArr + if let bannerArr = self.viewModel.bannerArr, bannerArr.count > 0 { + self.bgImageView.isHidden = false + self.pageView.updateHeadView() + } else { + self.bgImageView.isHidden = true + } + self.spotlightVC.reloadData() + self.updateStatusBarStyle() + } + + } + +} + + diff --git a/BeeReel/Class/Home/Controller/BRSpotlightViewViewController.swift b/BeeReel/Class/Home/Controller/BRSpotlightViewViewController.swift new file mode 100644 index 0000000..4ef9fa4 --- /dev/null +++ b/BeeReel/Class/Home/Controller/BRSpotlightViewViewController.swift @@ -0,0 +1,71 @@ +// +// BRSpotlightViewViewController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit + +class BRSpotlightViewViewController: BRViewController, WMZPageProtocol { + + var viewModel: BRHomeViewModel? + + private lazy var tableView: BRTableView = { + let tableView = BRTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.register(BRSpotlightMainBaseCell.self, forCellReuseIdentifier: "cell") + tableView.register(BRSpotlightHotMainCell.self, forCellReuseIdentifier: BRHomeModuleItem.ModuleKey.v3_recommand.rawValue) + tableView.register(BRSpotlightTopMainCell.self, forCellReuseIdentifier: BRHomeModuleItem.ModuleKey.week_ranking.rawValue) + tableView.register(BRSpotlightNewMainCell.self, forCellReuseIdentifier: BRHomeModuleItem.ModuleKey.new_recommand.rawValue) + tableView.register(BRSpotlightRecommandMainCell.self, forCellReuseIdentifier: BRHomeModuleItem.ModuleKey.cagetory_recommand.rawValue) + return tableView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + br_setupUI() + } + + + func reloadData() { + tableView.reloadData() + } + + func getMyScrollView() -> UIScrollView { + return tableView + } +} + +extension BRSpotlightViewViewController { + + private func br_setupUI() { + view.addSubview(tableView) + + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(15) + } + } + + +} + +//MARK: -------------- UITableViewDelegate UITableViewDataSource -------------- +extension BRSpotlightViewViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let moduleItem = self.viewModel?.spotlightDataArr[indexPath.row] + let cell = tableView.dequeueReusableCell(withIdentifier: moduleItem?.module_key?.rawValue ?? "cell", for: indexPath) as! BRSpotlightMainBaseCell + cell.moduleItem = moduleItem + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.viewModel?.spotlightDataArr.count ?? 0 + } + + +} diff --git a/BeeReel/Class/Home/Model/BRHomeModuleItem.swift b/BeeReel/Class/Home/Model/BRHomeModuleItem.swift new file mode 100644 index 0000000..6fa8010 --- /dev/null +++ b/BeeReel/Class/Home/Model/BRHomeModuleItem.swift @@ -0,0 +1,67 @@ +// +// BRHomeModuleItem.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/26. +// + +import UIKit +import SmartCodable + +class BRHomeModuleItem: BRModel, SmartCodable { + + enum ModuleKey: String, SmartCaseDefaultable { + case banner = "home_banner" + case v3_recommand = "home_v3_recommand" + ///分类推荐 + case cagetory_recommand = "home_cagetory_recommand" + case week_ranking = "week_ranking" + ///跑马灯 + case marquee = "marquee" + + case new_recommand = "new_recommand" + } + + + var module_key: ModuleKey? + var title: String? + var list: [BRShortModel]? +// var categoryList: [VPCategoryModel]? + + @SmartAny + var data: Any? + + @IgnoredKey + var iconImage: UIImage? + + @IgnoredKey + var br_cellHeight: CGFloat? + + + + func didFinishMapping() { + if let data = data as? [[String : Any]] { +// if module_key == .category_navigation { +// self.categoryList = [VPCategoryModel].deserialize(from: data) +// } else { +// } + self.list = [BRShortModel].deserialize(from: data) + } else if let data = data as? [String : Any] { + var dataList: [[String : Any]]? + if let list = data["list"] as? [[String : Any]] { + self.title = data["title"] as? String + dataList = list + + } else if let list = data["shortPlayList"] as? [[String : Any]] { + self.title = data["category_name"] as? String + dataList = list + } + + if let dataList = dataList { + self.list = [BRShortModel].deserialize(from: dataList) + } + + } + } + +} diff --git a/BeeReel/Class/Home/Model/BRPagerViewTransformer.swift b/BeeReel/Class/Home/Model/BRPagerViewTransformer.swift new file mode 100644 index 0000000..2c111fa --- /dev/null +++ b/BeeReel/Class/Home/Model/BRPagerViewTransformer.swift @@ -0,0 +1,61 @@ +// +// BRPagerViewTransformer.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit +import FSPagerView + +class BRPagerViewTransformer: FSPagerViewTransformer { + + + override func applyTransform(to attributes: FSPagerViewLayoutAttributes) { + guard let pagerView = self.pagerView else { + return + } + + let position = attributes.position + let scrollDirection = pagerView.scrollDirection + let itemSpacing = (scrollDirection == .horizontal ? attributes.bounds.width : attributes.bounds.height) + self.proposedInteritemSpacing() + + if self.type == .ferrisWheel { + guard scrollDirection == .horizontal else { + // This type doesn't support vertical mode + return + } + // http://ronnqvi.st/translate-rotate-translate/ + var zIndex = 0 + var transform = CGAffineTransform.identity + switch position { + case -5 ... 5: + let itemSpacing = attributes.bounds.width+self.proposedInteritemSpacing() + let count: CGFloat = 40 + let circle: CGFloat = .pi * 2.0 + let radius = itemSpacing * count / circle + let ty = radius * (self.type == .ferrisWheel ? 1 : -1) + let theta = circle / count + let rotation = position * theta * (self.type == .ferrisWheel ? 1 : -1) + transform = transform.translatedBy(x: -position*itemSpacing, y: ty) + transform = transform.rotated(by: rotation) + transform = transform.translatedBy(x: 0, y: -ty) + zIndex = Int((4.0-abs(position)*10)) + default: + break + } +// attributes.alpha = abs(position) < 0.5 ? 1 : self.minimumAlpha + + attributes.transform = transform + attributes.zIndex = zIndex + + + } else { + super.applyTransform(to: attributes) + } + } + + override func proposedInteritemSpacing() -> CGFloat { + return pagerView?.interitemSpacing ?? 0 + } +} diff --git a/BeeReel/Class/Home/Model/BRShortModel.swift b/BeeReel/Class/Home/Model/BRShortModel.swift new file mode 100644 index 0000000..57a3f5f --- /dev/null +++ b/BeeReel/Class/Home/Model/BRShortModel.swift @@ -0,0 +1,82 @@ +// +// BRShortModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/26. +// + +import UIKit +import SmartCodable + +class BRShortModel: BRModel, SmartCodable { + + enum VideoRevolution: String, SmartCaseDefaultable { + case r_540 = "540" + case r_720 = "720" + case r_1080 = "1080" + + var needLogin: Bool { + if self == .r_720 { + return true + } else { + return false + } + } + + var needVip: Bool { + if self == .r_1080 { + return true + } else { + return false + } + } + + var toString: String { + return "\(self.rawValue)P" + } + } + + enum TagType: String, SmartCaseDefaultable { + case hot = "hot" + case new = "new" + } + + var id: String? + var all_coins: String? + var buy_type: String? + var collect_total: Int? + var vp_description: String? + var episode_total: Int? + var horizontally_img: String? + var image_url: String? + var is_collect: Bool? + var name: String? + var process: String? + var search_click_total: String? + var short_id: String? + var short_play_id: String? + var short_play_video_id: String? + var tag_type: TagType? + var video_info: BRVideoInfoModel? + var category: [String]? + ///观看数 + var watch_total: Int? + var current_episode: String? + var video_url: String? + var updated_at: String? + + var revolution: VideoRevolution? + + @IgnoredKey + var titleAttributedString: NSAttributedString? + @IgnoredKey + var vp_isSelected: Bool? + + + static func mappingForKey() -> [SmartKeyTransformer]? { + return [ + CodingKeys.vp_description <--- ["description", "short_video_description"], + CodingKeys.name <--- ["short_video_title", "name"] + ] + } +} diff --git a/BeeReel/Class/Home/Model/BRVideoInfoModel.swift b/BeeReel/Class/Home/Model/BRVideoInfoModel.swift new file mode 100644 index 0000000..9667fdd --- /dev/null +++ b/BeeReel/Class/Home/Model/BRVideoInfoModel.swift @@ -0,0 +1,29 @@ +// +// BRVideoInfoModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/26. +// + +import UIKit +import SmartCodable + +class BRVideoInfoModel: BRModel, SmartCodable { + + var coins: Int? + var vip_coins: Int? + var episode: String? + var id: String? + var image_url: String? + ///1:会员,2: 非会员 + var is_vip: Int? + ///是否锁定,购买后解锁 + var is_lock: Bool? + var promise_view_ad: Int? + var short_id: String? + var short_play_id: String? + var short_play_video_id: String? + var video_url: String? + ///播放进度,毫秒 + var play_seconds: Int? +} diff --git a/BeeReel/Class/Home/View/BRHomeHeaderBannerCell.swift b/BeeReel/Class/Home/View/BRHomeHeaderBannerCell.swift new file mode 100644 index 0000000..7443ef5 --- /dev/null +++ b/BeeReel/Class/Home/View/BRHomeHeaderBannerCell.swift @@ -0,0 +1,126 @@ +// +// BRHomeHeaderBannerCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit + +class BRHomeHeaderBannerCell: BRCollectionViewCell { + + var model: BRShortModel? { + didSet { + coverImageView.br_setImage(url: model?.image_url) + titleLabel.text = model?.name + categoryLabel.text = model?.category?.first + } + } + + private lazy var coverImageView: BRImageView = { + let imageView = BRImageView() + return imageView + }() + + private lazy var holeLayer: CAShapeLayer = { + let maskLayer = CAShapeLayer() + maskLayer.fillRule = .evenOdd + return maskLayer + }() + + private lazy var playImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "play_icon_01")) + return imageView + }() + + private lazy var bottomView: UIView = { + let view = UIView() + view.br_addEffectView() + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontBold(ofSize: 14) + label.textColor = .colorFFFFFF() + label.layer.shadowColor = UIColor.color000000(alpha: 0.25).cgColor + label.layer.shadowOpacity = 1 + label.layer.shadowRadius = 1 + label.layer.shadowOffset = .init(width: 0, height: 1) + return label + }() + + private lazy var categoryLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 10) + label.textColor = .colorF1FF94() + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.layer.cornerRadius = 16 + contentView.layer.masksToBounds = true + contentView.layer.mask = self.holeLayer + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + override func layoutSubviews() { + super.layoutSubviews() + + let holeCenter = CGPoint(x: 21, y: 21) + let holeRadius: CGFloat = 11 + + let holePath = UIBezierPath(arcCenter: holeCenter, radius: holeRadius, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true) + + let path = UIBezierPath(rect: self.bounds) + path.append(holePath) + path.usesEvenOddFillRule = true + + holeLayer.path = path.cgPath + } + +} + +extension BRHomeHeaderBannerCell { + + private func br_setupUI() { + contentView.addSubview(coverImageView) + contentView.addSubview(playImageView) + contentView.addSubview(bottomView) + bottomView.addSubview(titleLabel) + bottomView.addSubview(categoryLabel) + + coverImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + playImageView.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + bottomView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(50) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.top.equalToSuperview().offset(6) + make.right.lessThanOrEqualToSuperview().offset(-8) + } + + categoryLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.bottom.equalToSuperview().offset(-11) + make.right.lessThanOrEqualToSuperview().offset(-8) + } + } + +} diff --git a/BeeReel/Class/Home/View/BRHomeHeaderView.swift b/BeeReel/Class/Home/View/BRHomeHeaderView.swift new file mode 100644 index 0000000..147bf97 --- /dev/null +++ b/BeeReel/Class/Home/View/BRHomeHeaderView.swift @@ -0,0 +1,86 @@ +// +// BRHomeHeaderView.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/25. +// + +import UIKit + +class BRHomeHeaderView: UIView { + + var bannerArr: [BRShortModel]? { + didSet { + bannerParam.wData = bannerArr ?? [] + bannerView.updateUI() + } + } + + var contentHeight: CGFloat = UIScreen.statusBarHeight + 107 + 280 + + private lazy var bgIconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "home_header_icon_01")) + return imageView + }() + + private lazy var bannerParam: WMZBannerParam = { + let param = WMZBannerParam() + param.wFrame = .init(x: 0, y: UIScreen.statusBarHeight + 107, width: UIScreen.width, height: 280) + param.wItemSize = .init(width: 180, height: 230) + param.wMyCell = { [weak self] (indexPath, collectionView, model, bgImageView, dataArr) in + let cell = collectionView?.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath ?? .init(row: 0, section: 0)) as? BRHomeHeaderBannerCell + cell?.model = model as? BRShortModel + return cell + } + param.wEventClick = { [weak self] (anyId, index) in + guard let self = self else { return } + guard let model = anyId as? BRShortModel else { return } + + let vc = BRVideoDetailViewController() + vc.shortPlayId = model.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + param.wSectionInset = .init(top: 0, left: 27, bottom: 0, right: 27) + param.wCardOverLapCount = 4 + param.wLineSpacing = 40 + param.wCardOverLap = true + param.wEffect = false + param.wAutoScroll = false + ///开启无限循环 + param.wRepeat = true + param.wHideBannerControl = true + param.wCanFingerSliding = false + + return param + }() + + private lazy var bannerView: WMZBannerView = { + let view = WMZBannerView(configureWithModel: bannerParam) + view.register(BRHomeHeaderBannerCell.self, forCellWithReuseIdentifier: "cell") + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + br_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BRHomeHeaderView { + + private func br_setupUI() { + addSubview(bannerView) + addSubview(bgIconImageView) + + bgIconImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(0) + make.top.equalToSuperview().offset(UIScreen.statusBarHeight) + } + } + +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightHotCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightHotCell.swift new file mode 100644 index 0000000..a01bf68 --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightHotCell.swift @@ -0,0 +1,163 @@ +// +// BRSpotlightHotCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRSpotlightHotCell: BRCollectionViewCell { + + var model: BRShortModel? { + didSet { + coverImageView.br_setImage(url: model?.image_url) + titleLabel.text = model?.name + categoryLabel.text = model?.category?.first + + favoriteButton.isSelected = self.model?.is_collect ?? false + + hotView.setNeedsUpdateConfiguration() + } + } + + + private lazy var coverImageView: BRImageView = { + let imageView = BRImageView() + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 12) + label.textColor = .color1C1C1C() + return label + }() + + private lazy var playIconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "play_icon_02")) + return imageView + }() + + private lazy var categoryLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 10) + label.textColor = .color899D00() + return label + }() + + private lazy var hotView: UIButton = { + var config = UIButton.Configuration.plain() + config.image = UIImage(named: "hot_icon_02") + config.imagePlacement = .leading + config.imagePadding = 2 + config.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0) + + let button = UIButton(configuration: config) + button.isUserInteractionEnabled = false + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + let count = model?.watch_total ?? 0 + var string = "\(count)" + if count > 100 { + string = String(format: "%.1fk", Float(count) / 1000) + } + button.configuration?.attributedTitle = AttributedString.br_createAttributedString(string: string, color: .colorFF7489(), font: .fontRegular(ofSize: 10)) + + } + return button + }() + + private lazy var favoriteButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "favorite_icon_01"), for: .normal) + button.setImage(UIImage(named: "favorite_icon_01_selected"), for: .selected) + button.setImage(UIImage(named: "favorite_icon_01_selected"), for: [.selected, .highlighted]) + button.addTarget(self, action: #selector(handleFavoriteButton), for: .touchUpInside) + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + NotificationCenter.default.addObserver(self, selector: #selector(updateShortFavoriteStateNotification), name: BRVideoAPI.updateShortFavoriteStateNotification, object: nil) + + contentView.backgroundColor = .colorFFFFFF() + contentView.layer.cornerRadius = 10 + contentView.layer.masksToBounds = true + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + @objc private func handleFavoriteButton() { + guard let shortPlayId = self.model?.short_play_id else { return } + + let isFavorite = !(self.model?.is_collect ?? false) + let videoId = self.model?.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.model?.short_play_id else { return } + + self.model?.is_collect = state; + + favoriteButton.isSelected = self.model?.is_collect ?? false + } + +} + +extension BRSpotlightHotCell { + + private func br_setupUI() { + contentView.addSubview(coverImageView) + contentView.addSubview(favoriteButton) + contentView.addSubview(titleLabel) + contentView.addSubview(playIconImageView) + contentView.addSubview(categoryLabel) + contentView.addSubview(hotView) + + coverImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.height.equalTo(160) + } + + favoriteButton.snp.makeConstraints { make in + make.top.equalToSuperview().offset(4) + make.right.equalToSuperview().offset(-4) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(7) + make.right.lessThanOrEqualToSuperview().offset(-7) + make.top.equalTo(coverImageView.snp.bottom).offset(5) + } + + playIconImageView.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-4) + make.bottom.equalToSuperview().offset(-5) + } + + categoryLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(7) + make.top.equalTo(coverImageView.snp.bottom).offset(26) + } + + hotView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(7) + make.bottom.equalToSuperview().offset(-8) + } + + } + +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightHotMainCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightHotMainCell.swift new file mode 100644 index 0000000..169e82e --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightHotMainCell.swift @@ -0,0 +1,105 @@ +// +// BRSpotlightHotMainCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit + +class BRSpotlightHotMainCell: BRSpotlightMainBaseCell { + + + override var moduleItem: BRHomeModuleItem? { + didSet { + + UIView.performWithoutAnimation { [weak self] in + self?.collectionView.reloadData() + } + + self.collectionView.performBatchUpdates(nil) { [weak self] _ in + guard let self = self else { return } + let height = moduleItem?.br_cellHeight ?? 0 + let contentHeight = self.collectionView.contentSize.height + if height != contentHeight { + self.collectionView.snp.updateConstraints { make in + make.height.equalTo(contentHeight + 1) + } + moduleItem?.br_cellHeight = contentHeight + + self.br_tableView?.reloadData() + } + + + } + + } + } + + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let width = floor((UIScreen.width - 30 - 10) / 2) + + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: width, height: 222) + layout.minimumInteritemSpacing = 10 + layout.minimumLineSpacing = 10 + 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.isScrollEnabled = false + collectionView.register(BRSpotlightHotCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BRSpotlightHotMainCell { + private func br_setupUI() { + containerView.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.height.equalTo(1) + } + } + +} + +//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource -------------- +extension BRSpotlightHotMainCell: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRSpotlightHotCell + cell.model = self.moduleItem?.list?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.moduleItem?.list?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = self.moduleItem?.list?[indexPath.row] + + let vc = BRVideoDetailViewController() + vc.shortPlayId = model?.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightMainBaseCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightMainBaseCell.swift new file mode 100644 index 0000000..9b21ea6 --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightMainBaseCell.swift @@ -0,0 +1,77 @@ +// +// BRSpotlightMainBaseCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/27. +// + +import UIKit + +class BRSpotlightMainBaseCell: BRTableViewCell { + + var moduleItem: BRHomeModuleItem? { + didSet { + titleStackView.br_removeAllArrangedSubview() + + if let icon = moduleItem?.iconImage { + iconImageView.image = icon + titleStackView.addArrangedSubview(iconImageView) + } + titleLabel.text = moduleItem?.title + titleStackView.addArrangedSubview(titleLabel) + + + + } + } + + + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 15) + label.textColor = .color1C1C1C() + return label + }() + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + private lazy var titleStackView: UIStackView = { + let view = UIStackView() + view.axis = .horizontal + view.spacing = 4 + return view + }() + + private(set) lazy var containerView: UIView = { + let view = UIView() + return view + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(titleStackView) + contentView.addSubview(containerView) + + titleStackView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview() + make.height.equalTo(20) + } + + containerView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalTo(titleStackView.snp.bottom).offset(10) + make.bottom.equalToSuperview().offset(-30) + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightNewCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightNewCell.swift new file mode 100644 index 0000000..023edac --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightNewCell.swift @@ -0,0 +1,107 @@ +// +// BRSpotlightNewCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRSpotlightNewCell: BRCollectionViewCell { + + var model: BRShortModel? { + didSet { + coverImageView.br_setImage(url: model?.image_url) + titleLabel.text = model?.name + categoryLabel.text = model?.category?.first + } + } + + private lazy var coverImageView: BRImageView = { + let imageView = BRImageView() + return imageView + }() + + private lazy var bottomView: UIView = { + let view = UIView() + view.br_addEffectView(style: .light) + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontBold(ofSize: 12) + label.textColor = .colorFFFFFF() + label.layer.shadowColor = UIColor.color000000(alpha: 0.25).cgColor + label.layer.shadowOpacity = 1 + label.layer.shadowRadius = 1 + label.layer.shadowOffset = .init(width: 0, height: 1) + return label + }() + + private lazy var categoryLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 10) + label.textColor = .colorF1FF94() + return label + }() + + private lazy var playImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "play_icon_04")) + imageView.setContentHuggingPriority(.required, for: .horizontal) + imageView.setContentCompressionResistancePriority(.required, for: .horizontal) + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BRSpotlightNewCell { + + private func br_setupUI() { + contentView.layer.cornerRadius = 10 + contentView.layer.masksToBounds = true + + contentView.addSubview(coverImageView) + contentView.addSubview(bottomView) + bottomView.addSubview(titleLabel) + bottomView.addSubview(playImageView) + bottomView.addSubview(categoryLabel) + + coverImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + bottomView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(52) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.top.equalToSuperview().offset(5) + make.right.lessThanOrEqualToSuperview().offset(-8) + } + + playImageView.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-4) + make.bottom.equalToSuperview().offset(-5) + } + + categoryLabel.snp.makeConstraints { make in + make.centerY.equalTo(playImageView) + make.left.equalToSuperview().offset(8) + make.right.lessThanOrEqualTo(playImageView.snp.left).offset(-5) + } + } + +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightNewMainCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightNewMainCell.swift new file mode 100644 index 0000000..7f66f12 --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightNewMainCell.swift @@ -0,0 +1,96 @@ +// +// BRSpotlightNewMainCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRSpotlightNewMainCell: BRSpotlightMainBaseCell { + + override var moduleItem: BRHomeModuleItem? { + didSet { + + UIView.performWithoutAnimation { [weak self] in + self?.collectionView.reloadData() + } + + self.collectionView.performBatchUpdates(nil) { [weak self] _ in + guard let self = self else { return } + let height = moduleItem?.br_cellHeight ?? 0 + let contentHeight = self.collectionView.contentSize.height + if height != contentHeight { + self.collectionView.snp.updateConstraints { make in + make.height.equalTo(contentHeight + 1) + } + moduleItem?.br_cellHeight = contentHeight + + self.br_tableView?.reloadData() + } + + + } + + } + } + + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let width = floor((UIScreen.width - 30 - 10) / 2) + + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: width, height: 222) + layout.minimumLineSpacing = 10 + layout.minimumInteritemSpacing = 10 + 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.register(BRSpotlightNewCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + containerView.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.height.equalTo(1) + } + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BRSpotlightNewMainCell: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRSpotlightNewCell + cell.model = self.moduleItem?.list?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return moduleItem?.list?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = self.moduleItem?.list?[indexPath.row] + + let vc = BRVideoDetailViewController() + vc.shortPlayId = model?.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightRecommandCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightRecommandCell.swift new file mode 100644 index 0000000..01fdfe5 --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightRecommandCell.swift @@ -0,0 +1,79 @@ +// +// BRSpotlightRecommandCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit +import FSPagerView + +class BRSpotlightRecommandCell: FSPagerViewCell { + + var model: BRShortModel? { + didSet { + coverImageView.br_setImage(url: model?.image_url) + } + } + + private lazy var coverImageView: BRImageView = { + let imageView = BRImageView() + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentView.layer.shadowOpacity = 0 + + br_setupUI() + + applyDiagonalMask() + } + + @MainActor required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + } + + + private func applyDiagonalMask() { + let maskLayer = CAShapeLayer() + let path = UIBezierPath() + let width = 150.0 + let height = 193.0 + let cut: CGFloat = 16 // 控制斜切角度 + let radius: CGFloat = 10 // 圆角半径 + + + path.move(to: CGPoint(x: 0, y: radius)) + path.addQuadCurve(to: CGPoint(x: radius, y: 0), controlPoint: CGPoint(x: 0, y: 0)) + + // 右上角 + path.addLine(to: CGPoint(x: width - radius, y: 0)) + path.addQuadCurve(to: CGPoint(x: width, y: radius), controlPoint: CGPoint(x: width, y: 0)) + // 右下角 + path.addLine(to: CGPoint(x: width - cut, y: height - radius)) + path.addQuadCurve(to: CGPoint(x: width - cut - radius, y: height), controlPoint: CGPoint(x: width - cut, y: height)) + // 左下角 + path.addLine(to: CGPoint(x: cut + radius, y: height)) + path.addQuadCurve(to: CGPoint(x: cut, y: height - radius), controlPoint: CGPoint(x: cut, y: height)) + path.close() + + maskLayer.path = path.cgPath + coverImageView.layer.mask = maskLayer + } +} + +extension BRSpotlightRecommandCell { + + private func br_setupUI() { + contentView.addSubview(coverImageView) + + coverImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightRecommandMainCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightRecommandMainCell.swift new file mode 100644 index 0000000..9bbcb63 --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightRecommandMainCell.swift @@ -0,0 +1,79 @@ +// +// BRSpotlightRecommandMainCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit +import FSPagerView + +class BRSpotlightRecommandMainCell: BRSpotlightMainBaseCell { + + override var moduleItem: BRHomeModuleItem? { + didSet { + bannerView.reloadData() + } + } + + + private lazy var bannerView: FSPagerView = { + let transformer = BRPagerViewTransformer(type: .ferrisWheel) + transformer.minimumAlpha = 1 + + let view = FSPagerView() + view.transformer = transformer + view.decelerationDistance = FSPagerView.automaticDistance + view.interitemSpacing = 0 + view.itemSize = .init(width: 150, height: 193) + view.isInfinite = true + view.delegate = self + view.dataSource = self + view.register(BRSpotlightRecommandCell.self, forCellWithReuseIdentifier: "cell") + + return view + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + containerView.addSubview(bannerView) + + bannerView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalToSuperview().offset(-20) + make.height.equalTo(193 + 40) + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + +} + +//MARK: -------------- FSPagerViewDataSource FSPagerViewDelegate -------------- +extension BRSpotlightRecommandMainCell: FSPagerViewDataSource, FSPagerViewDelegate { + + func numberOfItems(in pagerView: FSPagerView) -> Int { + return moduleItem?.list?.count ?? 0 + } + + func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell { + let cell = pagerView.dequeueReusableCell(withReuseIdentifier: "cell", at: index) as! BRSpotlightRecommandCell + cell.model = moduleItem?.list?[index] + return cell + } + + func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int) { + let model = self.moduleItem?.list?[index] + + let vc = BRVideoDetailViewController() + vc.shortPlayId = model?.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + + +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightTopCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightTopCell.swift new file mode 100644 index 0000000..73cebe4 --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightTopCell.swift @@ -0,0 +1,144 @@ +// +// BRSpotlightTopCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRSpotlightTopCell: BRCollectionViewCell { + + var model: BRShortModel? { + didSet { + coverImageView.br_setImage(url: model?.image_url) + + titleLabel.text = model?.name + categoryLabel.text = model?.category?.first + + hotView.setNeedsUpdateConfiguration() + } + } + + + private lazy var coverImageView: BRImageView = { + let imageView = BRImageView() + return imageView + }() + + private lazy var coverMarkView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "cover_mark_icon_01")) + return imageView + }() + + private lazy var playImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "play_icon_03")) + return imageView + }() + + private lazy var textBgView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "text_bg_image_01")) + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 12) + label.textColor = .color1C1C1C() + label.numberOfLines = 2 + return label + }() + + private lazy var categoryLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 10) + label.textColor = .color899D00() + return label + }() + + private lazy var hotView: UIButton = { + var config = UIButton.Configuration.plain() + config.image = UIImage(named: "hot_icon_02") + config.imagePlacement = .leading + config.imagePadding = 2 + config.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0) + + let button = UIButton(configuration: config) + button.isUserInteractionEnabled = false + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + let count = model?.watch_total ?? 0 + var string = "\(count)" + if count > 100 { + string = String(format: "%.1fk", Float(count) / 1000) + } + button.configuration?.attributedTitle = AttributedString.br_createAttributedString(string: string, color: .colorFF7489(), font: .fontRegular(ofSize: 10)) + + } + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BRSpotlightTopCell { + + private func br_setupUI() { + contentView.layer.cornerRadius = 11 + contentView.layer.masksToBounds = true + + + contentView.addSubview(coverImageView) + coverImageView.addSubview(coverMarkView) + contentView.addSubview(playImageView) + coverImageView.addSubview(textBgView) + textBgView.addSubview(titleLabel) + textBgView.addSubview(categoryLabel) + textBgView.addSubview(hotView) + + coverImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + coverMarkView.snp.makeConstraints { make in + make.right.bottom.equalToSuperview() + } + + playImageView.snp.makeConstraints { make in + make.right.equalToSuperview().offset(0) + make.bottom.equalToSuperview().offset(0) + } + + textBgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(10) + make.right.equalToSuperview().offset(-10) + make.bottom.equalToSuperview().offset(-10) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.top.equalToSuperview().offset(7) + make.right.lessThanOrEqualToSuperview().offset(-8) + } + + categoryLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.top.equalTo(titleLabel.snp.bottom).offset(5) + } + + hotView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.bottom.equalToSuperview().offset(-10) + } + } + +} diff --git a/BeeReel/Class/Home/View/Spotlight/BRSpotlightTopMainCell.swift b/BeeReel/Class/Home/View/Spotlight/BRSpotlightTopMainCell.swift new file mode 100644 index 0000000..37b081a --- /dev/null +++ b/BeeReel/Class/Home/View/Spotlight/BRSpotlightTopMainCell.swift @@ -0,0 +1,81 @@ +// +// BRSpotlightTopMainCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRSpotlightTopMainCell: BRSpotlightMainBaseCell { + + + override var moduleItem: BRHomeModuleItem? { + didSet { + collectionView.reloadData() + } + } + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) + layout.itemSize = .init(width: 210, height: 260) + layout.minimumLineSpacing = 10 + return layout + }() + + private lazy var collectionView: BRCollectionView = { + let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.register(BRSpotlightTopCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension BRSpotlightTopMainCell { + + private func br_setupUI() { + containerView.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.height.equalTo(260) + } + } + +} + +//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource -------------- +extension BRSpotlightTopMainCell: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRSpotlightTopCell + cell.model = self.moduleItem?.list?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.moduleItem?.list?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = self.moduleItem?.list?[indexPath.row] + + let vc = BRVideoDetailViewController() + vc.shortPlayId = model?.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } +} diff --git a/BeeReel/Class/Home/ViewModel/BRHomeViewModel.swift b/BeeReel/Class/Home/ViewModel/BRHomeViewModel.swift new file mode 100644 index 0000000..ca2d301 --- /dev/null +++ b/BeeReel/Class/Home/ViewModel/BRHomeViewModel.swift @@ -0,0 +1,67 @@ +// +// BRHomeViewModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/26. +// + +import UIKit + +class BRHomeViewModel { + + var homeOldDataArr: [BRHomeModuleItem] = [] { + didSet { + spotlightDataArr.removeAll() + + //v3数据 + var item1: BRHomeModuleItem? + //排行 + var item2: BRHomeModuleItem? + //新品 + var item3: BRHomeModuleItem? + + ///其余分类数据 + var item4List: [BRHomeModuleItem] = [] + + homeOldDataArr.forEach { + if $0.module_key == .banner { + bannerArr = $0.list + } else if $0.module_key == .v3_recommand { + $0.iconImage = UIImage(named: "hot_icon_01") + item1 = $0 + } else if $0.module_key == .week_ranking { + $0.iconImage = UIImage(named: "top_icon_01") + $0.title = "Top Charts".localized + item2 = $0 + } else if $0.module_key == .new_recommand { + $0.title = "Fresh Stories".localized + item3 = $0 + } else if $0.module_key == .cagetory_recommand { + item4List.append($0) + } + + } + + if let item = item1 { + spotlightDataArr.append(item) + } + + if let item = item2 { + spotlightDataArr.append(item) + } + + if let item = item3 { + spotlightDataArr.append(item) + } + + spotlightDataArr += item4List + + } + } + + var bannerArr: [BRShortModel]? + + var spotlightDataArr: [BRHomeModuleItem] = [] + + +} diff --git a/BeeReel/Class/Player/Controller/BRPlayerListViewController.swift b/BeeReel/Class/Player/Controller/BRPlayerListViewController.swift new file mode 100644 index 0000000..6d5d456 --- /dev/null +++ b/BeeReel/Class/Player/Controller/BRPlayerListViewController.swift @@ -0,0 +1,156 @@ +// +// BRPlayerListViewController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + + +@objc protocol BRPlayerListViewControllerDelegate { + + ///加载新数据 + @objc optional func br_playerViewControllerLoadNewDataV2(playerViewController: BRPlayerListViewController) + + ///将要加载更多数据 + @objc optional func br_playerViewControllerShouldLoadMoreData(playerViewController: BRPlayerListViewController) -> Bool + ///加载更多数据 + @objc optional func br_playerViewControllerLoadMoreData(playerViewController: BRPlayerListViewController) + ///向上加载更多数据 + @objc optional func br_playerViewControllerLoadUpMoreData(playerViewController: BRPlayerListViewController) + + ///当前展示的发生变化 + @objc optional func br_playerListViewController(_ viewController: BRPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) + +} + +@objc protocol BRPlayerListViewControllerDataSource { + + + func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell + + func br_playerListViewController(_ viewController: BRPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int + +} + +class BRPlayerListViewController: BRViewController { + + var contentSize: CGSize { + return CGSize(width: UIScreen.width, height: UIScreen.height - UIScreen.customTabBarHeight) + } + + var CellClass: BRPlayerListCell.Type { + return BRPlayerListCell.self + } + + private(set) lazy var viewModel = BRPlayerViewModel() + + weak var delegate: BRPlayerListViewControllerDelegate? + weak var dataSource: BRPlayerListViewControllerDataSource? + + + private lazy var collectionViewLayout: UICollectionViewLayout = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = contentSize + layout.minimumInteritemSpacing = 0 + layout.minimumLineSpacing = 0 + return layout + }() + + private(set) lazy var collectionView: BRCollectionView = { + let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.isPagingEnabled = true + collectionView.showsVerticalScrollIndicator = false + collectionView.showsHorizontalScrollIndicator = false + collectionView.bounces = false + collectionView.scrollsToTop = false + collectionView.register(CellClass.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + br_setupUI() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + } + + func play() { + if self.isDidAppear { + self.viewModel.currentPlayer?.start() + } + + self.viewModel.isPlaying = true + +// if getDataCount() - viewModel.currentIndexPath.row <= 2 { +// self.loadMoreData() +// } + } + +} + +extension BRPlayerListViewController { + private func br_setupUI() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview() + make.width.equalTo(self.contentSize.width) + make.height.equalTo(self.contentSize.height) + } + } + +} + +//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource -------------- +extension BRPlayerListViewController: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) + if let newCell = self.dataSource?.br_playerListViewController(self, collectionView, cellForItemAt: indexPath, oldCell: cell) { + cell = newCell + } + if let cell = cell as? BRPlayerListCell { + if cell.viewModel == nil { + cell.viewModel = viewModel + } + } + + + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + if let count = self.dataSource?.br_playerListViewController(self, collectionView, numberOfItemsInSection: section) { + return count + } else { + return 0 + } + } +} + + +extension BRPlayerListViewController { + + private func loadMoreData() { + let isLoad = self.delegate?.br_playerViewControllerShouldLoadMoreData?(playerViewController: self) + if isLoad != false { + self.delegate?.br_playerViewControllerLoadMoreData?(playerViewController: self) + } + } + + private func loadUpMoreData() { + self.delegate?.br_playerViewControllerLoadUpMoreData?(playerViewController: self) + } + + private func didChangeIndexPathForVisible() { + self.delegate?.br_playerListViewController?(self, didChangeIndexPathForVisible: viewModel.currentIndexPath) + } +} diff --git a/BeeReel/Class/Player/Controller/BRVideoDetailViewController.swift b/BeeReel/Class/Player/Controller/BRVideoDetailViewController.swift new file mode 100644 index 0000000..dc1c04e --- /dev/null +++ b/BeeReel/Class/Player/Controller/BRVideoDetailViewController.swift @@ -0,0 +1,75 @@ +// +// BRVideoDetailViewController.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRVideoDetailViewController: BRPlayerListViewController { + + + var shortPlayId: String? + var activityId: String? + + private var detailModel: BRVideoDetailModel? + + override func viewDidLoad() { + super.viewDidLoad() + + + self.requestDetailData() + } + + + + +} + + +extension BRVideoDetailViewController { + + private func requestDetailData(indexPath: IndexPath? = nil) { + guard let shortPlayId = shortPlayId else { return } + + + + + BRHUD.show(containerView: self.view) + BRVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId) { [weak self] model in + BRHUD.dismiss() + + guard let self = self else { return } + guard let model = model else { return } + self.detailModel = model +// self.videoNameLabel.text = model.shortPlayInfo?.name + + /* + self.reloadData { [weak self] in + guard let self = self else { return } + + 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) + } + } + */ + } + } + +} diff --git a/BeeReel/Class/Player/Model/BRPlayerProtocol.swift b/BeeReel/Class/Player/Model/BRPlayerProtocol.swift new file mode 100644 index 0000000..54ba3b7 --- /dev/null +++ b/BeeReel/Class/Player/Model/BRPlayerProtocol.swift @@ -0,0 +1,42 @@ +// +// BRPlayerProtocol.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +@objc protocol BRPlayerProtocol: NSObjectProtocol { + + + var videoInfo: BRVideoInfoModel? { get set } + + var isCurrent: Bool { get set } + + ///上一集是否加锁 + @objc optional var hasLastEpisodeUnlocked: Bool { get set } + + ///总进度 + var duration: Int { get } + ///当前进度 + var currentPosition: Int { get } + + var rate: Float { get set } + + ///播放准备 + func prepare() + + ///开始播放 + func start() + + ///暂停播放 + func pause() + + ///从头播放 + func replay() + + ///设置进度 + func seekToTime(toTime: Int) + +} diff --git a/BeeReel/Class/Player/Model/BRVideoDetailModel.swift b/BeeReel/Class/Player/Model/BRVideoDetailModel.swift new file mode 100644 index 0000000..eff4b5a --- /dev/null +++ b/BeeReel/Class/Player/Model/BRVideoDetailModel.swift @@ -0,0 +1,24 @@ +// +// BRVideoDetailModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit +import SmartCodable + +class BRVideoDetailModel: BRModel, SmartCodable { + + var business_model: String? + var video_info: BRVideoInfoModel? + var shortPlayInfo: BRShortModel? + var episodeList: [BRVideoInfoModel]? + var is_collect: Bool? + var show_share_coin: Int? + var share_coin: Int? + var install_coins: Int? + var revolution: Int? + var unlock_video_ad_count: Int? + var discount: Int? +} diff --git a/BeeReel/Class/Player/View/BRPlayerListCell.swift b/BeeReel/Class/Player/View/BRPlayerListCell.swift new file mode 100644 index 0000000..667d694 --- /dev/null +++ b/BeeReel/Class/Player/View/BRPlayerListCell.swift @@ -0,0 +1,87 @@ +// +// BRPlayerListCell.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRPlayerListCell: BRCollectionViewCell, BRPlayerProtocol { + + + var viewModel: BRPlayerViewModel? { + didSet { + + } + } + + var videoInfo: BRVideoInfoModel? { + didSet { + player.setPlayUrl(url: videoInfo?.video_url ?? "") + } + } + + var isCurrent: Bool = false + + var duration: Int = 0 + + var currentPosition: Int = 0 + + var rate: Float = 1 + + func prepare() { + + } + + func start() { + + } + + func pause() { + + } + + func replay() { + + } + + func seekToTime(toTime: Int) { + + } + + + private lazy var player: BRPlayer = { + let player = BRPlayer() + player.playerView = self.playerView + return player + }() + + private lazy var playerView: UIView = { + let view = UIView() + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + br_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension BRPlayerListCell { + + private func br_setupUI() { + contentView.addSubview(playerView) + + playerView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + +} diff --git a/BeeReel/Class/Player/ViewModel/BRPlayerViewModel.swift b/BeeReel/Class/Player/ViewModel/BRPlayerViewModel.swift new file mode 100644 index 0000000..04d063b --- /dev/null +++ b/BeeReel/Class/Player/ViewModel/BRPlayerViewModel.swift @@ -0,0 +1,26 @@ +// +// BRPlayerViewModel.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + +class BRPlayerViewModel: NSObject { + + + @objc dynamic var isPlaying: Bool = true + + var currentIndexPath = IndexPath(row: 0, section: 0) + + var currentPlayer: BRPlayerProtocol? { + didSet { + oldValue?.isCurrent = false + oldValue?.pause() + + self.currentPlayer?.isCurrent = true +// self.currentPlayer?.rate = rateModel.rate.getRate() + } + } +} diff --git a/BeeReel/Class/Player/ViewModel/BRVideoRevolutionManager.swift b/BeeReel/Class/Player/ViewModel/BRVideoRevolutionManager.swift new file mode 100644 index 0000000..00fa547 --- /dev/null +++ b/BeeReel/Class/Player/ViewModel/BRVideoRevolutionManager.swift @@ -0,0 +1,72 @@ +// +// BRVideoRevolutionManager.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/30. +// + +import UIKit + + +class BRVideoRevolutionManager: NSObject { + + static let manager = BRVideoRevolutionManager() + + ///当前分辨率 + lazy var revolution: BRShortModel.VideoRevolution = { + let userInfo = BRLoginManager.manager.userInfo + + if let revolution = UserDefaults.standard.object(forKey: kBRVideoRevolutionDefaultsKey) as? String { + var revolution = verify(revolution: BRShortModel.VideoRevolution.init(rawValue: revolution) ?? .r_540) + return revolution + } + return .r_540 + }() + + override init() { + super.init() + NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: BRLoginManager.userInfoUpdateNotification, object: nil) + } + + @objc private func userInfoUpdateNotification() { + self.setVideoRevolution(revolution: self.revolution) + } + + func setVideoRevolution(revolution: BRShortModel.VideoRevolution) { + let newRevolution = verify(revolution: revolution) + + if newRevolution != self.revolution { + self.revolution = newRevolution + NotificationCenter.default.post(name: BRVideoRevolutionManager.didChangeRevolutionNotification, object: nil) + UserDefaults.standard.set(newRevolution.rawValue, forKey: kBRVideoRevolutionDefaultsKey) + } + } + +} + +extension BRVideoRevolutionManager { + + ///校验分辨率是否可用,并返回可用分辨率 + func verify(revolution: BRShortModel.VideoRevolution) -> BRShortModel.VideoRevolution { + let userInfo = BRLoginManager.manager.userInfo + var newRevolution = revolution + if userInfo?.is_vip != true { + if newRevolution == .r_1080 { + newRevolution = .r_720 + } + if userInfo?.is_tourist != false, revolution != .r_540 { + newRevolution = .r_540 + } + } + return newRevolution + } + +} + + +extension BRVideoRevolutionManager { + + ///分辨率发生变化 + @objc static let didChangeRevolutionNotification = NSNotification.Name(rawValue: "VPVideoRevolutionManager.didChangeRevolutionNotification") +} + diff --git a/BeeReel/Delegate/AppDelegate+BRConfig.swift b/BeeReel/Delegate/AppDelegate+BRConfig.swift new file mode 100644 index 0000000..e4c9c80 --- /dev/null +++ b/BeeReel/Delegate/AppDelegate+BRConfig.swift @@ -0,0 +1,14 @@ +// +// AppDelegate+BRConfig.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/25. +// + +extension AppDelegate { + + func addConfig() { + UIView.vp_Awake() + } + +} diff --git a/BeeReel/Delegate/AppDelegate.swift b/BeeReel/Delegate/AppDelegate.swift new file mode 100644 index 0000000..88ea2aa --- /dev/null +++ b/BeeReel/Delegate/AppDelegate.swift @@ -0,0 +1,41 @@ +// +// AppDelegate.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + BRAppTool.appDelegate = self + + BRLoginManager.manager.updateUserInfo(completer: nil) + + addConfig() + + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/BeeReel/Delegate/SceneDelegate.swift b/BeeReel/Delegate/SceneDelegate.swift new file mode 100644 index 0000000..90f9d94 --- /dev/null +++ b/BeeReel/Delegate/SceneDelegate.swift @@ -0,0 +1,56 @@ +// +// SceneDelegate.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + BRAppTool.sceneDelegate = self + BRAppTool.windowScene = windowScene + + window = UIWindow(windowScene: windowScene) + + window?.rootViewController = BRTabBarController() + window?.makeKeyAndVisible() + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/BeeReel/Lib/AppTool/BRAppTool.swift b/BeeReel/Lib/AppTool/BRAppTool.swift new file mode 100644 index 0000000..ea9025c --- /dev/null +++ b/BeeReel/Lib/AppTool/BRAppTool.swift @@ -0,0 +1,55 @@ +// +// BRAppTool.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit + +class BRAppTool { + + static var appDelegate: AppDelegate? + static var sceneDelegate: SceneDelegate? + + static var windowScene: UIWindowScene? + + + static var keyWindow: UIWindow? { + return windowScene?.keyWindow + } + + static var rootViewController: UIViewController? { + return keyWindow?.rootViewController + } + + ///获得启动图 + static var lanuchViewController: UIViewController? { + let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil) + let vc = storyboard.instantiateInitialViewController() + return vc + } + + static var topViewController: UIViewController? { + var resultVC: UIViewController? = self.rootViewController + if let rootNav = resultVC as? UINavigationController { + resultVC = rootNav.topViewController + } + + resultVC = self._topViewController(resultVC) + while resultVC?.presentedViewController != nil { + resultVC = self._topViewController(resultVC?.presentedViewController) + } + return resultVC + } + + private static func _topViewController(_ vc: UIViewController?) -> UIViewController? { + if vc is UINavigationController { + return _topViewController((vc as? UINavigationController)?.topViewController) + } else if vc is UITabBarController { + return _topViewController((vc as? UITabBarController)?.selectedViewController) + } else { + return vc + } + } +} diff --git a/BeeReel/Lib/HUD/BRHUD.swift b/BeeReel/Lib/HUD/BRHUD.swift new file mode 100644 index 0000000..282d058 --- /dev/null +++ b/BeeReel/Lib/HUD/BRHUD.swift @@ -0,0 +1,21 @@ +// +// BRHUD.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import SVProgressHUD + +struct BRHUD { + + static func show(containerView: UIView? = nil, type: SVProgressHUDMaskType = .clear) { + SVProgressHUD.setContainerView(containerView) + SVProgressHUD.setDefaultMaskType(type) + SVProgressHUD.show() + } + + static func dismiss() { + SVProgressHUD.dismiss() + } +} diff --git a/BeeReel/Lib/HUD/BRToast.swift b/BeeReel/Lib/HUD/BRToast.swift new file mode 100644 index 0000000..45c1cd8 --- /dev/null +++ b/BeeReel/Lib/HUD/BRToast.swift @@ -0,0 +1,22 @@ +// +// BRToast.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import Toast + +struct BRToast { + + static func config() { + CSToastManager.setTapToDismissEnabled(false) + CSToastManager.setDefaultDuration(2) + CSToastManager.setDefaultPosition(CSToastPositionCenter) + } + + static func show(text: String?) { + guard let text = text else { return } + BRAppTool.keyWindow?.makeToast(text) + } +} diff --git a/BeeReel/Lib/LocalizedManager/BRLocalizedManager.swift b/BeeReel/Lib/LocalizedManager/BRLocalizedManager.swift new file mode 100644 index 0000000..52810c8 --- /dev/null +++ b/BeeReel/Lib/LocalizedManager/BRLocalizedManager.swift @@ -0,0 +1,122 @@ +// +// BRLocalizedManager.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import Foundation + +class BRLocalizedManager { + + static let manager = BRLocalizedManager() + + private let LocalizedUserDefaultsKey = "BRLocalizedManager.LocalizedUserDefaultsKey" + private let LocalizedDataUserDefaultsKey = "BRLocalizedManager.LocalizedDataUserDefaultsKey" + private let LocalizedDataLocalizedKeyUserDefaultsKey = "BRLocalizedManager.LocalizedDataLocalizedKeyUserDefaultsKey" + + + ///多语言数据 + private(set) lazy var localizedData: [String : String]? = UserDefaults.standard.object(forKey: LocalizedDataUserDefaultsKey) as? [String : String] + { + didSet { + UserDefaults.standard.set(localizedData, forKey: LocalizedDataUserDefaultsKey) + UserDefaults.standard.synchronize() + } + } + ///当前语言数据对应的key + private(set) lazy var localizedDataLocalizedKey: String? = UserDefaults.standard.object(forKey: LocalizedDataLocalizedKeyUserDefaultsKey) as? String + { + didSet { + UserDefaults.standard.set(localizedDataLocalizedKey, forKey: LocalizedDataLocalizedKeyUserDefaultsKey) + UserDefaults.standard.synchronize() + } + } + + // 获取当前语言代码(如果用户未手动设置,则返回系统语言) + var currentLocalizedKey: String { + get { + return "en" +// var key = UserDefaults.standard.string(forKey: LocalizedUserDefaultsKey) ?? Locale.preferredLanguages.first +// +// if key?.contains("zh-Hans") == true { +// key = "zh" +// } else if key?.contains("zh-Hant") == true { +// key = "zh_hk" +// } else { +// let arr = key?.components(separatedBy: "-") +// key = arr?.first +// } +// return key ?? "en" + } + set { + UserDefaults.standard.set(newValue, forKey: LocalizedUserDefaultsKey) + UserDefaults.standard.synchronize() + } + } + + var mjLocalizedKey: String { + let key = currentLocalizedKey + if key == "zh" { + return "zh-Hans" + } else if key == "zh_hk" { + return "zh-Hant" + } + return key + } + + // 判断是否跟随系统 + var isFollowingSystem: Bool { + return UserDefaults.standard.string(forKey: LocalizedUserDefaultsKey) == nil + } + + // 还原为系统默认语言 + func resetToSystemLanguage() { + UserDefaults.standard.removeObject(forKey: LocalizedUserDefaultsKey) + UserDefaults.standard.synchronize() + } + + // 获取本地化字符串 + func localizedString(forKey key: String, tableName: String? = nil) -> String { + if let localizedData = localizedData, + let text = localizedData[key] { + return text + + } else if let selectedLanguage = UserDefaults.standard.string(forKey: LocalizedUserDefaultsKey), + let bundlePath = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"), + let bundle = Bundle(path: bundlePath) { + + return bundle.localizedString(forKey: key, value: nil, table: tableName) + } else { + return NSLocalizedString(key, tableName: tableName, bundle: .main, value: "", comment: "") + } + } + +} + +extension BRLocalizedManager { + + static let localizedDidChangeNotification = Notification.Name(rawValue: "BRLocalizedManager.localizedDidChangeNotification") + +} + +extension String { + var localized: String { + var text = BRLocalizedManager.manager.localizedString(forKey: self) + text = text.replacingOccurrences(of: "
", with: "\n") + return text + } + + func localizedReplace(text: String) -> String { + return self.localized.replacingOccurrences(of: "##", with: text) + } + + func localizedReplace(text1: String, text2: String, text3: String? = nil) -> String { + var string = self.localized.replacingOccurrences(of: "#1#", with: text1) + string = string.replacingOccurrences(of: "#2#", with: text2) + if let text = text3 { + string = string.replacingOccurrences(of: "#3#", with: text) + } + return string + } +} diff --git a/BeeReel/Lib/Login/BRLoginManager.swift b/BeeReel/Lib/Login/BRLoginManager.swift new file mode 100644 index 0000000..e7963a2 --- /dev/null +++ b/BeeReel/Lib/Login/BRLoginManager.swift @@ -0,0 +1,127 @@ +// +// BRLoginManager.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit +import SmartCodable + +class BRLoginManager { + + enum LoginType: String, SmartCaseDefaultable { + case apple = "Apple" + case faceBook = "Facebook" + case google = "Google" + case tiktok = "Tiktok" + } + + + static let manager = BRLoginManager() + + private(set) var token = UserDefaults.br_object(forKey: kBRLoginTokenDefaultsKey, as: BRLoginToken.self) + private(set) var userInfo = UserDefaults.br_object(forKey: kBRLoginUserInfoDefaultsKey, as: BRUserInfo.self) + + ///是否正在刷新token + private(set) var isRefreshingToken = false + + + + func setLoginToken(token: BRLoginToken?) { + self.token = token + UserDefaults.br_setObject(token, forKey: kBRLoginTokenDefaultsKey) + } + + func openLogin(finishHandle: (() -> Void)? = nil) { +// let view = VPLoginContentView() +// view.loginFinishBlock = finishHandle +// view.present(in: nil) + } + + func login(type: LoginType, presentingViewController: UIViewController?, completer: ((_ isFinish: Bool) -> Void)?) { + +// switch type { +// case .apple: +// appleSignLogin { [weak self] model in +// self?.requestThirdLogin(thirdSignModel: model, completer: completer) +// } +// +// case .faceBook: +// facebookLogin(presentingViewController: presentingViewController) { [weak self] model in +// self?.requestThirdLogin(thirdSignModel: model, completer: completer) +// } +// default: +// completer?(false) +// } + + } + + ///退出登录 + func logout(completer: ((_ isFinish: Bool) -> Void)?) { +// VPStatAPI.requestLeaveApp() +// VPUserAPI.requestLogout { [weak self] token in +// guard let self = self else { return } +// if let token = token { +// self.setLoginToken(token: token) +// self.userInfo?.is_tourist = true +// self.updateUserInfo(completer: nil) +// VPStatAPI.requestStatOnLine() +// VPStatAPI.requestEnterApp() +// completer?(true) +// NotificationCenter.default.post(name: VPLoginManager.userInfoUpdateNotification, object: nil) +// NotificationCenter.default.post(name: VPLoginManager.loginStateDidChangeNotification, object: nil) +// } else { +// completer?(false) +// } +// } + } + + ///删除账号 + func deleteAccount(completer: ((_ isFinish: Bool) -> Void)?) { +// VPStatAPI.requestLeaveApp() +// VPUserAPI.requestDelete { [weak self] isFinish in +// guard let self = self else { return } +// if isFinish { +// self.setLoginToken(token: nil) +// self.userInfo?.is_tourist = true +// self.updateUserInfo(completer: nil) +// VPStatAPI.requestStatOnLine() +// VPStatAPI.requestEnterApp() +// completer?(true) +// NotificationCenter.default.post(name: VPLoginManager.userInfoUpdateNotification, object: nil) +// NotificationCenter.default.post(name: VPLoginManager.loginStateDidChangeNotification, object: nil) +// } else { +// completer?(false) +// } +// } + } + + + ///更新用户信息 + func updateUserInfo(completer: (() -> Void)?) { + + BRUserAPI.requestUserInfo { [weak self] userInfo in + guard let self = self else { return } + if let userInfo = userInfo { + self.userInfo = userInfo + UserDefaults.br_setObject(userInfo, forKey: kBRLoginUserInfoDefaultsKey) + NotificationCenter.default.post(name: BRLoginManager.userInfoUpdateNotification, object: nil) + } + completer?() + } + + } + + + +} + +extension BRLoginManager { + + ///登录状态发生变化 + @objc static let loginStateDidChangeNotification = NSNotification.Name(rawValue: "BRLoginManager.loginStateDidChangeNotification") + ///用户信息更新 + @objc static let userInfoUpdateNotification = NSNotification.Name(rawValue: "BRLoginManager.userInfoUpdateNotification") + +} diff --git a/BeeReel/Lib/Login/BRLoginToken.swift b/BeeReel/Lib/Login/BRLoginToken.swift new file mode 100644 index 0000000..15537a9 --- /dev/null +++ b/BeeReel/Lib/Login/BRLoginToken.swift @@ -0,0 +1,43 @@ +// +// BRLoginToken.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit +import SmartCodable + +class BRLoginToken: BRModel, SmartCodable, NSSecureCoding { + + var token: String? + var customer_id: String? + var auto_login: Int? + + required init() { } + + static var supportsSecureCoding: Bool { + get { + return true + } + } + + + func encode(with coder: NSCoder) { + coder.encode(token, forKey: "token") + coder.encode(customer_id, forKey: "customer_id") + coder.encode(auto_login, forKey: "auto_login") + } + + required init?(coder: NSCoder) { + super.init() + + token = coder.decodeObject(of: NSString.self, forKey: "token") as? String + customer_id = coder.decodeObject(of: NSString.self, forKey: "customer_id") as? String + auto_login = coder.decodeObject(of: NSNumber.self, forKey: "auto_login")?.intValue + } + + + + +} diff --git a/BeeReel/Lib/Player/BRPlayer.swift b/BeeReel/Lib/Player/BRPlayer.swift new file mode 100644 index 0000000..5349a4b --- /dev/null +++ b/BeeReel/Lib/Player/BRPlayer.swift @@ -0,0 +1,90 @@ +// +// BRPlayer.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit +import SJBaseVideoPlayer + + +class BRPlayer { + + private lazy var player: SJBaseVideoPlayer = { + let player = SJBaseVideoPlayer() + player.autoplayWhenSetNewAsset = false + return player + }() + + var playerView: UIView? { + didSet { + playerView?.addSubview(player.view) + player.view.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + } + + var duration: TimeInterval { + return self.player.duration + } + + var currentTime: TimeInterval { + return self.player.currentTime + } + + init() { + setupPlayer() + } + + func setPlayUrl(url: String) { + self.stop() + guard let url = URL(string: url) else { return } + let asset = SJVideoPlayerURLAsset(url: url) + self.player.urlAsset = asset + } + + func start() { + self.player.play() + } + + func pause() { + self.player.pause() + } + + func stop() { + self.player.stop() + } + + func seek(toTime: Int) { + + } +} + +extension BRPlayer { + + private func setupPlayer() { + //播放完成回调 + self.player.playbackObserver.playbackDidFinishExeBlock = { [weak self] player in + guard let self = self else { return } + + } + //播放状态改变 + self.player.playbackObserver.playbackStatusDidChangeExeBlock = { [weak self] player in + guard let self = self else { return } + + } + //播放时长改变 + self.player.playbackObserver.durationDidChangeExeBlock = { [weak self] player in + guard let self = self else { return } + + } + //播放进度改变 + self.player.playbackObserver.currentTimeDidChangeExeBlock = { [weak self] player in + guard let self = self else { return } + + } + } + +} diff --git a/BeeReel/Lib/User/BRUserInfo.swift b/BeeReel/Lib/User/BRUserInfo.swift new file mode 100644 index 0000000..b938733 --- /dev/null +++ b/BeeReel/Lib/User/BRUserInfo.swift @@ -0,0 +1,72 @@ +// +// BRUserInfo.swift +// BeeReel +// +// Created by 湖南秦九 on 2025/6/24. +// + +import UIKit +import SmartCodable + +class BRUserInfo: BRModel, SmartCodable, NSSecureCoding { + + var id: String? + var customer_id: String? + var is_guide_vip: String? + ///是否为游客登录 + var is_tourist: Bool? + var family_name: String? + var giving_name: String? + var vip_end_time: TimeInterval? + var third_access_id: String? + var is_vip: Bool? + var coin_left_total: Int? + var send_coin_left_total: Int? + var vip_type: String? + var email: String? + var third_access_platform: String? + var ip_address: String? + var country_code: String? + var user_level: String? + var avator: String? + var sign_in_status: String? + var registered_days: String? + var ln: String? + var country: String? + + + var totalCoin: Int { + return (coin_left_total ?? 0) + (send_coin_left_total ?? 0) + } + + required init() { } + + static var supportsSecureCoding: Bool { + get { + return true + } + } + + + func encode(with coder: NSCoder) { + coder.encode(id, forKey: "id") + coder.encode(customer_id, forKey: "customer_id") + coder.encode(is_tourist, forKey: "is_tourist") + coder.encode(is_vip, forKey: "is_vip") + coder.encode(avator, forKey: "avator") + coder.encode(family_name, forKey: "family_name") + coder.encode(giving_name, forKey: "giving_name") + } + + required init?(coder: NSCoder) { + super.init() + id = coder.decodeObject(of: NSString.self, forKey: "id") as? String + customer_id = coder.decodeObject(of: NSString.self, forKey: "customer_id") as? String + is_tourist = coder.decodeObject(of: NSNumber.self, forKey: "is_tourist")?.boolValue + is_vip = coder.decodeObject(of: NSNumber.self, forKey: "is_vip")?.boolValue + avator = coder.decodeObject(of: NSString.self, forKey: "avator") as? String + family_name = coder.decodeObject(of: NSString.self, forKey: "family_name") as? String + giving_name = coder.decodeObject(of: NSString.self, forKey: "giving_name") as? String + } + +} diff --git a/BeeReel/Sources/Assets.xcassets/AccentColor.colorset/Contents.json b/BeeReel/Sources/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json b/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/Contents.json b/BeeReel/Sources/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Contents.json new file mode 100644 index 0000000..9ef6fb7 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Exclude@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Exclude@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Exclude@2x.png b/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Exclude@2x.png new file mode 100644 index 0000000..9d0079e Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Exclude@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Exclude@3x.png b/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Exclude@3x.png new file mode 100644 index 0000000..4064219 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/cover_mark_icon_01.imageset/Exclude@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Component 2@2x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Component 2@2x.png new file mode 100644 index 0000000..b0a9979 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Component 2@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Component 2@3x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Component 2@3x.png new file mode 100644 index 0000000..fd11f15 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Component 2@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Contents.json new file mode 100644 index 0000000..19b16b1 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Component 2@2x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Component 2@2x.png new file mode 100644 index 0000000..55210c5 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Component 2@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Component 2@3x.png b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Component 2@3x.png new file mode 100644 index 0000000..49737ca Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Component 2@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Contents.json new file mode 100644 index 0000000..19b16b1 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/favorite_icon_01_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/Contents.json new file mode 100644 index 0000000..cb7b1bc --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "顶部卡片bg@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "顶部卡片bg@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/顶部卡片bg@2x.png b/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/顶部卡片bg@2x.png new file mode 100644 index 0000000..225de44 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/顶部卡片bg@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/顶部卡片bg@3x.png b/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/顶部卡片bg@3x.png new file mode 100644 index 0000000..3d9cdb8 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/home_header_icon_01.imageset/顶部卡片bg@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5472a49 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "hot@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "hot@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/hot@2x.png b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/hot@2x.png new file mode 100644 index 0000000..086356e Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/hot@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/hot@3x.png b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/hot@3x.png new file mode 100644 index 0000000..aa7b8c8 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_01.imageset/hot@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Frame@2x.png b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Frame@2x.png new file mode 100644 index 0000000..0a51961 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Frame@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Frame@3x.png b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Frame@3x.png new file mode 100644 index 0000000..67bda4a Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/hot_icon_02.imageset/Frame@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Component 1@2x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Component 1@2x.png new file mode 100644 index 0000000..bb06bfe Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Component 1@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Component 1@3x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Component 1@3x.png new file mode 100644 index 0000000..ba60c83 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Component 1@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Contents.json new file mode 100644 index 0000000..392dbd7 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/play_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Component 1@2x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Component 1@2x.png new file mode 100644 index 0000000..7f36f0b Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Component 1@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Component 1@3x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Component 1@3x.png new file mode 100644 index 0000000..796e46a Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Component 1@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Contents.json new file mode 100644 index 0000000..392dbd7 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/play_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Component 11@2x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Component 11@2x.png new file mode 100644 index 0000000..9f460e9 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Component 11@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Component 11@3x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Component 11@3x.png new file mode 100644 index 0000000..8664723 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Component 11@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Contents.json new file mode 100644 index 0000000..9321853 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/play_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 11@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 11@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Component 1@2x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Component 1@2x.png new file mode 100644 index 0000000..f038d5d Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Component 1@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Component 1@3x.png b/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Component 1@3x.png new file mode 100644 index 0000000..8b85407 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Component 1@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Contents.json new file mode 100644 index 0000000..392dbd7 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/play_icon_04.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Component 14@2x.png b/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Component 14@2x.png new file mode 100644 index 0000000..249c654 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Component 14@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Component 14@3x.png b/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Component 14@3x.png new file mode 100644 index 0000000..197caf2 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Component 14@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Contents.json new file mode 100644 index 0000000..0e6f35f --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/search_button_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 14@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 14@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/Contents.json new file mode 100644 index 0000000..8bb64fe --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "奖杯 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "奖杯 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/奖杯 1@2x.png b/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/奖杯 1@2x.png new file mode 100644 index 0000000..fb4f1d1 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/奖杯 1@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/奖杯 1@3x.png b/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/奖杯 1@3x.png new file mode 100644 index 0000000..fe9e31d Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/icon/top_icon_01.imageset/奖杯 1@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/Contents.json b/BeeReel/Sources/Assets.xcassets/image/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/image/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Contents.json new file mode 100644 index 0000000..b84ecd9 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Contents.json @@ -0,0 +1,50 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 11@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 548, + "left" : 350, + "right" : 136, + "top" : 37 + }, + "center" : { + "height" : 95, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "2x" + }, + { + "filename" : "Frame 11@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 806, + "left" : 477, + "right" : 238, + "top" : 80 + }, + "center" : { + "height" : 134, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Frame 11@2x.png b/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Frame 11@2x.png new file mode 100644 index 0000000..07e8e15 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Frame 11@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Frame 11@3x.png b/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Frame 11@3x.png new file mode 100644 index 0000000..73d769c Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/home_header_bg_image.imageset/Frame 11@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..078ffa7 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "选中光效@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 49, + "right" : 70 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "选中光效@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 64, + "right" : 100 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/选中光效@2x.png b/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/选中光效@2x.png new file mode 100644 index 0000000..d095210 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/选中光效@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/选中光效@3x.png b/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/选中光效@3x.png new file mode 100644 index 0000000..1767418 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/menu_bg_image_01.imageset/选中光效@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..62e475d --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 49@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 49@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Rectangle 49@2x.png b/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Rectangle 49@2x.png new file mode 100644 index 0000000..e39c251 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Rectangle 49@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Rectangle 49@3x.png b/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Rectangle 49@3x.png new file mode 100644 index 0000000..4ceed5d Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/image/text_bg_image_01.imageset/Rectangle 49@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Contents.json new file mode 100644 index 0000000..3458710 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Default@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Default@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Property 1=Default@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Property 1=Default@2x.png new file mode 100644 index 0000000..50bbff7 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Property 1=Default@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Property 1=Default@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Property 1=Default@3x.png new file mode 100644 index 0000000..5a9653c Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01.imageset/Property 1=Default@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Contents.json new file mode 100644 index 0000000..239b6b0 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Variant2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Variant2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Property 1=Variant2@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Property 1=Variant2@2x.png new file mode 100644 index 0000000..5a9452e Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Property 1=Variant2@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Property 1=Variant2@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Property 1=Variant2@3x.png new file mode 100644 index 0000000..d32e426 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_01_selected.imageset/Property 1=Variant2@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Contents.json new file mode 100644 index 0000000..3458710 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Default@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Default@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Property 1=Default@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Property 1=Default@2x.png new file mode 100644 index 0000000..e765a71 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Property 1=Default@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Property 1=Default@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Property 1=Default@3x.png new file mode 100644 index 0000000..be78f2f Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02.imageset/Property 1=Default@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Contents.json new file mode 100644 index 0000000..239b6b0 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Variant2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Variant2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Property 1=Variant2@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Property 1=Variant2@2x.png new file mode 100644 index 0000000..c522556 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Property 1=Variant2@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Property 1=Variant2@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Property 1=Variant2@3x.png new file mode 100644 index 0000000..1a3843b Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_02_selected.imageset/Property 1=Variant2@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Contents.json new file mode 100644 index 0000000..3458710 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Default@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Default@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Property 1=Default@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Property 1=Default@2x.png new file mode 100644 index 0000000..3eef094 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Property 1=Default@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Property 1=Default@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Property 1=Default@3x.png new file mode 100644 index 0000000..7ff7c8c Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03.imageset/Property 1=Default@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Contents.json new file mode 100644 index 0000000..239b6b0 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Variant2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Variant2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Property 1=Variant2@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Property 1=Variant2@2x.png new file mode 100644 index 0000000..0879aa4 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Property 1=Variant2@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Property 1=Variant2@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Property 1=Variant2@3x.png new file mode 100644 index 0000000..5208b0f Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_03_selected.imageset/Property 1=Variant2@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Contents.json new file mode 100644 index 0000000..3458710 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Default@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Default@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Property 1=Default@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Property 1=Default@2x.png new file mode 100644 index 0000000..0dc571b Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Property 1=Default@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Property 1=Default@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Property 1=Default@3x.png new file mode 100644 index 0000000..6acf852 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04.imageset/Property 1=Default@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Contents.json new file mode 100644 index 0000000..239b6b0 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=Variant2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=Variant2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Property 1=Variant2@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Property 1=Variant2@2x.png new file mode 100644 index 0000000..818365b Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Property 1=Variant2@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Property 1=Variant2@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Property 1=Variant2@3x.png new file mode 100644 index 0000000..f0b504e Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_icon_04_selected.imageset/Property 1=Variant2@3x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/Contents.json b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/Contents.json new file mode 100644 index 0000000..7dcd974 --- /dev/null +++ b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "选中@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "选中@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/选中@2x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/选中@2x.png new file mode 100644 index 0000000..4d55f76 Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/选中@2x.png differ diff --git a/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/选中@3x.png b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/选中@3x.png new file mode 100644 index 0000000..55fceec Binary files /dev/null and b/BeeReel/Sources/Assets.xcassets/tabbar/tabbar_top_icon.imageset/选中@3x.png differ diff --git a/BeeReel/Sources/Base.lproj/LaunchScreen.storyboard b/BeeReel/Sources/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/BeeReel/Sources/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BeeReel/Sources/Bridging-Header.h b/BeeReel/Sources/Bridging-Header.h new file mode 100644 index 0000000..4e44453 --- /dev/null +++ b/BeeReel/Sources/Bridging-Header.h @@ -0,0 +1,8 @@ + + +#import "JXUUID.h" +#import +#import +#import +#import +#import "WMZBannerView.h" diff --git a/BeeReel/Sources/Info.plist b/BeeReel/Sources/Info.plist new file mode 100644 index 0000000..0eb786d --- /dev/null +++ b/BeeReel/Sources/Info.plist @@ -0,0 +1,23 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + diff --git a/BeeReel/Sources/Localizable.xcstrings b/BeeReel/Sources/Localizable.xcstrings new file mode 100644 index 0000000..793624e --- /dev/null +++ b/BeeReel/Sources/Localizable.xcstrings @@ -0,0 +1,94 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "Browse Genres" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Browse Genres" + } + } + } + }, + "Categories" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Categories" + } + } + } + }, + "Fresh Stories" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fresh Stories" + } + } + } + }, + "New Releases" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New Releases" + } + } + } + }, + "Popular Picks" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Popular Picks" + } + } + } + }, + "Spotlight" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spotlight" + } + } + } + }, + "Top 10" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Top 10" + } + } + } + }, + "Top Charts" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Top Charts" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/BeeReel/Thirdparty/JXUUID/JXUUID.h b/BeeReel/Thirdparty/JXUUID/JXUUID.h new file mode 100644 index 0000000..242e0b6 --- /dev/null +++ b/BeeReel/Thirdparty/JXUUID/JXUUID.h @@ -0,0 +1,20 @@ +// +// JXUUID.h +// 设备标识符 +// +// Created by 曾觉新 on 2017/8/24. +// Copyright © 2017年 曾觉新. All rights reserved. +// + +#import + +@interface JXUUID : NSObject + ++ (nonnull NSString *)uuid; ++ (nonnull NSString *)idfa; +/** + 重新安装app后,会发生变化 + */ ++ (nonnull NSString *)systemUUID; + +@end diff --git a/BeeReel/Thirdparty/JXUUID/JXUUID.m b/BeeReel/Thirdparty/JXUUID/JXUUID.m new file mode 100644 index 0000000..be414ea --- /dev/null +++ b/BeeReel/Thirdparty/JXUUID/JXUUID.m @@ -0,0 +1,47 @@ +// +// JXUUID.m +// 设备标识符 +// +// Created by 曾觉新 on 2017/8/24. +// Copyright © 2017年 曾觉新. All rights reserved. +// + +#import "JXUUID.h" +#import +#import "PDKeyChain.h" +#import + +static NSString *const uuidKey = @"com.JXUUID"; + +@implementation JXUUID + ++ (nonnull NSString *)uuid +{ + static NSString *uuid; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + uuid = [PDKeyChain objectForKey:uuidKey]; + if (uuid && uuid.length > 0) { + } else { + uuid = [[NSUUID UUID] UUIDString]; + [PDKeyChain setObject:uuid forKey:uuidKey]; + } + }); + return uuid; +} ++ (nonnull NSString *)idfa +{ + static NSString *idfa; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; + }); + return idfa; +} + ++ (nonnull NSString *)systemUUID +{ + return [UIDevice currentDevice].identifierForVendor.UUIDString; +} + +@end diff --git a/BeeReel/Thirdparty/JXUUID/PDKeyChain.h b/BeeReel/Thirdparty/JXUUID/PDKeyChain.h new file mode 100755 index 0000000..45c8eb1 --- /dev/null +++ b/BeeReel/Thirdparty/JXUUID/PDKeyChain.h @@ -0,0 +1,31 @@ +// +// PDKeyChain.h +// PDKeyChain +// +// Created by Panda on 16/8/23. +// Copyright © 2016年 v2panda. All rights reserved. +// + +#import +#import + +@interface PDKeyChain : NSObject +/** + * 从 KeyChain 中读取存储的数据 + * + * @return NSDictionary + */ ++ (NSDictionary *)getKeyChainData; + ++ (id)objectForKey:(NSString *)key; ++ (void)setObject:(id)object forKey:(NSString *)key; ++ (void)removeObjectForKey:(NSString *)key; ++ (void)removeAllObjects; + + +/** + * 删除 KeyChain 信息 + */ ++ (void)keyChainDelete; + +@end diff --git a/BeeReel/Thirdparty/JXUUID/PDKeyChain.m b/BeeReel/Thirdparty/JXUUID/PDKeyChain.m new file mode 100755 index 0000000..b4a6c3d --- /dev/null +++ b/BeeReel/Thirdparty/JXUUID/PDKeyChain.m @@ -0,0 +1,100 @@ +// +// PDKeyChain.m +// PDKeyChain +// +// Created by Panda on 16/8/23. +// Copyright © 2016年 v2panda. All rights reserved. +// + +#import "PDKeyChain.h" + +static NSString * const kPDKeyChainKey = @"com.BeeReel.keychainKey"; + +@implementation PDKeyChain + ++ (void)keyChainDelete{ + [self delete:kPDKeyChainKey]; +} + ++ (NSDictionary *)getKeyChainData +{ + NSDictionary *dic = [self load:kPDKeyChainKey]; + if (!dic) { + dic = [NSDictionary dictionary]; + } + return dic; +} + ++ (void)setObject:(id)object forKey:(NSString *)key +{ + NSMutableDictionary *tempDic = [[self getKeyChainData] mutableCopy]; + [tempDic setObject:object forKey:key]; + [self save:kPDKeyChainKey data:tempDic]; +} ++ (id)objectForKey:(NSString *)key +{ + NSDictionary *tempDic = [self getKeyChainData]; + return tempDic[key]; +} ++ (void)removeObjectForKey:(NSString *)key +{ + NSMutableDictionary *tempDic = [[self getKeyChainData] mutableCopy]; + [tempDic removeObjectForKey:key]; + [self save:kPDKeyChainKey data:tempDic]; +} ++ (void)removeAllObjects +{ + NSMutableDictionary *tempDic = [[self getKeyChainData] mutableCopy]; + [tempDic removeAllObjects]; + [self save:kPDKeyChainKey data:tempDic]; +} + + + ++ (NSMutableDictionary *)getKeychainQuery:(NSString *)service { + return [NSMutableDictionary dictionaryWithObjectsAndKeys: + (id)kSecClassGenericPassword,(id)kSecClass, + service, (id)kSecAttrService, + service, (id)kSecAttrAccount, + (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible, + nil]; +} + ++ (void)save:(NSString *)service data:(id)data { + //Get search dictionary + NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; + //Delete old item before add new item + SecItemDelete((CFDictionaryRef)keychainQuery); + //Add new object to search dictionary(Attention:the data format) + [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData]; + //Add item to keychain with the search dictionary + SecItemAdd((CFDictionaryRef)keychainQuery, NULL); +} + ++ (id)load:(NSString *)service { + id ret = nil; + NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; + //Configure the search setting + //Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue + [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; + [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; + CFDataRef keyData = NULL; + if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) { + @try { + ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData]; + } @catch (NSException *e) { + NSLog(@"Unarchive of %@ failed: %@", service, e); + } @finally { + } + } + if (keyData) + CFRelease(keyData); + return ret; +} + ++ (void)delete:(NSString *)service { + NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; + SecItemDelete((CFDictionaryRef)keychainQuery); +} + +@end diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerConfig.h b/BeeReel/Thirdparty/WMZBanner/WMZBannerConfig.h new file mode 100644 index 0000000..c4279e3 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerConfig.h @@ -0,0 +1,109 @@ +// +// WMZBannerConfig.h +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + + + +#ifndef WMZBannerConfig_h +#define WMZBannerConfig_h + +#import + +//#if __has_include() +//#import +//#else +//#import "UIImageView+WebCache.h" +//#endif + +#define BANNERCOUNT 500 + +#define BannerWitdh [UIScreen mainScreen].bounds.size.width +#define BannerHeight [UIScreen mainScreen].bounds.size.height + +#define BannerWeakSelf(obj) __weak typeof(obj) weakObject = obj; +#define BannerStrongSelf(obj) __strong typeof(obj) strongObject = weakObject; + +#define BannerColor(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] + +#define WMZBannerPropStatementAndPropSetFuncStatement(propertyModifier,className, propertyPointerType, propertyName) \ +@property(nonatomic,propertyModifier)propertyPointerType propertyName; \ +- (className * (^) (propertyPointerType propertyName)) propertyName##Set; + +#define WMZBannerPropSetFuncImplementation(className, propertyPointerType, propertyName) \ +- (className * (^) (propertyPointerType propertyName))propertyName##Set{ \ +return ^(propertyPointerType propertyName) { \ +self->_##propertyName = propertyName; \ +return self; \ +}; \ +} + + +/* + * cell的block + */ +typedef UICollectionViewCell* (^BannerCellCallBlock)(NSIndexPath *indexPath,UICollectionView* collectionView,id model,UIImageView* bgImageView,NSArray*dataArr); + +/* + * 点击 + */ +typedef void (^BannerClickBlock)(id anyID,NSInteger index); + +/* + * 自定义pageControl + */ +typedef void (^BannerPageControl)(UIControl* pageControl); + +/* + * 点击 ,可获取居中cell + */ +typedef void (^BannerCenterClickBlock)(id anyID,NSInteger index,BOOL isCenter,UICollectionViewCell* cell); + +/* + * 滚动结束 + */ +typedef void (^BannerScrollEndBlock)(id anyID,NSInteger index,BOOL isCenter,UICollectionViewCell* cell); + +/* +* 滚动 +*/ +typedef void (^BannerScrollBlock)(CGPoint point); + +/* + * 自定义下划线 + */ +typedef void (^BannerSpecialLine)(UIView *line); + +/* + *cell动画的位置 + */ +typedef enum :NSInteger{ + BannerCellPositionCenter = 0, //居中 默认 + BannerCellPositionBottom = 1, //置底 + BannerCellPositionTop = 2, //顶部 +}BannerCellPosition; + + +/* + *特殊样式 + */ +typedef enum :NSInteger{ + SpecialStyleLine = 1, //下划线 + SpecialStyleFirstScale = 2, //首个变大效果 +}SpecialStyle; + +/* + *pageControl的位置 + */ +typedef enum :NSInteger{ + BannerControlCenter = 0, //居中 默认 + BannerControlLeft = 1, //左下 + BannerControlRight = 2, //右下 +}BannerControlPosition; + + + +#endif /* WMZBannerConfig_h */ diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerControl.h b/BeeReel/Thirdparty/WMZBanner/WMZBannerControl.h new file mode 100644 index 0000000..3efdc11 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerControl.h @@ -0,0 +1,26 @@ +// +// WMZBannerControl.h +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + +#import +#import "WMZBannerParam.h" +NS_ASSUME_NONNULL_BEGIN +@interface WMZBannerControl : UIControl +@property (nonatomic, strong) UIImage *currentImage; +@property (nonatomic, strong) UIImage *inactiveImage; +@property (nonatomic, assign) CGSize currentImageSize; +@property (nonatomic, assign) CGSize inactiveImageSize; +@property (nonatomic, assign) NSInteger numberOfPages; +@property (nonatomic, assign) NSInteger currentPage; +@property (nonatomic,strong) UIColor *currentPageIndicatorTintColor; +@property (nonatomic,strong) UIColor *pageIndicatorTintColor; +@property (nonatomic, strong) WMZBannerParam *param; +- (instancetype)initWithFrame:(CGRect)frame WithModel:(WMZBannerParam *)param; + +@end + +NS_ASSUME_NONNULL_END diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerControl.m b/BeeReel/Thirdparty/WMZBanner/WMZBannerControl.m new file mode 100644 index 0000000..97d0901 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerControl.m @@ -0,0 +1,170 @@ +// +// WMZBannerControl.m +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + +#import "WMZBannerControl.h" +#define bannerPointSize CGSizeMake(8,8) +@interface WMZBannerControl() +{ + NSInteger _numberOfPages; + NSInteger _currentPage; +} +@property(nonatomic,strong)NSMutableArray *imageArr; +@end +@implementation WMZBannerControl + +- (instancetype)initWithFrame:(CGRect)frame WithModel:(WMZBannerParam *)param{ + if (self = [super initWithFrame:frame]) { + self.param = param; + self.userInteractionEnabled = NO; + self.currentPageIndicatorTintColor = param.wBannerControlSelectColor; + self.pageIndicatorTintColor = param.wBannerControlColor; + if (param.wBannerControlImage) { + self.inactiveImage = [UIImage imageNamed:param.wBannerControlImage]; + self.inactiveImageSize = param.wBannerControlImageSize; + self.pageIndicatorTintColor = [UIColor clearColor]; + } + if (param.wBannerControlSelectImage) { + self.currentImage = [UIImage imageNamed:param.wBannerControlSelectImage]; + self.currentImageSize = param.wBannerControlSelectImageSize; + self.currentPageIndicatorTintColor = [UIColor clearColor]; + } + + [self resetFrame]; + + } + return self; +} + +- (void)setCurrentPage:(NSInteger)currentPage{ + _currentPage = currentPage; + [self updateDots]; +} + +- (void)setNumberOfPages:(NSInteger)numberOfPages{ + _numberOfPages = numberOfPages; + [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];\ + UIView *tempView = nil; + for (int i = 0; i +#import "WMZBannerParam.h" +NS_ASSUME_NONNULL_BEGIN + +@interface WMZBannerFadeLayout : UICollectionViewFlowLayout +@property(nonatomic,strong)WMZBannerParam *param; +@property(nonatomic,assign)BOOL right; +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param; +@end + +NS_ASSUME_NONNULL_END diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerFadeLayout.m b/BeeReel/Thirdparty/WMZBanner/WMZBannerFadeLayout.m new file mode 100644 index 0000000..50a3ea1 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerFadeLayout.m @@ -0,0 +1,97 @@ + +// +// WMZBannerFadeLayout.m +// WMZBanner +// +// Created by wmz on 2020/6/15. +// Copyright © 2020 wmz. All rights reserved. +// + +#import "WMZBannerFadeLayout.h" +@interface WMZBannerFadeLayout() +@property(nonatomic,assign)CGPoint collectionContenOffset; +@property(nonatomic,assign)CGSize collectionContenSize; +@property(nonatomic,assign)CGFloat last; +@end +@implementation WMZBannerFadeLayout +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param{ + if (self = [super init]) { + self.param = param; + } + return self; +} + +- (void)prepareLayout +{ + [super prepareLayout]; + self.collectionView.bounces = NO; + self.collectionView.pagingEnabled = YES; + self.itemSize = self.param.wItemSize; + self.minimumInteritemSpacing = (self.param.wFrame.size.height-self.param.wItemSize.height)/2; + self.minimumLineSpacing = self.param.wLineSpacing; + self.sectionInset = self.param.wSectionInset; + self.scrollDirection = self.param.wVertical? UICollectionViewScrollDirectionVertical + :UICollectionViewScrollDirectionHorizontal; +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { + return [self cardOverLapTypeInRect:rect]; +} + +//卡片重叠 +- (NSArray *)cardOverLapTypeInRect:(CGRect)rect{ + NSInteger itemsCount = [self.collectionView numberOfItemsInSection:0]; + if (itemsCount <= 0) { + return nil; + } + NSMutableArray *mArr = [[NSMutableArray alloc] init]; + if (self.param.wVertical) { + if (self.collectionView.contentOffset.y>self.last) { + self.right = YES; + }else if (self.collectionView.contentOffset.yself.last) { + self.right = YES; + }else if (self.collectionView.contentOffset.x +#import "WMZBannerParam.h" +NS_ASSUME_NONNULL_BEGIN + +@interface WMZBannerFlowLayout : UICollectionViewFlowLayout +@property(nonatomic,strong)WMZBannerParam *param; +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param; +@end + +NS_ASSUME_NONNULL_END diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerFlowLayout.m b/BeeReel/Thirdparty/WMZBanner/WMZBannerFlowLayout.m new file mode 100644 index 0000000..e85813e --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerFlowLayout.m @@ -0,0 +1,227 @@ +// +// WMZBannerFlowLayout.m +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + +#import "WMZBannerFlowLayout.h" +@interface WMZBannerFlowLayout(){ + CGSize factItemSize; +} +@end +@implementation WMZBannerFlowLayout +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param{ + if (self = [super init]) { + self.param = param; + } + return self; +} + +- (void)prepareLayout +{ + [super prepareLayout]; + + self.itemSize = self.param.wItemSize; + self.minimumInteritemSpacing = (self.param.wFrame.size.height-self.param.wItemSize.height)/2; + self.minimumLineSpacing = self.param.wLineSpacing; + self.sectionInset = self.param.wSectionInset; + + if ([self.collectionView isPagingEnabled]) { + self.scrollDirection = self.param.wVertical? UICollectionViewScrollDirectionVertical + :UICollectionViewScrollDirectionHorizontal; + }else{ + self.scrollDirection = UICollectionViewScrollDirectionHorizontal; + } +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { + return [self cardScaleTypeInRect:rect]; +} + +//卡片缩放 +- (NSArray *)cardScaleTypeInRect:(CGRect)rect{ + [self setUpIndex]; + NSArray *array = [self getCopyOfAttributes:[super layoutAttributesForElementsInRect:rect]]; + if (!self.param.wScale||self.param.wMarquee) { + return array; + } + CGRect visibleRect = CGRectZero; + visibleRect.origin = self.collectionView.contentOffset; + visibleRect.size = self.collectionView.bounds.size; + NSMutableArray *marr = [NSMutableArray new]; + NSInteger minIndex = 0; + CGFloat minCenterX = [(UICollectionViewLayoutAttributes*)array.firstObject center].x; + for (int i = 0; i=0&& + cellFrameInSuperview.origin.x<=self.collectionView.frame.size.width) { + if (minCenterX>cellFrameInSuperview.origin.x) { + minCenterX = cellFrameInSuperview.origin.x; + minIndex = i; + } + } + } + for (int i = 0; i * _Nullable bindings){ + return (evaluatedObject.representedElementCategory == UICollectionElementCategoryCell); + }]; + + NSArray *cellAttributes = [attributes filteredArrayUsingPredicate: cellAttributesPredicate]; + + UICollectionViewLayoutAttributes *currentAttributes; + + for (UICollectionViewLayoutAttributes *layoutAttributes in cellAttributes) + { + CGFloat itemHorizontalCenter = layoutAttributes.center.x; + if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offSetAdjustment)) + { + currentAttributes = layoutAttributes; + offSetAdjustment = itemHorizontalCenter - horizontalCenter; + } + } + + CGFloat nextOffset = proposedContentOffset.x + offSetAdjustment; + + proposedContentOffset.x = nextOffset; + CGFloat deltaX = proposedContentOffset.x - self.collectionView.contentOffset.x; + CGFloat velX = velocity.x; + + if (fabs(deltaX) <= FLT_EPSILON || fabs(velX) <= FLT_EPSILON || (velX > 0.0 && deltaX > 0.0) || (velX < 0.0 && deltaX < 0.0)) + { + + }else if (velocity.x > 0.0){ + NSArray *revertedArray = [[attributes reverseObjectEnumerator] allObjects]; + BOOL found = YES; + float proposedX = 0.0; + for (UICollectionViewLayoutAttributes *layoutAttributes in revertedArray) + { + if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) + { + CGFloat itemHorizontalCenter = layoutAttributes.center.x; + if (itemHorizontalCenter > proposedContentOffset.x) { + found = YES; + proposedX = nextOffset + (currentAttributes.frame.size.width / 2) + (layoutAttributes.frame.size.width / 2); + } else { + break; + } + } + } + + if (found) { + proposedContentOffset.x = proposedX; + proposedContentOffset.x += self.param.wLineSpacing; + } + } + else if (velocity.x < 0.0) + { + for (UICollectionViewLayoutAttributes *layoutAttributes in cellAttributes) + { + CGFloat itemHorizontalCenter = layoutAttributes.center.x; + if (itemHorizontalCenter > proposedContentOffset.x) + { + proposedContentOffset.x = nextOffset - ((currentAttributes.frame.size.width / 2) + (layoutAttributes.frame.size.width / 2)); + proposedContentOffset.x -= self.param.wLineSpacing; + break; + } + } + } + proposedContentOffset.y = 0.0; + + return proposedContentOffset; + +} + +@end diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerOverLayout.h b/BeeReel/Thirdparty/WMZBanner/WMZBannerOverLayout.h new file mode 100644 index 0000000..10b42a8 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerOverLayout.h @@ -0,0 +1,18 @@ +// +// WMZBannerOverLayout.h +// WMZBanner +// +// Created by wmz on 2019/12/18. +// Copyright © 2019 wmz. All rights reserved. +// + +#import +#import "WMZBannerParam.h" +NS_ASSUME_NONNULL_BEGIN + +@interface WMZBannerOverLayout : UICollectionViewFlowLayout +@property(nonatomic,strong)WMZBannerParam *param; +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param; +@end + +NS_ASSUME_NONNULL_END diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerOverLayout.m b/BeeReel/Thirdparty/WMZBanner/WMZBannerOverLayout.m new file mode 100644 index 0000000..3574942 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerOverLayout.m @@ -0,0 +1,158 @@ + +// +// WMZBannerOverLayout.m +// WMZBanner +// +// Created by wmz on 2019/12/18. +// Copyright © 2019 wmz. All rights reserved. +// +#import "WMZBannerOverLayout.h" +@interface WMZBannerOverLayout() +@property(nonatomic,assign)CGPoint collectionContenOffset; +@property(nonatomic,assign)CGSize collectionContenSize; +@property(nonatomic,assign)CGFloat last; +@property(nonatomic,assign)BOOL right; +@end +@implementation WMZBannerOverLayout +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param{ + if (self = [super init]) { + self.param = param; + self.collectionView.bounces = NO; + } + return self; +} + +- (void)prepareLayout +{ + [super prepareLayout]; + if (self.param.wCardOverLapCount<=0) { + self.param.wCardOverLapCount = 4; + } + self.collectionView.pagingEnabled = YES; + self.itemSize = self.param.wVertical? + CGSizeMake(self.param.wItemSize.width , (self.param.wItemSize.height - (self.param.wCardOverLapCount - 1)*self.param.wLineSpacing)): +// CGSizeMake(self.param.wItemSize.width - (self.param.wCardOverLapCount - 1)*self.param.wLineSpacing, self.param.wItemSize.height); + CGSizeMake(self.param.wItemSize.width, self.param.wItemSize.height); + self.minimumInteritemSpacing = (self.param.wCardOverLapCount - 1)*self.param.wLineSpacing*2; + self.minimumLineSpacing = (self.param.wCardOverLapCount - 1)*self.param.wLineSpacing*2; + self.sectionInset = self.param.wSectionInset; + self.scrollDirection = self.param.wVertical? UICollectionViewScrollDirectionVertical + :UICollectionViewScrollDirectionHorizontal; +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { + return [self cardOverLapTypeInRect:rect]; +} + +//卡片重叠 +- (NSArray *)cardOverLapTypeInRect:(CGRect)rect{ + + NSInteger itemsCount = [self.collectionView numberOfItemsInSection:0]; + if (itemsCount <= 0) { + return nil; + } + + self.param.myCurrentPath = self.param.wVertical? + MAX(floor ((int)self.collectionContenOffset.y / self.collectionContenSize.height ), 0): + MAX(floor((int)self.collectionContenOffset.x / self.collectionContenSize.width ), 0); + + self.param.overFactPath = self.param.wVertical? + MAX(ceil ((int)self.collectionContenOffset.y / self.collectionContenSize.height ), 0): + MAX(ceil((int)self.collectionContenOffset.x / self.collectionContenSize.width ), 0); + NSInteger minVisibleIndex = MAX(self.param.myCurrentPath, 0); + NSInteger contentOffset = self.param.wVertical? + self.collectionContenOffset.y:self.collectionContenOffset.x; + NSInteger collectionBounds = self.param.wVertical? + self.collectionContenSize.height:self.collectionContenSize.width; + CGFloat offset = contentOffset % collectionBounds; + CGFloat offsetProgress = offset / (self.param.wVertical?self.collectionContenSize.height:self.collectionContenSize.width)*1.0f; + NSInteger maxVisibleIndex = MAX(MIN(itemsCount - 1, self.param.myCurrentPath + self.param.wCardOverLapCount), minVisibleIndex); + NSMutableArray *mArr = [[NSMutableArray alloc] init]; + + for (NSInteger i = minVisibleIndex; i<=maxVisibleIndex; i++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0]; + UICollectionViewLayoutAttributes *attributes = [[self layoutAttributesForItemAtIndexPath:indexPath] copy]; + + NSInteger visibleIndex = MAX(indexPath.item - self.param.myCurrentPath + 1, 0); + attributes.size = self.itemSize; +// CGFloat topCardMidX = self.param.wVertical? +// (self.collectionContenOffset.y + self.collectionContenSize.height / 2): +// (self.collectionContenOffset.x + self.collectionContenSize.width / 2); + CGFloat topCardMidX = self.sectionInset.left + self.collectionContenOffset.x + self.itemSize.width / 2; + attributes.center = self.param.wVertical? + CGPointMake(self.collectionContenSize.width/2, topCardMidX + self.param.wLineSpacing * (visibleIndex - 1)): + CGPointMake(topCardMidX + self.param.wLineSpacing * (visibleIndex - 1), self.collectionContenSize.height/2); + attributes.zIndex = 925457662 - visibleIndex; + CGFloat scale = [self parallaxProgressForVisibleIndex:visibleIndex offsetProgress:offsetProgress minScale:self.param.wScaleFactor]; +// attributes.transform = CGAffineTransformMakeScale(scale, scale); + CGFloat angle = M_PI / 180 * 140 * (1 - scale); + + attributes.transform = CGAffineTransformMakeRotation(-angle); + + if (visibleIndex == 1) { + if (self.param.wVertical) { + if (minVisibleIndex != maxVisibleIndex) { + if (self.collectionContenOffset.y >= 0) { + attributes.center = CGPointMake(attributes.center.x, attributes.center.y - offset); + }else{ + attributes.center = CGPointMake(attributes.center.x , attributes.center.y + attributes.size.height * (1 - scale)/2 - self.param.wLineSpacing * offsetProgress); + } + } + }else{ + if (minVisibleIndex != maxVisibleIndex) { + if (self.collectionContenOffset.x >= 0) { + attributes.center = CGPointMake(attributes.center.x - offset, attributes.center.y); + }else{ + attributes.center = CGPointMake(attributes.center.x + attributes.size.width * (1 - scale)/2 - self.param.wLineSpacing * offsetProgress, attributes.center.y); + } + } + } + if (self.param.wCardOverAlphaOpen) { + attributes.alpha = MAX(1-offsetProgress, MAX(self.param.wCardOverMinAlpha, 0)); + } + }else if (visibleIndex == self.param.wCardOverLapCount + 1){ + attributes.center = self.param.wVertical? + CGPointMake(attributes.center.x, attributes.center.y + attributes.size.height * (1 - scale)/2 - self.param.wLineSpacing): + CGPointMake(attributes.center.x + attributes.size.width * (1 - scale)/2 - self.param.wLineSpacing, attributes.center.y); + if (self.param.wCardOverAlphaOpen) { + attributes.alpha = MAX(offsetProgress, MAX(self.param.wCardOverMinAlpha, 0)); + } + }else{ + attributes.center = self.param.wVertical? + CGPointMake(attributes.center.x , attributes.center.y + attributes.size.height * (1 - scale)/2 - self.param.wLineSpacing * offsetProgress): + CGPointMake(attributes.center.x + attributes.size.width * (1 - scale)/2 - self.param.wLineSpacing * offsetProgress, attributes.center.y); + if (self.param.wCardOverAlphaOpen) { + attributes.alpha = MAX(offsetProgress, MAX(self.param.wCardOverMinAlpha*2, 0)); + } + } + [mArr addObject:attributes]; + } + return mArr; +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { + return YES; +} + +- (CGFloat)parallaxProgressForVisibleIndex:(NSInteger)visibleIndex + offsetProgress:(CGFloat)offsetProgress + minScale:(CGFloat)minScale +{ + if (visibleIndex <= self.param.wCardOverLapCount) { + CGFloat step = (1.0 - minScale) / (self.param.wCardOverLapCount-1)*1.0; + return (1.0 - (visibleIndex - 1) * step + step * offsetProgress); + } else { + CGFloat step = (1.0 - minScale) / (self.param.wCardOverLapCount-1)*1.0; + return (1.0 - (visibleIndex - 2) * step + step * 0); + } +} + +- (CGSize)collectionContenSize{ + return CGSizeMake((int)self.collectionView.bounds.size.width, (int)self.collectionView.bounds.size.height); +} + +- (CGPoint)collectionContenOffset{ + return CGPointMake((int)self.collectionView.contentOffset.x, (int)self.collectionView.contentOffset.y); +} + +@end diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerParam.h b/BeeReel/Thirdparty/WMZBanner/WMZBannerParam.h new file mode 100644 index 0000000..af7c7c1 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerParam.h @@ -0,0 +1,142 @@ +// +// WMZBannerParam.h +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + +#import "WMZBannerConfig.h" +NS_ASSUME_NONNULL_BEGIN + +@interface WMZBannerParam : NSObject +/* =========================================Attributes==========================================*/ + +//布局方式 frame 必传 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGRect, wFrame) +//数据源 必传 +WMZBannerPropStatementAndPropSetFuncStatement(strong, WMZBannerParam, NSArray*, wData) +//特殊样式 default 无 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, SpecialStyle, wSpecialStyle) + +//淡入淡出 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wFadeOpen) +//开启缩放 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wScale) +//开启卡片重叠模式 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wCardOverLap) +//卡片重叠模式开启偏移透明度变化 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wCardOverAlphaOpen) +//叠加模式透明度最小值 defalt 0.1 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wCardOverMinAlpha) +//卡片重叠显示个数 default 4 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, NSInteger, wCardOverLapCount) +//背景毛玻璃效果 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wEffect) +//隐藏pageControl default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wHideBannerControl) +//是否允许手势滑动 default YES +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wCanFingerSliding) +//图片不变形铺满 默认 YES +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wImageFill) +//开启无线滚动 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wRepeat) +//开启自动滚动 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wAutoScroll) +//纵向(cell全屏的时候有效) default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wVertical) +//跑马灯(文字效果) default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wMarquee) +//点击左右居中 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wClickCenter) +//中间视图放最上面 default NO +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BOOL, wZindex) +//整体间距 默认UIEdgeInsetsMake(0,0, 0, 0) +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, UIEdgeInsets, wSectionInset) +//自定义图片圆角 default 5 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wCustomImageRadio) +//整体视图缩放系数 default 1 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wScreenScale) +//毛玻璃背景的高度 (视图的高度*倍数) default 1 范围0~1 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wEffectHeight) +//缩放系数 数值越大缩放越大 default 0.5 卡片叠加效果时默认为0.8 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wScaleFactor) +//左右的透明度 default 1 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wAlpha) +//垂直缩放 数值越大缩放越小 default 400 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wActiveDistance) +//item的size default 视图的宽高 item的width最小为父视图的一半 (为了保证同屏最多显示3个 减少不必要的bug) +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGSize, wItemSize) +//item的之间的间距 default 0 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, int, wLineSpacing) +//滑动的时候偏移的距离 以倍数计算 default 0.5 正中间 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wContentOffsetX) +//左右相邻item的中心点 default BannerCellPositionCenter +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BannerCellPosition, wPosition) +//占位图片 默认 - +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, NSString*, wPlaceholderImage) +//数据源的图片字段 默认 icon +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, NSString*, wDataParamIconName) +//滚动减速时间 default UIScrollViewDecelerationRateFast +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, UIScrollViewDecelerationRate,wDecelerationRate) +//自动滚动间隔时间 default 3.0f +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wAutoScrollSecond) +//默认移动到第几个 default 0 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, NSInteger, wSelectIndex) +//自定义cell内容 默认是Collectioncell类 +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, BannerCellCallBlock, wMyCell) +//特殊样式SpecialLine 自定义下划线 +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, BannerSpecialLine, wSpecialCustumLine) +//自定义cell的类名 自定义视图必传 不然会crash +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, NSString*, wMyCellClassName) +//自定义cell的类名 自定义视图必传 不然会crash 和上面的属性wMyCellClassName 二选一 此属性可以传数组 +WMZBannerPropStatementAndPropSetFuncStatement(strong, WMZBannerParam, id, wMyCellClassNames) +//自定义xib cell的类名 自定义视图必传 不然会crash +WMZBannerPropStatementAndPropSetFuncStatement(strong, WMZBannerParam, id, wXibCellClassNames) +//系统的圆点颜色 default ffffff +WMZBannerPropStatementAndPropSetFuncStatement(strong, WMZBannerParam, UIColor*, wBannerControlColor) +//系统的圆点选中颜色 default orange +WMZBannerPropStatementAndPropSetFuncStatement(strong, WMZBannerParam, UIColor*, wBannerControlSelectColor) +//自定义安全的圆点图标 default - +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, NSString*, wBannerControlImage) +//自定义安全的选中圆点图标 default - +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, NSString*, wBannerControlSelectImage) +//自定义安全的圆点图片圆角 default ImageSize/2 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wBannerControlImageRadius) +//自定义安全的圆点图标的size default (5,5) +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGSize, wBannerControlImageSize) +//自定义安全的选中圆点图标的size (10,5) +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGSize, wBannerControlSelectImageSize) +//自定义圆点的间距 default 3 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wBannerControlSelectMargin) +//自定义pageControl +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, BannerPageControl, wCustomControl) +//pageControl的位置 default BannerControlCenter +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, BannerControlPosition,wBannerControlPosition) +//跑马灯文字颜色 default red +WMZBannerPropStatementAndPropSetFuncStatement(strong, WMZBannerParam, UIColor*, wMarqueeTextColor) +//跑马灯速度 default 0.5 +WMZBannerPropStatementAndPropSetFuncStatement(assign, WMZBannerParam, CGFloat, wMarqueeRate) +/* =========================================Attributes==========================================*/ + +/* =========================================Events==============================================*/ +WMZBannerParam * BannerParam(void); +//点击方法 +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, BannerClickBlock, wEventClick) +//点击方法 可获取居中cell +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, BannerCenterClickBlock,wEventCenterClick) +//每次滚动结束都会调用 最好是关闭自动滚动的场景使用 +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, BannerScrollEndBlock, wEventScrollEnd) +//正在滚动 +WMZBannerPropStatementAndPropSetFuncStatement(copy, WMZBannerParam, BannerScrollBlock, wEventDidScroll) +/* =========================================Events==============================================*/ + +/* =========================================custom==============================================*/ +@property(nonatomic,assign)NSInteger myCurrentPath; + +@property(nonatomic,assign)NSInteger overFactPath; +/* =========================================custom==============================================*/ + +@end + +NS_ASSUME_NONNULL_END diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerParam.m b/BeeReel/Thirdparty/WMZBanner/WMZBannerParam.m new file mode 100644 index 0000000..c49e3cd --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerParam.m @@ -0,0 +1,106 @@ +// +// WMZBannerParam.m +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + +#import "WMZBannerParam.h" + +@implementation WMZBannerParam + +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGRect, wFrame) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSArray*, wData) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wScaleFactor) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wCardOverMinAlpha) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wCardOverAlphaOpen) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wFadeOpen) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wEffect) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wVertical) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wImageFill) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wScale) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wRepeat) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wAutoScroll) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wHideBannerControl) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wCanFingerSliding) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wMarquee) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wCardOverLap) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wZindex) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BOOL, wClickCenter) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSInteger, wCardOverLapCount) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wActiveDistance) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGSize, wItemSize) +WMZBannerPropSetFuncImplementation(WMZBannerParam, int, wLineSpacing) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wEffectHeight) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wContentOffsetX) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wScreenScale) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerCellPosition, wPosition) +WMZBannerPropSetFuncImplementation(WMZBannerParam, SpecialStyle, wSpecialStyle) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSString*, wPlaceholderImage) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSInteger, wSelectIndex) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSString*, wMyCellClassName) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerCellCallBlock, wMyCell) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerClickBlock, wEventClick) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerScrollEndBlock, wEventScrollEnd) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerCenterClickBlock, wEventCenterClick) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerScrollBlock, wEventDidScroll) +WMZBannerPropSetFuncImplementation(WMZBannerParam, UIColor*, wBannerControlColor) +WMZBannerPropSetFuncImplementation(WMZBannerParam, UIColor*, wBannerControlSelectColor) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSString*, wBannerControlImage) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSString*, wDataParamIconName) +WMZBannerPropSetFuncImplementation(WMZBannerParam, NSString*, wBannerControlSelectImage) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGSize, wBannerControlImageSize) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGSize, wBannerControlSelectImageSize) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wAutoScrollSecond) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wAlpha) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wBannerControlImageRadius) +WMZBannerPropSetFuncImplementation(WMZBannerParam, UIEdgeInsets, wSectionInset) +WMZBannerPropSetFuncImplementation(WMZBannerParam, UIScrollViewDecelerationRate, wDecelerationRate) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerControlPosition, wBannerControlPosition) +WMZBannerPropSetFuncImplementation(WMZBannerParam, UIColor*, wMarqueeTextColor) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerPageControl, wCustomControl) +WMZBannerPropSetFuncImplementation(WMZBannerParam, BannerSpecialLine, wSpecialCustumLine) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wBannerControlSelectMargin) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wMarqueeRate) +WMZBannerPropSetFuncImplementation(WMZBannerParam, CGFloat, wCustomImageRadio) +WMZBannerPropSetFuncImplementation(WMZBannerParam, id, wMyCellClassNames) +WMZBannerPropSetFuncImplementation(WMZBannerParam, id, wXibCellClassNames) +WMZBannerParam * BannerParam(void){ + return [WMZBannerParam new]; +} + +- (instancetype)init{ + if (self = [super init]) { + _wAlpha = 1; + _wScaleFactor = 0.5f; + _wLineSpacing = 0.0f; + _wContentOffsetX = 0.5f; + _wAutoScrollSecond = 3.0f; + _wPosition = BannerCellPositionCenter; + _wActiveDistance = 400.0f; + _wScale = NO; + _wRepeat = NO; + _wSelectIndex = 0; + _wImageFill = YES; + _wBannerControlColor = [UIColor whiteColor]; + _wBannerControlSelectColor = [UIColor orangeColor]; + _wBannerControlImageSize = CGSizeMake(10, 10); + _wBannerControlSelectImageSize = CGSizeMake(10, 10); + _wCanFingerSliding = YES; + _wSectionInset = UIEdgeInsetsMake(0,0, 0, 0); + _wDecelerationRate = 0.1; + _wScreenScale = 1; + _wMarqueeTextColor = [UIColor redColor]; + _wEffectHeight = 1; + _wDataParamIconName = @"icon"; + _wBannerControlSelectMargin = 3; + _wMarqueeRate = 0.5; + _wCardOverLapCount = 4; + _wCardOverMinAlpha = 0.1; + _wCustomImageRadio = 5.0f; + } + return self; +} + +@end diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerView.h b/BeeReel/Thirdparty/WMZBanner/WMZBannerView.h new file mode 100644 index 0000000..f84fafa --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerView.h @@ -0,0 +1,55 @@ +// +// WMZBannerView.h +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + +#import "WMZBannerParam.h" +#import +NS_ASSUME_NONNULL_BEGIN + +@interface WMZBannerView : UIView +//背景图 +@property(strong,nonatomic)UIImageView *bgImgView; + +/** + * 调用方法 + * + */ +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param withView:(UIView*)parentView; + +/** + * 调用方法 + * + */ +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param; + +- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier; +/** + * 更新UI + * + */ +- (void)updateUI; + + +/** +* 手动调用滚动 +* +*/ +- (void)scrolToPath:(NSIndexPath*)path animated:(BOOL)animated; + +@end + +@interface Collectioncell : UICollectionViewCell +@property(nonatomic,strong)UIImageView *icon; +@property(nonatomic,strong)WMZBannerParam *param; +@end + +@interface CollectionTextCell : UICollectionViewCell +@property(nonatomic,strong)UILabel *label; +@property(nonatomic,strong)WMZBannerParam *param; +@end + +NS_ASSUME_NONNULL_END diff --git a/BeeReel/Thirdparty/WMZBanner/WMZBannerView.m b/BeeReel/Thirdparty/WMZBanner/WMZBannerView.m new file mode 100644 index 0000000..80a8ce9 --- /dev/null +++ b/BeeReel/Thirdparty/WMZBanner/WMZBannerView.m @@ -0,0 +1,701 @@ + + +// +// WMZBannerView.m +// WMZBanner +// +// Created by wmz on 2019/9/6. +// Copyright © 2019 wmz. All rights reserved. +// + +#import "WMZBannerView.h" +#import "WMZBannerFlowLayout.h" +#import "WMZBannerControl.h" +#import "WMZBannerOverLayout.h" +#import "WMZBannerFadeLayout.h" +#import +@interface WMZBannerView(){ + BOOL beganDragging; + CGFloat marginTime; +} +@property(strong,nonatomic)UICollectionView *myCollectionV; +@property(strong,nonatomic)UICollectionViewFlowLayout *flowL ; +@property(strong,nonatomic)WMZBannerControl *bannerControl ; +@property(strong,nonatomic)NSArray *data; +@property(strong,nonatomic)WMZBannerParam *param; +@property(strong,nonatomic)NSTimer *timer; +@property(strong,nonatomic)UIView *line; +@property(assign,nonatomic)NSInteger lastIndex; +@end +@implementation WMZBannerView +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param withView:(UIView*)parentView{ + if (self = [super init]) { + self.param = param; + if (parentView) { + [parentView addSubview:self]; + } + self.param.wFrame = CGRectMake(self.param.wFrame.origin.x, + self.param.wFrame.origin.y, + (int)self.param.wFrame.size.width, + (int)self.param.wFrame.size.height); + [self setFrame:self.param.wFrame]; + self.data = [NSArray arrayWithArray:self.param.wData]; + [self setUp]; + } + return self; +} + +/** + * 调用方法 + * + */ +- (instancetype)initConfigureWithModel:(WMZBannerParam *)param{ + if (self = [super init]) { + self.param = param; + self.param.wFrame = CGRectMake(self.param.wFrame.origin.x, + self.param.wFrame.origin.y, + (int)self.param.wFrame.size.width, + (int)self.param.wFrame.size.height); + [self setFrame:self.param.wFrame]; + self.data = [NSArray arrayWithArray:self.param.wData]; + [self setUp]; + } + return self; +} + + +- (void)updateUI{ + self.data = [NSArray arrayWithArray:self.param.wData]; + [self resetCollection]; +} + + +- (void)resetCollection{ + self.bannerControl.frame = CGRectMake((self.bounds.size.width - 60)/2 , self.bounds.size.height - 30,60, 30); + self.bannerControl.numberOfPages = self.data.count; + self.bannerControl.hidden = self.param.wHideBannerControl; + if (self.data.count == 1) { + self.bannerControl.hidden = YES; + } + [UIView animateWithDuration:0.0 animations:^{ + [self.myCollectionV reloadData]; + if (self.param.wSelectIndex>=0|| self.param.wRepeat) { + NSIndexPath *path = [NSIndexPath indexPathForRow: self.param.wRepeat?((BANNERCOUNT/2)*self.data.count+self.param.wSelectIndex):self.param.wSelectIndex inSection:0]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self scrolToPath:path animated:NO]; + self.bannerControl.currentPage = self.param.wSelectIndex; + self.param.myCurrentPath = self.param.wRepeat?((BANNERCOUNT/2)*self.data.count+self.param.wSelectIndex):self.param.wSelectIndex; + if (self.param.wAutoScroll) { + [self createTimer]; + }else{ + [self cancelTimer]; + } + }); + } + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self scrollEnd:[NSIndexPath indexPathForRow: self.param.wRepeat?((BANNERCOUNT/2)*self.data.count+self.param.wSelectIndex):self.param.wSelectIndex inSection:0]]; + }); + } completion:^(BOOL finished) {}]; + + + if (self.param.wSpecialStyle == SpecialStyleLine&&self.param.wData.count) { + [self addSubview:self.line]; + self.line.hidden = NO; + self.line.backgroundColor = [UIColor redColor]; + if (self.param.wSpecialCustumLine) { + self.param.wSpecialCustumLine(self.line); + } + + CGFloat lineHeight = self.line.frame.size.height?:2; + CGFloat lineWidth = self.param.wFrame.size.width/self.param.wData.count; + self.line.frame = CGRectMake(0, self.param.wFrame.size.height -lineHeight, lineWidth, lineHeight); + }else{ + self.line.hidden = YES; + } + + +} + +- (void)setUp{ + + if (self.data&&self.data.count==1) { + self.param.wRepeat = NO; + self.param.wAutoScroll = NO; + } + + if (self.param.wMarquee) { + self.param.wAutoScroll = YES; + self.param.wHideBannerControl = YES; + marginTime = 0.005; + self.param.wRepeat = YES; + } + self.param.wFrame = CGRectIntegral(self.param.wFrame); + if (self.param.wScreenScale<1&&self.param.wScreenScale>0) { + CGRect rect = self.param.wFrame; + rect.origin.x = rect.origin.x * self.param.wScreenScale; + rect.origin.y = rect.origin.y * self.param.wScreenScale; + rect.size.width = rect.size.width * self.param.wScreenScale; + rect.size.height = rect.size.height * self.param.wScreenScale; + self.param.wFrame = rect; + self.frame = self.param.wFrame; + + CGSize size = self.param.wItemSize; + size.width *= self.param.wScreenScale; + size.height *= self.param.wScreenScale; + self.param.wItemSize = size; + + self.param.wLineSpacing*=self.param.wScreenScale; + + UIEdgeInsets sets = self.param.wSectionInset; + sets.top*=self.param.wScreenScale; + sets.right*=self.param.wScreenScale; + sets.bottom*=self.param.wScreenScale; + sets.left*=self.param.wScreenScale; + self.param.wSectionInset = sets; + } + if (self.param.wItemSize.height == 0 || self.param.wItemSize.width == 0 ) { + self.param.wItemSize = CGSizeMake(self.frame.size.width, self.frame.size.height); + } + + else if(self.param.wItemSize.height>self.frame.size.height){ + self.param.wItemSize = CGSizeMake(self.param.wItemSize.width, self.frame.size.height); + }else if(self.param.wItemSize.width>self.frame.size.width){ + self.param.wItemSize = CGSizeMake(self.frame.size.width, self.param.wItemSize.height); + } + int width = self.param.wItemSize.width; + int height = self.param.wItemSize.height; + self.param.wItemSize = CGSizeMake(width, height); + + if (self.param.wFadeOpen) { + self.flowL = [[WMZBannerFadeLayout alloc] initConfigureWithModel:self.param]; + }else if (self.param.wCardOverLap) { + if (self.param.wScaleFactor == 0.5) { + self.param.wScaleFactor = 0.8f; + } + self.flowL = [[WMZBannerOverLayout alloc] initConfigureWithModel:self.param]; + }else{ + self.flowL = [[WMZBannerFlowLayout alloc] initConfigureWithModel:self.param]; + } + + + [self addSubview:self.myCollectionV]; + self.myCollectionV.scrollEnabled = self.param.wCanFingerSliding; + [self.myCollectionV registerClass:[Collectioncell class] forCellWithReuseIdentifier:NSStringFromClass([Collectioncell class])]; + [self.myCollectionV registerClass:[CollectionTextCell class] forCellWithReuseIdentifier:NSStringFromClass([CollectionTextCell class])]; + if (self.param.wMyCellClassName) { + [self.myCollectionV registerClass:NSClassFromString(self.param.wMyCellClassName) forCellWithReuseIdentifier:self.param.wMyCellClassName]; + } + if (self.param.wMyCellClassNames) { + if ([self.param.wMyCellClassNames isKindOfClass:[NSString class]]) { + [self.myCollectionV registerClass:NSClassFromString(self.param.wMyCellClassNames) forCellWithReuseIdentifier:self.param.wMyCellClassNames]; + }else if ([self.param.wMyCellClassNames isKindOfClass:[NSArray class]]){ + [(NSArray*)self.param.wMyCellClassNames enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:[NSString class]]) { + [self.myCollectionV registerClass:NSClassFromString(obj) forCellWithReuseIdentifier:obj]; + } + }]; + } + } + + if (self.param.wXibCellClassNames) { + if ([self.param.wXibCellClassNames isKindOfClass:[NSString class]]) { + [self.myCollectionV registerNib:[UINib nibWithNibName:self.param.wXibCellClassNames bundle:nil] forCellWithReuseIdentifier:self.param.wXibCellClassNames]; + }else if ([self.param.wXibCellClassNames isKindOfClass:[NSArray class]]){ + [(NSArray*)self.param.wXibCellClassNames enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:[NSString class]]) { + [self.myCollectionV registerNib:[UINib nibWithNibName:obj bundle:nil] forCellWithReuseIdentifier:obj]; + } + }]; + } + } + + self.myCollectionV.pagingEnabled = (self.param.wItemSize.width == self.myCollectionV.frame.size.width && self.param.wLineSpacing == 0)||self.param.wVertical; + if ([self.myCollectionV isPagingEnabled]) { + self.myCollectionV.decelerationRate = UIScrollViewDecelerationRateNormal; + } + + self.bannerControl = [[WMZBannerControl alloc]initWithFrame:CGRectMake((self.bounds.size.width - 60)/2 , self.bounds.size.height - 30,60, 30) WithModel:self.param]; + [self addSubview:self.bannerControl]; + + self.bgImgView = [UIImageView new]; + self.bgImgView.contentMode = self.param.wImageFill?UIViewContentModeScaleAspectFill:UIViewContentModeScaleToFill; + [self addSubview:self.bgImgView]; + [self sendSubviewToBack:self.bgImgView]; + self.bgImgView.hidden = !self.param.wEffect; + self.bgImgView.layer.masksToBounds = YES; + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + [self.bgImgView addSubview:effectView]; + + self.myCollectionV.frame = self.bounds; + if (self.param.wCustomControl) { + self.param.wCustomControl(self.bannerControl); + } + self.bgImgView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height*self.param.wEffectHeight); + effectView.frame = self.bgImgView.bounds; + [self resetCollection]; + +} + +- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier { + [self.myCollectionV registerClass:cellClass forCellWithReuseIdentifier:identifier]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ + NSInteger index = self.param.wRepeat?indexPath.row%self.data.count:indexPath.row; + id dic = self.data[index]; + UICollectionViewCell *tmpCell = nil; + if (self.param.wMyCell) { + tmpCell = self.param.wMyCell([NSIndexPath indexPathForRow:index inSection:indexPath.section], collectionView, dic,self.bgImgView,self.data); + }else{ + //默认视图 + Collectioncell *cell = (Collectioncell *)[collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([Collectioncell class]) forIndexPath:indexPath]; + cell.param = self.param; + if ([dic isKindOfClass:[NSDictionary class]]) { + [self setIconData:cell.icon withData:dic[self.param.wDataParamIconName]]; + }else{ + [self setIconData:cell.icon withData:dic]; + } + tmpCell = cell; + cell.contentView.layer.cornerRadius = self.param.wCustomImageRadio; + } + return tmpCell; +} + +- (void)setIconData:(UIImageView*)icon withData:(id)data{ + if (!data) return; + if ([data isKindOfClass:[NSString class]]) { + if ([(NSString*)data hasPrefix:@"http"]) { +// [icon sd_setImageWithURL:[NSURL URLWithString:(NSString*)data] placeholderImage:self.param.wPlaceholderImage?[UIImage imageNamed:self.param.wPlaceholderImage]:nil]; + }else{ + icon.image = [UIImage imageNamed:(NSString*)data]; + } + } +} + + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return self.param.wRepeat?self.data.count*BANNERCOUNT:self.data.count; + +} + +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ + if (self.param.wEventClick) { + NSInteger index = self.param.wRepeat?indexPath.row%self.data.count:indexPath.row; + id dic = self.data[index]; + self.param.wEventClick(dic, index); + } + + if (self.param.wEventCenterClick) { + NSInteger index = self.param.wRepeat?indexPath.row%self.data.count:indexPath.row; + id dic = self.data[index]; + BOOL center = [self checkCellInCenterCollectionView:collectionView AtIndexPath:indexPath]; + UICollectionViewCell *currentCell = (UICollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath]; + self.param.wEventCenterClick(dic, index,center,currentCell); + } + if (self.param.wClickCenter) { + NSArray *visibleCellIndex = [collectionView visibleCells]; + NSArray *sortedIndexPaths = [visibleCellIndex sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { + NSIndexPath *path1 = (NSIndexPath *)[collectionView indexPathForCell:obj1]; + NSIndexPath *path2 = (NSIndexPath *)[collectionView indexPathForCell:obj2]; + return [path1 compare:path2]; + }]; + if (sortedIndexPaths.count>0) { + NSInteger center = sortedIndexPaths.count/2; + UICollectionViewCell *tmpCell = [collectionView cellForItemAtIndexPath:indexPath]; + for (int i = 0; i < sortedIndexPaths.count; i++) { + UICollectionViewCell *cell = sortedIndexPaths[i]; + if (cell == tmpCell) { + NSIndexPath *nextIndexPath = nil; + if (i>center || i self.data.count*BANNERCOUNT-1):(path.row> self.data.count-1)){ + [self cancelTimer]; + return; + } + if (self.data.count==0) return; + if (self.param.wCardOverLap||self.param.wFadeOpen) { + [self.myCollectionV setContentOffset: self.param.wVertical? + CGPointMake(0, path.row *self.myCollectionV.bounds.size.height): + CGPointMake(path.row *self.myCollectionV.bounds.size.width, 0) + animated:animated]; + }else{ + if ([self.myCollectionV isPagingEnabled]) { + [self.myCollectionV scrollToItemAtIndexPath:path atScrollPosition: + self.param.wVertical?UICollectionViewScrollPositionCenteredVertically: + UICollectionViewScrollPositionCenteredHorizontally animated:animated]; + }else{ + [self.myCollectionV scrollToItemAtIndexPath:path atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated]; + } + } + + if ([self.myCollectionV isPagingEnabled]||self.param.wCardOverLap) return; + if(self.param.wContentOffsetX>0.5){ + self.myCollectionV.contentOffset = CGPointMake(self.myCollectionV.contentOffset.x-(self.param.wContentOffsetX-0.5)*self.myCollectionV.frame.size.width, self.myCollectionV.contentOffset.y); + }else if(self.param.wContentOffsetX<0.5){ + self.myCollectionV.contentOffset = CGPointMake(self.myCollectionV.contentOffset.x+self.myCollectionV.frame.size.width *(0.5-self.param.wContentOffsetX), self.myCollectionV.contentOffset.y); + } +} + + +//定时器 +- (void)createTimer{ + if (!self.timer) { + SEL sel = NSSelectorFromString(self.param.wMarquee?@"autoMarqueenScrollAction":@"autoScrollAction"); + self.timer = [NSTimer scheduledTimerWithTimeInterval:self.param.wMarquee?marginTime: self.param.wAutoScrollSecond target:[YYTextWeakProxy proxyWithTarget:self] selector:sel userInfo:nil repeats:YES]; + [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; + } +} + +//定时器方法 自动滚动 +- (void)autoScrollAction{ + if (beganDragging) return; + if (!self.timer) return; + if (!self.superview) return; + if (!self.param.wAutoScroll) { + [self cancelTimer]; + return; + } + self.param.myCurrentPath+=1; + if (self.param.wRepeat&& self.param.myCurrentPath == (self.data.count*BANNERCOUNT - 1)) { + self.param.myCurrentPath = 0; + } + else if(!self.param.wRepeat&& self.param.myCurrentPath == self.data.count){ + [self cancelTimer]; + return; + } + NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem: self.param.myCurrentPath inSection:0]; + [self scrolToPath:nextIndexPath animated:YES]; +} + +//定时器方法 跑马灯 +- (void)autoMarqueenScrollAction{ + if (!self.timer) return; + if (!self.superview) return; + if (!self.param.wAutoScroll) { + [self cancelTimer]; + return; + } + NSValue *value = nil; + if (self.param.wVertical) { + CGFloat OffsetY = self.myCollectionV.contentOffset.y + self.param.wMarqueeRate; + if (OffsetY >self.myCollectionV.contentSize.height) { + OffsetY = self.myCollectionV.contentSize.height/2; + } + value = [NSValue valueWithCGPoint:CGPointMake(self.myCollectionV.contentOffset.x, OffsetY)]; + }else{ + CGFloat OffsetX = self.myCollectionV.contentOffset.x + self.param.wMarqueeRate; + if (OffsetX >self.myCollectionV.contentSize.width) { + OffsetX = self.myCollectionV.contentSize.width/2; + } + value = [NSValue valueWithCGPoint:CGPointMake(OffsetX, self.myCollectionV.contentOffset.y)]; + } + [self.myCollectionV setContentOffset:value.CGPointValue]; +} + +//定时器销毁 +- (void)cancelTimer{ + if (self.timer) { + [self.timer invalidate]; + self.timer = nil; + } +} + +//开始拖动 +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ + beganDragging = YES; + if (!self.param.wMarquee) { + if (self.param.wAutoScroll) { + [self cancelTimer]; + } + }else{ + [self cancelTimer]; + [self performSelector:@selector(createTimer) withObject:nil afterDelay:self.param.wAutoScrollSecond]; + } +} + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ + NSInteger index = 0; + if (self.param.wCardOverLap||self.param.wFadeOpen) { + if ([self.myCollectionV isPagingEnabled]&&!self.param.wMarquee) { + index = self.param.myCurrentPath; + } + }else{ + if ([self.myCollectionV isPagingEnabled]&&!self.param.wMarquee) { + index = self.param.wVertical? + scrollView.contentOffset.y/scrollView.frame.size.height: + scrollView.contentOffset.x/scrollView.frame.size.width; + self.param.myCurrentPath = index; + }else{ + index = self.param.myCurrentPath; + } + } + self.bannerControl.currentPage = self.param.wRepeat?index %self.data.count:index; + if (self.param.wEventDidScroll) { + self.param.wEventDidScroll(scrollView.contentOffset); + } + [self setUpSpecialFrame]; +} + +//拖动结束 +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ + beganDragging = NO; +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ + if (!self.param.wMarquee) { + if (![self.myCollectionV isPagingEnabled]) { + self.bannerControl.currentPage = self.param.wRepeat?self.param.myCurrentPath%self.data.count:self.param.myCurrentPath; + } + if (self.param.wAutoScroll) { + [self createTimer]; + } + [self setUpSpecialFrame]; + [self scrollEnd:[NSIndexPath indexPathForRow:self.param.myCurrentPath inSection:0]]; + [self fadeAction]; + } +} + +- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{ + if (self.param.wCardOverLap) { + self.param.myCurrentPath = self.param.wVertical? + MAX(floor(scrollView.contentOffset.y / scrollView.bounds.size.height ), 0): + MAX(floor(scrollView.contentOffset.x / scrollView.bounds.size.width ), 0); + } + [self scrollEnd:[NSIndexPath indexPathForRow:self.param.myCurrentPath inSection:0]]; + [self setUpSpecialFrame]; + [self fadeAction]; +} + +- (void)scrollEnd:(NSIndexPath*)indexPath{ + if (!self.data.count) return; + if (self.param.wMarquee) return; + NSInteger current = MAX( self.param.wCardOverLap?self.param.overFactPath:self.param.myCurrentPath, 0); + NSInteger index = self.param.wRepeat?current%self.data.count:current; + if (index>self.data.count-1) { + index = 0; + } + //取上一张 + id dic = self.data[index]; + if (self.param.wEventScrollEnd) { + BOOL center = [self checkCellInCenterCollectionView:self.myCollectionV AtIndexPath:indexPath]; + UICollectionViewCell *currentCell = (UICollectionViewCell*)[self.myCollectionV cellForItemAtIndexPath:indexPath]; + self.param.wEventScrollEnd(dic, index, center,currentCell); + } + if (self.param.wEffect) { + if ([dic isKindOfClass:[NSDictionary class]]) { + [self setIconData:self.bgImgView withData:dic[self.param.wDataParamIconName]]; + }else{ + [self setIconData:self.bgImgView withData:dic]; + } + } + self.bannerControl.currentPage = index; + + if (self.param.wEventDidScroll) { + self.param.wEventDidScroll(self.myCollectionV.contentOffset); + } + self.lastIndex = current; +} +//淡入淡出 +- (void)fadeAction{ + if (self.param.wFadeOpen) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + WMZBannerFadeLayout *fade = (WMZBannerFadeLayout*)self.flowL; + if (![fade isKindOfClass:[WMZBannerFadeLayout class]]) { + return; + } + NSInteger current = MAX(self.param.myCurrentPath, 0); + NSInteger index = self.param.wRepeat?current%self.data.count:current; + self.bannerControl.currentPage = index; + NSInteger itemsCount = [self.myCollectionV numberOfItemsInSection:0]; + NSInteger showIndex = MIN(itemsCount-1, MAX(0, current)); + NSInteger hideIndex = fade.right?MAX(showIndex-1, 0):MIN(showIndex+1, itemsCount-1); + NSIndexPath *showIndexPath = [NSIndexPath indexPathForRow:showIndex inSection:0]; + NSIndexPath *hideIndexPath = [NSIndexPath indexPathForRow:hideIndex inSection:0]; + [self showAninationWithView:[self.myCollectionV cellForItemAtIndexPath:showIndexPath]]; + [self hideAninationWithView:[self.myCollectionV cellForItemAtIndexPath:hideIndexPath]]; + }); + } +} +//更新下划线位置 +- (void)setUpSpecialFrame{ + if (!self.param.wSpecialStyle) return; + if (!self.data.count) return; + + if (self.param.wSpecialStyle == SpecialStyleLine) { + [UIView animateWithDuration:0.5 animations:^{ + CGRect rect = self.line.frame; + rect.origin.x = (self.param.wRepeat?self.param.myCurrentPath%self.data.count:self.param.myCurrentPath)*rect.size.width; + self.line.frame = rect; + }]; + } +} + +- (void)showAninationWithView:(UIView*)view{ + [view.layer removeAllAnimations]; + CABasicAnimation *scale = [CABasicAnimation animation]; + scale.keyPath = @"transform.scale"; + scale.fromValue = [NSNumber numberWithFloat:1.3]; + scale.toValue = [NSNumber numberWithFloat:1.0]; + + CABasicAnimation *showViewAnn = [CABasicAnimation animationWithKeyPath:@"opacity"]; + showViewAnn.fromValue = [NSNumber numberWithFloat:0.5]; + showViewAnn.toValue = [NSNumber numberWithFloat:1]; + + CAAnimationGroup *group = [CAAnimationGroup animation]; + group.animations = @[scale, showViewAnn]; + group.duration = 0.6; + [view.layer addAnimation:group forKey:nil]; +} +- (void)hideAninationWithView:(UIView*)view{ + [view.layer removeAllAnimations]; + CABasicAnimation *scale = [CABasicAnimation animation]; + scale.keyPath = @"transform.scale"; + scale.fromValue = [NSNumber numberWithFloat:1]; + scale.toValue = [NSNumber numberWithFloat:1.3]; + + CABasicAnimation *showViewAnn = [CABasicAnimation animationWithKeyPath:@"opacity"]; + showViewAnn.fromValue = [NSNumber numberWithFloat:1]; + showViewAnn.toValue = [NSNumber numberWithFloat:0]; + + CAAnimationGroup *group = [CAAnimationGroup animation]; + group.animations = @[scale, showViewAnn]; + group.duration = 0.6; + [view.layer addAnimation:group forKey:nil]; +} + +- (UICollectionView *)myCollectionV{ + if (!_myCollectionV) { + _myCollectionV = [[UICollectionView alloc]initWithFrame:self.bounds collectionViewLayout:self.flowL]; + _myCollectionV.delegate = self; + _myCollectionV.dataSource = self; + _myCollectionV.showsVerticalScrollIndicator = NO; + _myCollectionV.showsHorizontalScrollIndicator = NO; + _myCollectionV.backgroundColor = [UIColor clearColor]; + _myCollectionV.decelerationRate = _param.wDecelerationRate; + } + return _myCollectionV; +} + +- (WMZBannerControl *)bannerControl{ + if (!_bannerControl) { + _bannerControl = [[WMZBannerControl alloc]initWithFrame:CGRectZero WithModel:_param]; + } + return _bannerControl; +} + +- (UIView *)line{ + if (!_line) { + _line = [UIView new]; + } + return _line; +} + +- (void)dealloc{ + //单纯调用这里无法消除定时器 + [self cancelTimer]; +} + +//要配合这里调用 +- (void)willMoveToSuperview:(UIView *)newSuperview { + [super willMoveToSuperview:newSuperview]; + if (!newSuperview &&self.timer) { + // 销毁定时器 + [self.timer invalidate]; + self.timer = nil; + } +} + +@end + +@implementation Collectioncell +-(instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self){ + self.icon = [UIImageView new]; + self.icon.layer.masksToBounds = YES; + [self.contentView addSubview:self.icon]; + self.icon.frame = self.contentView.bounds; + self.contentView.layer.masksToBounds = YES; + } + return self; +} + +- (void)setParam:(WMZBannerParam *)param{ + _param = param; + self.icon.contentMode = param.wImageFill?UIViewContentModeScaleAspectFill:UIViewContentModeScaleToFill; +} +@end + +@implementation CollectionTextCell +-(instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self){ + self.contentView.backgroundColor = [UIColor whiteColor]; + self.label = [UILabel new]; + self.label.font = [UIFont systemFontOfSize:17.0]; + self.label.textColor = [UIColor redColor]; + [self.contentView addSubview:self.label]; + self.label.frame = CGRectMake(10, 0, frame.size.width-20, frame.size.height); + } + return self; +} + +- (void)setParam:(WMZBannerParam *)param{ + _param = param; + self.label.textColor = self.param.wMarqueeTextColor; +} +@end diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..5d07fe5 --- /dev/null +++ b/Podfile @@ -0,0 +1,32 @@ +# Uncomment the next line to define a global platform for your project + platform :ios, '15.0' + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" + config.build_settings['EXCLUDED_ARCHITECTURES'] = 'i386' + end + end +end + +target 'BeeReel' do + + use_frameworks! + + pod 'SVProgressHUD' #HUD + pod 'Moya' #网络框架 + pod 'SmartCodable' #数据解析 + pod 'Kingfisher' #图片加载 + pod 'SnapKit' #布局 + pod 'Toast' #吐司提示 +# pod 'YYKit' #工具类 + pod 'SJVideoPlayer' #播放器 + pod 'WMZPageController' #分页控制器 + pod 'YYCategories' + pod 'YYText' + pod 'FSPagerView' #banner + + +end